diff --git a/apps/presentation/dashboard/package.json b/apps/presentation/dashboard/package.json
index 1b61e8a922..e6bd8d48fc 100644
--- a/apps/presentation/dashboard/package.json
+++ b/apps/presentation/dashboard/package.json
@@ -5,6 +5,7 @@
"license": "Apache-2.0",
"type": "module",
"scripts": {
+ "smoke:todo-ownership": "vite build --ssr smoke/todo-ownership-smoke.mjs --outDir node_modules/.cache/todo-ownership --emptyOutDir && node node_modules/.cache/todo-ownership/todo-ownership-smoke.js",
"smoke:workspace-progressive-loader": "node smoke/workspace-progressive-status-smoke.mjs",
"smoke:workspace-progressive": "node ../../../examples/workspace-progressive-loading-browser-smoke.mjs",
"build": "tsc --noEmit && vite build && npm run build:chat",
diff --git a/apps/presentation/dashboard/smoke/todo-ownership-smoke.mjs b/apps/presentation/dashboard/smoke/todo-ownership-smoke.mjs
new file mode 100644
index 0000000000..270b0e2a30
--- /dev/null
+++ b/apps/presentation/dashboard/smoke/todo-ownership-smoke.mjs
@@ -0,0 +1,64 @@
+import assert from "node:assert/strict";
+import { createElement } from "react";
+import { renderToStaticMarkup } from "react-dom/server";
+import { GoalTasksView } from "../src/features/personal-workspace/goal-tasks-view.tsx";
+import { CompletedTaskLane } from "../src/features/personal-workspace/completed-task-lane.tsx";
+import { todoItemSchema } from "../src/data/status.ts";
+import { workspaceAgentTodoFromItem } from "../src/features/personal-workspace/personal-workspace-model.ts";
+import { ContextDrawer } from "../src/features/personal-workspace/context-drawer.tsx";
+import { WorkspaceI18nProvider } from "../src/features/personal-workspace/i18n.tsx";
+
+// A Goal's default executor is not evidence that a Todo has been claimed.
+// Exercise the real consumers, including retained history and stale labels.
+const originalWindow = globalThis.window;
+try {
+ for (const [locale, unassigned, owner] of [["en", "Unassigned", "Owner"], ["zh-CN", "未分配", "Owner"]]) {
+ globalThis.window = { localStorage: { getItem: () => locale } };
+ const render = (component, props) => renderToStaticMarkup(createElement(
+ WorkspaceI18nProvider, null, createElement(component, props),
+ ));
+ const notesLabel = locale === "en" ? "Notes" : "备注";
+ const evidenceLabel = locale === "en" ? "Evidence" : "证据";
+ const longNote = "Preserve separate notes. ".repeat(800) + "NOTE_END_SENTINEL";
+ for (const fields of [{}, {note: null, evidence: null}, {note: "", evidence: ""},
+ {note: longNote}, {evidence: "RECORDED_EVIDENCE"}, {note: longNote, evidence: "RECORDED_EVIDENCE"}]) {
+ for (const done of [false, true]) {
+ const source = todoItemSchema.parse({todo_id: "metadata-todo", text: "Read source metadata", done,
+ status: done ? "done" : "open", ...fields});
+ const todo = workspaceAgentTodoFromItem(source, "fallback-id");
+ assert.equal(todo.note, fields.note ?? null, "Notes remain an independent source field");
+ assert.equal(todo.evidence, fields.evidence ?? null, "An absent evidence field cannot be inferred from notes");
+ const drawer = render(ContextDrawer, {agents: [], callbacks: {}, onClose() {}, selection: {
+ kind: "todo", item: {...todo, goalId: "test-goal", goalTitle: "Test Goal"},
+ }});
+ assert.equal(drawer.includes(`
{previewFailure.message}
: null} {selection.kind === "attention" ? ( <>