Skip to content

qa(checklist): a hook-refused script action shows exactly one error toast carrying its sentence — item, showcase fixture, and one run - #21609

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-21596-hook-refusal-toast-item
Oct 3, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-21596-hook-refusal-toast-item

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 3, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #21596

Clause-②: no

What this does

Gives objectui#9151's re-observation a home: one platform-checklist item, the smallest showcase fixture it needs, and one recorded run of it against a console built at this repo's .objectui-sha.

Run verdict: PASS (1/1 item, 4/4 clauses, reproduced on two finished tasks on fresh page loads). The console showed exactly one error toast carrying the hook's sentence, tied to a 400 from the action route, and the record was left unchanged.

The fixture (measured absent, so added)

examples/app-showcase had no hook that refuses a write. src/data/hooks/index.ts held four hooks: trim, stamp, and two log-only after-hooks, none of which throw. Added:

  • showcase_guard_task_reopen (src/data/hooks/index.ts, GuardTaskReopenHook): a beforeUpdate hook on showcase_task. Its condition is previous.done == true && record.done != true and its sandboxed body throws TASK_REOPEN_REFUSAL = "A finished task cannot be reopened. Create a follow-up task instead." with onError: 'abort'.
  • showcase_reopen_task (src/ui/actions/index.ts, ReopenTaskAction): a script action, perRecord, offered only on finished tasks. It appears in the task list's row menu only (list_item). The Task Detail page is kind: 'full' and draws no record_header bar, so declaring that location would have been inert. Its body writes { id, done: false } through ctx.api.
  • zh-CN label for the action (check:i18n-coverage ratchet).
  • test/task-reopen-hook-refusal.test.ts (5 tests). It uses the production harness: real ObjectQL, SqlDriver and QuickJSScriptRunner, with the app's real objects and hooks. The tests check four things. The reopening write is refused with the sentence and the row is unchanged. An edit of a finished task that leaves done alone still lands. With the hooks unbound, the same write lands (reverse check). The action body writes exactly the refused shape.

The checklist item

docs/qa/platform-checklist/areas/records-forms.json, id records-forms.script-action-hook-refusal-toast (revision 1, P2, browser). It sits in records-forms, beside the area's action-surface items (action-location-matrix, action-param-widgets, object-hook-lifecycle). It has four clauses:

  • network: the action route answers 4xx carrying the sentence.
  • dom: exactly one new toast, data-type=error, containing the sentence. It is counted inside sonner's 4s auto-dismiss window, excluding data-removed nodes.
  • network: the toast is tied to its request. That request is the only non-GET request in the window, and its message matches the toast.
  • api: the record is unchanged.

It is mapped under action and hook in coverage.json.

records-forms.object-hook-lifecycle was bumped from revision 2 to 3, roster only, with no clause moved. Two of its knownGaps became false once the fifth hook landed: "no two showcase hooks share an object+event" and "the fixtures never throw". Both now name the new hook and say why it does not close that item's ordering clause or its beforeInsert abort-rollback clause.

pnpm check:platform-checklist passes with exit 0: 15 areas, 270 items, coverage 39 kinds mapped and 2 waived, symbol anchors 650/660.

The run

Acceptance notes

  • Not scored against the item: the action route hands the user the sandbox debug wrapper. The toast reads "hook 'showcase_guard_task_reopen' threw: Error: " followed by the sentence. The same refusal through PATCH /api/v1/data/showcase_task/ID answers 400 with the bare sentence. The wrapper is what packages/qa/dogfood/test/hook-error-format.dogfood.test.ts says belongs in server logs, not in the toast. This is reported to the PM in the report and is not fixed here: no runtime change is in scope.
  • Changeset: none. The only package this touches is @objectstack/example-showcase, which is "private": true. Its earlier showcase-only fixes (393ae878d3, 99398542b3) also carried none. docs/qa/** does not publish. skip-changeset applies.

Verification (head ff3d71c7cf)

  • pnpm --filter @objectstack/example-showcase run typecheck passed (exit 0); --listFiles includes the new test.
  • pnpm --filter @objectstack/example-showcase exec vitest run --maxWorkers=2: 32 files and 399 tests passed.
  • node scripts/pm/dispatch-gates.mjs --commands derived 45 commands; all 45 exited 0. --ran reconciliation: 45 derived, 45 run, 0 unrun.

Generated by Claude Code

claude added 4 commits October 3, 2026 15:57
…e script action that reaches it

Claude-Session: https://claude.ai/code/session_01VDtqoecgES7ScQYGbFVDRv
Co-authored-by: Claude <noreply@anthropic.com>
…ror toast; hook-lifecycle item names the new fixture

Claude-Session: https://claude.ai/code/session_01VDtqoecgES7ScQYGbFVDRv
Co-authored-by: Claude <noreply@anthropic.com>
… only

The Task Detail page is kind 'full' and draws no record_header bar, so that
declared location was inert; the checklist item now drives the row menu.

Claude-Session: https://claude.ai/code/session_01VDtqoecgES7ScQYGbFVDRv
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Oct 3, 2026
@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 3, 2026 16:41
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 3, 2026 16:42
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 3, 2026
Merged via the queue into main with commit 54521f0 Oct 3, 2026
44 of 46 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-21596-hook-refusal-toast-item branch October 3, 2026 17:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate tests

Projects

None yet

1 participant