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
Save answer: a hook or record-triggered flow can attach one user-facing notice to a record save, shown with the save toast #28000
Filing gate: ③ triage-directed. The order on #27380 said: take what is ui-only, card the rest. The card thread measured each row on origin/main5f6dc96c (report 6517004938, out_of_scope_findings). Row 1 is taken in PR #27996.
Reach: any app whose save sets something in motion the user should hear about. HotCRM sends a deal for approval on save and shows a record:alert banner as the workaround (#27380 row 3).
Reader who acts: domain:spec first (the answer key), then domain:server (hook context and flow node), then domain:ui (the toast).
Duplicate search: search_issues "hook or flow attach user-facing notice message to record save answer toast" → #21596 and #14945, both closed and about refusals, not success notices.
Observed
A beforeUpdate / afterUpdate hook or a record-triggered flow cannot tell the user what the save set in motion. The single-record write answer declares warnings (advisory validation-rule hits) and nothing else a save can say.
Wanted
Spec: a key on the create/update answer for an author-written notice: one message, info severity, distinct from warnings (packages/specapi/protocol.zod.ts write answers).
Server: an API for a hook context and a flow node to set it on the running write.
Claim: PM loop round 3
Session: session_01L9qibx5r5nhDMwpRs4SL6D
Account: os-tesla
Branch: claude/project-thread-qo9o0z (the card thread's designated push branch, the recorded platform exception to claude/issue-<n>-<slug>)
Worktree: objectstack-issue-28000
Domain: domain:spec
Seat: domain:spec#2
File surface: packages/spec/src/api/protocol.zod.ts (create/update write answers), packages/spec/src/contracts/** (hook context / write options), and if the smallest sound shape needs it the packages/metadata-protocol and packages/objectql lines that carry it (the #27703 precedent), tests, regenerated artefacts and reference docs, changeset. The server half (engine, flow node) and the console toast are follow-up cards the seat files. ⛔ no ui/** and no governed path (stop on breach; explain in the report)
Container & model: S, mode:thread, model: TIER_DEFAULT, effort: high; Clause-②: yes ⇒ the contract review at CONTRACT_REVIEW_TIER is the seat's own, on the maintainer's 「你有 fable,可以自己契约复审」
Clause-②: yes (widening)
Metadata-protocol: added
Responsibility: n/a — not a defect card
Thread-read: none
Premise: holds (re-read at origin/main9f13ee81; the declaration named on the card still lacks the key)
Serial constraints cleared: seat 2 PR #28174 (#27703) edits the same two answer schemas and WriteObservabilityOptions; it is in the merge queue: merge origin/main after it lands, before opening the PR
Ruling-ref: none on the shape; dispatch authority = the maintainer's 「p3 也可以开发」 (2026-10-30T12:41Z) and 「每个座位并发可以5」 (2026-10-30T15:04Z); the card's filing gate ③ (triage-directed)
Dispatched 2026-10-30T17:08Z to a card thread of the seat's project; the order lives in the project. ⛔ The thread posts no claim and writes no assignee.
Reported 2026-10-30T18:55Z for #28000. Draft PR #28247 at head e7c47912.
{
"issue": 28000,
"status": "done",
"branch": "claude/project-thread-qo9o0z",
"pr": "https://github.com/objectstack-ai/objectstack/pull/28247",
"session": "session_01L9qibyvb1yBsrocHdqS4Ua",
"premise_still_valid": true,
"summary": "Spec half built. CreateDataResponseSchema and UpdateDataResponseSchema declare an optional response-only notice: one author-written message (non-empty string), info severity, distinct from warnings, last call wins, omitted when unset, never stored. The hook-context contract is a fifth WriteObservabilityOptions listener, onNotice(message), reached on ctx.input.options exactly like #27703's onMentionsWithheld; metadata-protocol createData/updateData pass it and answer the last non-empty message, objectql admits the option on update, client mirrors the key. Only a code handler can call it today; the sandboxed body face and a record-triggered flow are the server half.",
"tests": "head e7c47912: objectql test 500/500 files, 9738/9738 tests; metadata-protocol test 284 passed + 4 skipped files, 21860 passed + 26 skipped tests; client test 52/52, 682/682; spec test 760/760 files, 22573 passed + 1 todo; typecheck spec, metadata-protocol, objectql, client: lock VERDICT command-exit 0. New: spec write-answer-notice.test.ts 8/8, objectql protocol-save-notice-answer.test.ts 14/14 (real ObjectQL + real protocol, raw registerHook and declarative handler surfaces). Ablations via scripts/ablation-replace.mjs, metadata-protocol rebuilt each leg, ablation-dist-preflight marker present then absent, blob == HEAD after restore: A1 createData listener records nothing -> 6 failed/14, restore 14/14; A2 updateData listener records nothing -> 3 failed/14, restore 14/14; A3 blank guard removed -> 1 failed/14, restore 14/14. Docs os:check example mutated (number passed to onNotice) -> check:skill-examples exit 1 at that line, restored green.",
"gates": "dispatch-gates --commands at e7c47912: 109 families, each run with stdin from /dev/null, exit codes recorded before any pipe (record: /mnt/project-files/spec-seat-2/r2026-10-30/out/28000-gates-ran-e7c47912.list). --ran: 109 derived, 108 run, 1 NOT MEASURED, 0 unrun, 0 red. NOT MEASURED: check:dual-build-cjs-loads (exit 3, whole-repo build). Re-runs after prerequisite: check-plugin-teardown-shape --self-test (control 621a4876 fetched, 48 cases, exit 0), spec check:skill-examples (client-react dist built, 277 examples, exit 0). spec check:generated after the merged rebuild: check:docs stale, --fix regenerated references/api/protocol.mdx, re-check green. CI check-runs on #28247: in_progress at report time.",
"dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
"line_budget": "+497 / -9 across 14 files at e7c47912 vs origin/main merge base",
"files_changed": [
".changeset/28000-save-notice-answer.md",
"content/docs/api/data-api.mdx",
"content/docs/kernel/contracts/data-engine.mdx",
"content/docs/references/api/protocol.mdx",
"packages/client/src/index.ts",
"packages/metadata-protocol/src/protocol.ts",
"packages/objectql/src/engine-unknown-option.test.ts",
"packages/objectql/src/engine.ts",
"packages/objectql/src/protocol-save-notice-answer.test.ts",
"packages/spec/authorable-surface/api.json",
"packages/spec/src/api/protocol.zod.ts",
"packages/spec/src/api/write-answer-notice.test.ts",
"packages/spec/src/contracts/data-engine.ts",
"packages/spec/src/data/hook-api.ts"
],
"deviations": [
"File surface: besides spec and the metadata-protocol / objectql lines the order allows for the transport, the diff touches packages/client/src/index.ts (CreateDataResult / UpdateDataResult mirror, as #27703 did in its review round), spec data/hook-api.ts (one docblock word list) and two hand-written docs pages (content/docs/api/data-api.mdx, content/docs/kernel/contracts/data-engine.mdx). No governed path, no ui/**.",
"Worktree: the shared checkout held the designated branch, so it was detached and the branch checked out in a dedicated worktree (objectstack-issue-28000).",
"The merge commit 3b56bcd3 carries no trailer pair: amending it was refused by the os-regen pre-commit until the next commit discharged the regeneration.",
"test:repo (vitest repo project) not run locally; CI's."
],
"decision_for_maintainer": "Shape taken: a new answer key notice (one non-empty string, last call wins) set through a WriteObservabilityOptions listener. Not taken: (a) reusing warnings with severity info — warnings are validation findings with field/code/rule that the console renders as problems; (b) a list of messages — the card asks for one and the save shows one toast; (c) an object { message } — nothing else is known about a notice; (d) a HookContext member — needs engine binding at every dispatch site plus sandbox marshalling (the server half), and declaring it here would be declared-never-produced.",
"mcp_calls": "6 — mcp__github__issue_read x3 (get, get_comments x2), mcp__github__pull_request_read x3 (get #28174 x2, get #28247); 0 writes",
"api_writes": "2 — relay pr_create (objectstack-ai/objectstack#28247, read-back identical, 11858 bytes), this comment via post-stamped",
"open_questions": [],
"out_of_scope_findings": [
"carrier: the seat · noted, not filed: server half — a hook-context spelling a sandboxed body (and a handler the build lowers into one, as HotCRM's are) can call to set the notice (engine binds it from the write's onNotice at each dispatch site, body runner marshals it), plus a flow node that sets it on the running write; dedupe words: hook save notice, ctx notice, flow notice on save",
"carrier: the seat · noted, not filed: console half — the save toast shows notice with, or in place of, the record-named message (AppContent crud_success, RecordFormPage, inline edit save), following #27380 row 2's one-toast rule; dedupe words: save toast message from hook, notice toast",
"carrier: the seat · Skills-docs follow-up owed: skills/objectstack-data/rules/lifecycle.md teaches the save notice once the server half lands",
"carrier: the seat · noted, not filed: batch (createManyData / batchData), clone and flow-written saves do not answer notice; the card scopes single-record create and update"
]
}
Activity
objectstack-fleet commentedon Oct 30, 2026
domain:spec#2 · os-tesla · session_01L9qibx5r5nhDMwpRs4SL6D
Claim: PM loop round 3
Session:
session_01L9qibx5r5nhDMwpRs4SL6DAccount:
os-teslaBranch:
claude/project-thread-qo9o0z(the card thread's designated push branch, the recorded platform exception toclaude/issue-<n>-<slug>)Worktree:
objectstack-issue-28000Domain:
domain:specSeat:
domain:spec#2File surface:
packages/spec/src/api/protocol.zod.ts(create/update write answers),packages/spec/src/contracts/**(hook context / write options), and if the smallest sound shape needs it thepackages/metadata-protocolandpackages/objectqllines that carry it (the #27703 precedent), tests, regenerated artefacts and reference docs, changeset. The server half (engine, flow node) and the console toast are follow-up cards the seat files. ⛔ noui/**and no governed path (stop on breach; explain in the report)Container & model:
S,mode:thread,model: TIER_DEFAULT,effort: high;Clause-②: yes⇒ the contract review atCONTRACT_REVIEW_TIERis the seat's own, on the maintainer's 「你有 fable,可以自己契约复审」Clause-②: yes (widening)
Metadata-protocol: added
Responsibility: n/a — not a defect card
Thread-read: none
Premise: holds (re-read at
origin/main9f13ee81; the declaration named on the card still lacks the key)Serial constraints cleared: seat 2 PR #28174 (#27703) edits the same two answer schemas and
WriteObservabilityOptions; it is in the merge queue: mergeorigin/mainafter it lands, before opening the PRRuling-ref: none on the shape; dispatch authority = the maintainer's 「p3 也可以开发」 (2026-10-30T12:41Z) and 「每个座位并发可以5」 (2026-10-30T15:04Z); the card's filing gate ③ (triage-directed)
Dispatched 2026-10-30T17:08Z to a card thread of the seat's project; the order lives in the project. ⛔ The thread posts no claim and writes no assignee.
objectstack-fleet commentedon Oct 30, 2026
domain:spec#2 · os-tesla · session_01L9qibyvb1yBsrocHdqS4Ua
os-dev-report
Reported 2026-10-30T18:55Z for #28000. Draft PR #28247 at head
e7c47912.{ "issue": 28000, "status": "done", "branch": "claude/project-thread-qo9o0z", "pr": "https://github.com/objectstack-ai/objectstack/pull/28247", "session": "session_01L9qibyvb1yBsrocHdqS4Ua", "premise_still_valid": true, "summary": "Spec half built. CreateDataResponseSchema and UpdateDataResponseSchema declare an optional response-only notice: one author-written message (non-empty string), info severity, distinct from warnings, last call wins, omitted when unset, never stored. The hook-context contract is a fifth WriteObservabilityOptions listener, onNotice(message), reached on ctx.input.options exactly like #27703's onMentionsWithheld; metadata-protocol createData/updateData pass it and answer the last non-empty message, objectql admits the option on update, client mirrors the key. Only a code handler can call it today; the sandboxed body face and a record-triggered flow are the server half.", "tests": "head e7c47912: objectql test 500/500 files, 9738/9738 tests; metadata-protocol test 284 passed + 4 skipped files, 21860 passed + 26 skipped tests; client test 52/52, 682/682; spec test 760/760 files, 22573 passed + 1 todo; typecheck spec, metadata-protocol, objectql, client: lock VERDICT command-exit 0. New: spec write-answer-notice.test.ts 8/8, objectql protocol-save-notice-answer.test.ts 14/14 (real ObjectQL + real protocol, raw registerHook and declarative handler surfaces). Ablations via scripts/ablation-replace.mjs, metadata-protocol rebuilt each leg, ablation-dist-preflight marker present then absent, blob == HEAD after restore: A1 createData listener records nothing -> 6 failed/14, restore 14/14; A2 updateData listener records nothing -> 3 failed/14, restore 14/14; A3 blank guard removed -> 1 failed/14, restore 14/14. Docs os:check example mutated (number passed to onNotice) -> check:skill-examples exit 1 at that line, restored green.", "gates": "dispatch-gates --commands at e7c47912: 109 families, each run with stdin from /dev/null, exit codes recorded before any pipe (record: /mnt/project-files/spec-seat-2/r2026-10-30/out/28000-gates-ran-e7c47912.list). --ran: 109 derived, 108 run, 1 NOT MEASURED, 0 unrun, 0 red. NOT MEASURED: check:dual-build-cjs-loads (exit 3, whole-repo build). Re-runs after prerequisite: check-plugin-teardown-shape --self-test (control 621a4876 fetched, 48 cases, exit 0), spec check:skill-examples (client-react dist built, 277 examples, exit 0). spec check:generated after the merged rebuild: check:docs stale, --fix regenerated references/api/protocol.mdx, re-check green. CI check-runs on #28247: in_progress at report time.", "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm", "line_budget": "+497 / -9 across 14 files at e7c47912 vs origin/main merge base", "files_changed": [ ".changeset/28000-save-notice-answer.md", "content/docs/api/data-api.mdx", "content/docs/kernel/contracts/data-engine.mdx", "content/docs/references/api/protocol.mdx", "packages/client/src/index.ts", "packages/metadata-protocol/src/protocol.ts", "packages/objectql/src/engine-unknown-option.test.ts", "packages/objectql/src/engine.ts", "packages/objectql/src/protocol-save-notice-answer.test.ts", "packages/spec/authorable-surface/api.json", "packages/spec/src/api/protocol.zod.ts", "packages/spec/src/api/write-answer-notice.test.ts", "packages/spec/src/contracts/data-engine.ts", "packages/spec/src/data/hook-api.ts" ], "deviations": [ "File surface: besides spec and the metadata-protocol / objectql lines the order allows for the transport, the diff touches packages/client/src/index.ts (CreateDataResult / UpdateDataResult mirror, as #27703 did in its review round), spec data/hook-api.ts (one docblock word list) and two hand-written docs pages (content/docs/api/data-api.mdx, content/docs/kernel/contracts/data-engine.mdx). No governed path, no ui/**.", "Worktree: the shared checkout held the designated branch, so it was detached and the branch checked out in a dedicated worktree (objectstack-issue-28000).", "The merge commit 3b56bcd3 carries no trailer pair: amending it was refused by the os-regen pre-commit until the next commit discharged the regeneration.", "test:repo (vitest repo project) not run locally; CI's." ], "decision_for_maintainer": "Shape taken: a new answer key notice (one non-empty string, last call wins) set through a WriteObservabilityOptions listener. Not taken: (a) reusing warnings with severity info — warnings are validation findings with field/code/rule that the console renders as problems; (b) a list of messages — the card asks for one and the save shows one toast; (c) an object { message } — nothing else is known about a notice; (d) a HookContext member — needs engine binding at every dispatch site plus sandbox marshalling (the server half), and declaring it here would be declared-never-produced.", "mcp_calls": "6 — mcp__github__issue_read x3 (get, get_comments x2), mcp__github__pull_request_read x3 (get #28174 x2, get #28247); 0 writes", "api_writes": "2 — relay pr_create (objectstack-ai/objectstack#28247, read-back identical, 11858 bytes), this comment via post-stamped", "open_questions": [], "out_of_scope_findings": [ "carrier: the seat · noted, not filed: server half — a hook-context spelling a sandboxed body (and a handler the build lowers into one, as HotCRM's are) can call to set the notice (engine binds it from the write's onNotice at each dispatch site, body runner marshals it), plus a flow node that sets it on the running write; dedupe words: hook save notice, ctx notice, flow notice on save", "carrier: the seat · noted, not filed: console half — the save toast shows notice with, or in place of, the record-named message (AppContent crud_success, RecordFormPage, inline edit save), following #27380 row 2's one-toast rule; dedupe words: save toast message from hook, notice toast", "carrier: the seat · Skills-docs follow-up owed: skills/objectstack-data/rules/lifecycle.md teaches the save notice once the server half lands", "carrier: the seat · noted, not filed: batch (createManyData / batchData), clone and flow-written saves do not answer notice; the card scopes single-record create and update" ] }