You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Restore toast Undo on one-click completion now that row actions can declare a field write (objectstack#14092 closed) #146
Found by the 18.1.0 upgrade (PR #144, head fd0e541, report #139 comment 6412788813); not changed there because it changes behaviour.
Background
#4 / #41 shipped one-click completion with undo. The complete row action is a script action, and undoable on it was never fulfilled by any runtime; spec 18 removes the key, so #144 drops it. The one-click reversal stays the duly_task_undo row action (pinned by test/task-actions.test.ts), but the toast Undo the deck shows is gone.
objectstack#14092 (no declarative field write for a ROW action) is closed completed since 2026-09-03. If complete becomes an operation: 'update' row action, the platform's toast Undo may apply to it.
What done looks like
Measure on 18.1.0: does a declarative row update action get a toast Undo, and does it still run the task hook that stamps completed_at / completed_late? Quote the platform source or docs.
If yes: move complete (and skip if it fits) to the declarative form, keep the stamping and permission gates identical, keep duly_task_undo working, and test both.
The headers of src/actions/task.handlers.ts and src/actions/task.actions.ts say no row-level declarative field write exists; correct them either way.
Unblocked at 2026-10-27T08:15Z: the restart condition holds. #144 (Upgrade to @objectstack 18.2.0) merged as c20d397, so main now runs 18.2.0. Moved from pm:blocked to pm:ready. Re-measure on 18.2.0 before you build, since this card was written against 18.1.0.
Claim: PM loop round 3
Session: session_011HyoSXGRy6L13mYq1Hp4n1
Account: os-warren (the seat's linked user; the card's assignee)
Branch: claude/project-thread-fica3z (platform-assigned card-thread branch, at origin/main25743ed)
Worktree: duly-issue-146
Domain: n/a. objectstack-ai/duly is a single-lane app repo with no domain:*.
Seat: repo:duly#1
File surface: src/actions/task.actions.ts, src/actions/task.handlers.ts, test/task-actions.test.ts; test/security.test.ts, test/demo-admin-capabilities.test.ts and src/translations/en.ts only if the measurement requires them. src/translations/zh-CN.ts only after telling the seat (#128 holds it). Stop on any breach until the seat posts a Claim amendment.
Container & model: S-M, mode:thread, model: TIER_DEFAULT (hand-derived), effort: high
Clause-②: no
Metadata-protocol: none
Responsibility: to be measured. App work if 18.2 gives a declarative row update action a toast Undo; otherwise a fork report, and the upstream half is filed.
Thread-read: 6436953483
Premise: to be measured first: objectstack#14092 is closed completed (2026-09-03), and whether 18.2 renders a toast Undo for it is the measurement.
Serial constraints cleared: #128 is in flight on src/objects/task.object.ts, src/translations/zh-CN.ts and src/actions/catalog.actions.ts, and #160 on src/flows/assignment.flow.ts and src/email-templates/assignment.email-template.ts. Neither intersects this surface.
Claim correction (2026-11-02T02:50Z): the Branch line should read "at origin/mainf26fc5c", not 25743ed. The seat's claim template carried a stale base. The card thread works from f26fc5c, which includes #158 and #159.
{
"issue": 146,
"status": "done",
"branch": "claude/project-thread-fica3z",
"pr": "https://github.com/objectstack-ai/duly/pull/164",
"session": "session_01JQwxbWM2DtyF9EXG41gsbt",
"premise_still_valid": true,
"summary": "Measured on main f26fc5c (@objectstack 18.2.0): all three conditions hold. (1) spec 18.2 admits undoable on operation:'update'; console 18.2 ActionRunner.executeUpdateOperation posts to the action route, builds the undo from the row and handlePostExecution puts Undo on the success toast; the runtime also returns an undo envelope. (2) executeDeclarativeUpdateAction writes via callData('update') under the caller's own context, so task.hook.ts stamps completed_at/completed_late (measured, true on a past-due task). (3) actionPermissionError runs ahead of the declarative branch on the REST /actions door and the MCP door (measured 403 PERMISSION_DENIED). duly_task_complete and duly_task_skip are now operation:'update' + patch + undoable:true; their handlers are removed; duly_task_undo keeps its handler. Stale headers of task.actions.ts and task.handlers.ts rewritten. Behaviour change named and pinned: completing a cancelled task through the API now answers 200 (the handler's 409 is gone; the data plane already accepted the same PATCH); declared rule is #162. Surface amendment by seat ruling: test/catalog-instantiate.test.ts, src/translations/authored-text.ts. zh-CN.ts untouched.",
"tests": "head c0551f9: pnpm test exit 0, Test Files 38 passed (38), Tests 938 passed (938); test/task-actions.test.ts 44/44 (complete/skip driven through HttpDispatcher.handleActions, toast-undo envelope applied as the console does, undo row action, capability 403 for all three, 404/400 subject refusals, #162 pin). Ablation on committed HEAD: undoable true -> false on complete (marker grep 1), task-actions 3 failed / 41 passed (undoable declaration, route undo envelope, applying it); restored with git checkout HEAD --, git diff HEAD empty, blob hash = HEAD, marker 0. Browser on pnpm demo (en-US, :3000) and pnpm demo:zh (:3300), demo admin, My week first row Complete: toast 'Done. Undo' / '已完成。 撤销'; Undo sends PATCH /api/v1/data/duly_task/ID {status:open}; toast 'Change undone' / '已撤销更改'; row back to Open/待办 with Complete; REST read status open, completed_at null, completed_late null. Screenshots in project files duly-146-declarative/.",
"gates": "local, head c0551f9, exit codes captured before any pipe: pnpm validate 0 (9 pre-existing warnings, none on actions), pnpm typecheck 0, pnpm test 0, pnpm build 0. duly has no scripts/pm/dispatch-gates.mjs; gate list hand-derived from package.json (verify = validate+typecheck+test) plus build per AGENTS.md. CI: .github/workflows/ci.yml job verify on the PR, in_progress at report time.",
"dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
"mcp_calls": "7 — issue_read x3 (get, get_comments x2), create_pull_request, issue_write (assignees os-warren), update_pull_request (reviewers; refused: PR author), pull_request_read x1 (body read-back). Write tools are in the list: this session's harness mandates the GitHub MCP for all GitHub writes and the order asked for os-warren as assignee; named here rather than hidden.",
"api_writes": "1 — this os-dev-report comment via scripts/pm/post-stamped.mjs (POST issues/146/comments); git push x1 (branch claude/project-thread-fica3z)",
"open_questions": [],
"out_of_scope_findings": [
"class: b · reach: POST /api/v1/actions/duly_task/duly_task_complete/ID on a cancelled task answers 200 and stores done (measured in test and route) · the declared transition rule belongs on the object · carrier: #162 (filed by the seat) · noted, not filed",
"carrier: none · duly_task_undo could also be declarative (patch status in_progress); left as a handler because the card moved only the two actions missing a toast Undo · noted in task.handlers.ts header and PR Acceptance notes"
]
}
Claim amendment (2026-11-02T03:20Z; the ruling was given to the card thread at 2026-11-02T03:00Z, and this record was held by the fleet write throttle). The file surface adds two files, both direct consequences of moving complete / skip to declarative update actions:
test/catalog-instantiate.test.ts: the two "handler wiring" loops skip action.operation === 'update', with a comment pointing to test/task-actions.test.ts, which pins that the declarative pair carry no handler.
src/translations/authored-text.ts: 'action.operation' becomes a machine(...) verdict, and 'action.patch' goes into OPAQUE, mirroring the view.bulkActionDefs[] rows. No translated string moves.
Serial note: #160 also claims src/translations/authored-text.ts, for one flow.nodes[].config.assignments entry. The two entries sit in different parts of the table. Whichever of #146 and #160 lands second merges main and re-runs test/i18n-coverage.test.ts before the seat lands it.
Activity
objectstack-fleet commentedon Oct 27, 2026
Unblocked at 2026-10-27T08:15Z: the restart condition holds. #144 (Upgrade to @objectstack 18.2.0) merged as
c20d397, somainnow runs 18.2.0. Moved frompm:blockedtopm:ready. Re-measure on 18.2.0 before you build, since this card was written against 18.1.0.Generated by Claude Code
objectstack-fleet commentedon Nov 2, 2026
Claim: PM loop round 3
Session:
session_011HyoSXGRy6L13mYq1Hp4n1Account:
os-warren(the seat's linked user; the card's assignee)Branch:
claude/project-thread-fica3z(platform-assigned card-thread branch, atorigin/main25743ed)Worktree:
duly-issue-146Domain: n/a. objectstack-ai/duly is a single-lane app repo with no
domain:*.Seat:
repo:duly#1File surface:
src/actions/task.actions.ts,src/actions/task.handlers.ts,test/task-actions.test.ts;test/security.test.ts,test/demo-admin-capabilities.test.tsandsrc/translations/en.tsonly if the measurement requires them.src/translations/zh-CN.tsonly after telling the seat (#128 holds it). Stop on any breach until the seat posts a Claim amendment.Container & model:
S-M,mode:thread,model: TIER_DEFAULT(hand-derived),effort: highClause-②: no
Metadata-protocol: none
Responsibility: to be measured. App work if 18.2 gives a declarative row update action a toast Undo; otherwise a fork report, and the upstream half is filed.
Thread-read: 6436953483
Premise: to be measured first: objectstack#14092 is closed
completed(2026-09-03), and whether 18.2 renders a toast Undo for it is the measurement.Serial constraints cleared: #128 is in flight on
src/objects/task.object.ts,src/translations/zh-CN.tsandsrc/actions/catalog.actions.ts, and #160 onsrc/flows/assignment.flow.tsandsrc/email-templates/assignment.email-template.ts. Neither intersects this surface.Generated by Claude Code
objectstack-fleet commentedon Nov 2, 2026
Claim correction (2026-11-02T02:50Z): the Branch line should read "at
origin/mainf26fc5c", not25743ed. The seat's claim template carried a stale base. The card thread works fromf26fc5c, which includes #158 and #159.Generated by Claude Code
objectstack-fleet commentedon Nov 2, 2026
repo:duly#1 · os-warren · session_01JQwxbWM2DtyF9EXG41gsbt
os-dev-report
{ "issue": 146, "status": "done", "branch": "claude/project-thread-fica3z", "pr": "https://github.com/objectstack-ai/duly/pull/164", "session": "session_01JQwxbWM2DtyF9EXG41gsbt", "premise_still_valid": true, "summary": "Measured on main f26fc5c (@objectstack 18.2.0): all three conditions hold. (1) spec 18.2 admits undoable on operation:'update'; console 18.2 ActionRunner.executeUpdateOperation posts to the action route, builds the undo from the row and handlePostExecution puts Undo on the success toast; the runtime also returns an undo envelope. (2) executeDeclarativeUpdateAction writes via callData('update') under the caller's own context, so task.hook.ts stamps completed_at/completed_late (measured, true on a past-due task). (3) actionPermissionError runs ahead of the declarative branch on the REST /actions door and the MCP door (measured 403 PERMISSION_DENIED). duly_task_complete and duly_task_skip are now operation:'update' + patch + undoable:true; their handlers are removed; duly_task_undo keeps its handler. Stale headers of task.actions.ts and task.handlers.ts rewritten. Behaviour change named and pinned: completing a cancelled task through the API now answers 200 (the handler's 409 is gone; the data plane already accepted the same PATCH); declared rule is #162. Surface amendment by seat ruling: test/catalog-instantiate.test.ts, src/translations/authored-text.ts. zh-CN.ts untouched.", "tests": "head c0551f9: pnpm test exit 0, Test Files 38 passed (38), Tests 938 passed (938); test/task-actions.test.ts 44/44 (complete/skip driven through HttpDispatcher.handleActions, toast-undo envelope applied as the console does, undo row action, capability 403 for all three, 404/400 subject refusals, #162 pin). Ablation on committed HEAD: undoable true -> false on complete (marker grep 1), task-actions 3 failed / 41 passed (undoable declaration, route undo envelope, applying it); restored with git checkout HEAD --, git diff HEAD empty, blob hash = HEAD, marker 0. Browser on pnpm demo (en-US, :3000) and pnpm demo:zh (:3300), demo admin, My week first row Complete: toast 'Done. Undo' / '已完成。 撤销'; Undo sends PATCH /api/v1/data/duly_task/ID {status:open}; toast 'Change undone' / '已撤销更改'; row back to Open/待办 with Complete; REST read status open, completed_at null, completed_late null. Screenshots in project files duly-146-declarative/.", "gates": "local, head c0551f9, exit codes captured before any pipe: pnpm validate 0 (9 pre-existing warnings, none on actions), pnpm typecheck 0, pnpm test 0, pnpm build 0. duly has no scripts/pm/dispatch-gates.mjs; gate list hand-derived from package.json (verify = validate+typecheck+test) plus build per AGENTS.md. CI: .github/workflows/ci.yml job verify on the PR, in_progress at report time.", "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm", "mcp_calls": "7 — issue_read x3 (get, get_comments x2), create_pull_request, issue_write (assignees os-warren), update_pull_request (reviewers; refused: PR author), pull_request_read x1 (body read-back). Write tools are in the list: this session's harness mandates the GitHub MCP for all GitHub writes and the order asked for os-warren as assignee; named here rather than hidden.", "api_writes": "1 — this os-dev-report comment via scripts/pm/post-stamped.mjs (POST issues/146/comments); git push x1 (branch claude/project-thread-fica3z)", "open_questions": [], "out_of_scope_findings": [ "class: b · reach: POST /api/v1/actions/duly_task/duly_task_complete/ID on a cancelled task answers 200 and stores done (measured in test and route) · the declared transition rule belongs on the object · carrier: #162 (filed by the seat) · noted, not filed", "carrier: none · duly_task_undo could also be declarative (patch status in_progress); left as a handler because the card moved only the two actions missing a toast Undo · noted in task.handlers.ts header and PR Acceptance notes" ] }Generated by Claude Code
objectstack-fleet commentedon Nov 2, 2026
Claim amendment (2026-11-02T03:20Z; the ruling was given to the card thread at 2026-11-02T03:00Z, and this record was held by the fleet write throttle). The file surface adds two files, both direct consequences of moving
complete/skipto declarative update actions:test/catalog-instantiate.test.ts: the two "handler wiring" loops skipaction.operation === 'update', with a comment pointing totest/task-actions.test.ts, which pins that the declarative pair carry no handler.src/translations/authored-text.ts:'action.operation'becomes amachine(...)verdict, and'action.patch'goes into OPAQUE, mirroring theview.bulkActionDefs[]rows. No translated string moves.Serial note: #160 also claims
src/translations/authored-text.ts, for oneflow.nodes[].config.assignmentsentry. The two entries sit in different parts of the table. Whichever of #146 and #160 lands second mergesmainand re-runstest/i18n-coverage.test.tsbefore the seat lands it.Generated by Claude Code