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
skills: teach the save notice in the objectstack-data lifecycle rule #28253
Filing gate: ③ the Skills-docs: follow-up owed line of PR #28247 (#28000), filed by the seat before ACCEPT as the claim template requires. ⛔ Not a claim. Triage sets the lane and grade.
Reach: an AI author writing a save hook from the objectstack-data skill does not learn that a hook can tell the user what the save started, so it keeps reaching for a record:alert banner.
Wanted
skills/objectstack-data/rules/lifecycle.md teaches the save notice: one message, info severity, last call wins, shown with the save toast; distinct from validation warnings.
Claim: PM loop round 1
Session: session_017ihL2g7RyXa8ErM6GAkHyc
Account: marchtian
Branch: claude/issue-28253-save-notice-skill
Worktree: objectstack-issue-28253
Domain: domain:skills
Seat: domain:skills#2
File surface: skills/objectstack-data/rules/lifecycle.md, plus the changeset the published skill package owes.
Container & model: M, mode:subagent, model: claude-fable-5-1 (dispatch-gates --tier: MANDATORY on skills/**, clause ①, no one-line exit), effort: high; then an independent CONTRACT_REVIEW_TIER review by a separate agent.
Clause-②: no
Metadata-protocol: none
Responsibility: n/a — not a defect card (a Skills-docs: follow-up owed line)
Thread-read: 6563078644
Premise: holds — Blocked-by: #28251 is closed and PR #28753 merged, so a sandboxed hook or flow can set the save notice; on mainrules/lifecycle.md does not teach it.
Serial constraints cleared: no open PR touches the file surface above (read over every open PR).
Landing: Tier H (skills/**). The PR stays draft with needs-user-decision until an authorised approver acts.
{
"issue": 28253,
"status": "done",
"branch": "claude/issue-28253-save-notice-skill",
"pr": "https://github.com/objectstack-ai/objectstack/pull/28813",
"head": "63afc09f2dc6c50cca54ca4fefd99367161d27d9",
"session": "session_017ihL2g7RyXa8ErM6GAkHyc",
"premise_still_valid": true,
"summary": "skills/objectstack-data/rules/lifecycle.md gains a Save Notice section taken from the live code: ctx.notice(message) on a create/update hook, inline handler and sandboxed body alike with no capability; the answer carries it as notice and the console shows it on the save toast; info severity, distinct from validation warnings and from a record:alert banner; one message per write, the last call wins, never stored; insert/update dispatches only, a write nobody waits on sets nothing, a non-string refuses the write, a refused write answers its error; and the flow face (save_notice node, refused outside a create/update record trigger and after an approval or wait). Paid inside the file's token ceiling by deleting restating prose in the same file. Changeset: @objectstack/skills patch. PR is draft, no reviewers, no assignees.",
"files_changed": [
"skills/objectstack-data/rules/lifecycle.md (156 to 161 lines; 6360 to 6347 bytes; 1590 to 1587 tokens, ceiling 1590, headroom 0 to 3)",
".changeset/28253-skill-save-notice.md (new, @objectstack/skills patch, Clause-②: no, Metadata-protocol: none)"
],
"token_budget": {
"file": "rules/lifecycle.md 1590 to 1587 tokens against ceiling 1590 (headroom 3); no ceiling moved",
"package_skill_md_lines": "sum of the 11 skills/*/SKILL.md: 4561 to 4561 (no SKILL.md touched)",
"bundle_total_tokens": "222242 to 222239 (check-skills-token-ratchet price tag)",
"paid_with": "deleted restating prose in the same file: the Decision Tree section (items 1-5 restated the Lifecycle Classes table and the four Syntax examples; item 6 restated the MIXED example comment and the fifth Validation rule), the telemetry-growth anecdote in Why This Exists, and the sentence 'Nothing is deleted.' in the first Safety Semantics bullet (it restated 'rows are never deleted')"
},
"tests": "Docs-only diff in a published skill package; no test file owed. node scripts/check-skills-token-ratchet.mjs at 63afc09f2d: exit 0, 'skills/objectstack-data/rules/lifecycle.md is 1587 tokens (ceiling 1590; headroom 3)', '57 authored bundle file(s) within their ceilings'.",
"gates": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths; change set from git: 2 files, +34/-18) at head 63afc09f2d derived 43 families. First sweep without dist: 38 exit 0, 5 exit 3 PREREQUISITE NOT MET (pnpm --filter @objectstack/lint run check:doc-formula-expressions, check:dts-closure, check:dual-build-cjs-loads, check:lean-entry-closure, check:sourcemap-no-sources-content). pnpm build --concurrency=2 under scripts/pm/os-verify-lock.sh: VERDICT command-exit 0 (116/117 tasks replayed from the shared turbo cache; waited 196s behind another dev's build). The five re-run on the same head: all exit 0. --ran over the merged record: '43 derived famil(ies) accounted for — 43 run, 0 NOT-MEASURED, 0 UNRUN', exit 0. Every exit code captured before any pipe (cmd to a log file; status=$?). Skills families among them, all exit 0: check-skills-token-ratchet (+ --self-test), check:skill-frame-sync, check:skill-identifier-liveness, check:skill-compatibility, check:doc-authoring, check:pm-skill-id-lint, check:role-word, check:corpus-claim-drift, check:published-files, check-empty-changeset, check-changeset-no-major, check-adr-0087-registration. Local-only families not derived and not run: check:dev-prereqs, check:skill-frame-freshness (no frame file, no packages/spec source touched). CI families are the PR's check-runs (in_progress at report time), not re-run locally.",
"dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
"mcp_calls": "0 — no mcp__github__* tool was called (reads went through gh api REST: issue 28253 body and comments, pulls 28795 files, pulls 28753 and its files, pulls 28813 read-back)",
"api_writes": "2 REST writes plus 1 git push — (1) git push -u origin claude/issue-28253-save-notice-skill (not REST); (2) pr_create via scripts/pm/fleet-write/dispatch.mjs: POST /repos/objectstack-ai/objectstack/pulls, run 41351802290 success, read-back 7830 bytes sent = 7830 stored, PR #28813; (3) this comment via scripts/pm/post-stamped.mjs --comment=28253: POST /repos/objectstack-ai/objectstack/issues/28253/comments",
"deviations": [
"No label sent with pr_create: the dispatch named none and skip-changeset does not apply (a changeset is included). The labels the PR now carries (documentation, size/s, tooling) were set by another actor (a labeler) after the create, not by this dev; left as found. The claim's needs-user-decision on the PR is the seat's act at hand-off.",
"skills/objectstack-data/SKILL.md not touched (PR #28795 open on it): its rule index line 45 describes rules/lifecycle.md as retention/TTL/rotation/archive only and does not name the save notice; if the index must name the topic, it is a follow-up on that file after #28795 lands.",
"Worktree /home/claude/objectstack-issue-28253: node_modules and the worktree removed after this report, per the os-dev cleanup clause; the branch is on origin at 63afc09f2d."
],
"facts": [
"ctx.notice is bound by the engine on every insert/update dispatch before any handler runs: packages/objectql/src/engine.ts:5996-5998; packages/objectql/src/hook-notice.ts:29 (NOTICE_EVENTS beforeInsert/afterInsert/beforeUpdate/afterUpdate), :61-67 (bound on those, deleted on every other event)",
"Anything but a non-empty string is refused with a TypeError naming the call, so the write fails: packages/objectql/src/hook-notice.ts:42-47; pinned on a sandboxed body at packages/runtime/src/sandbox/hook-notice.integration.test.ts:118-120",
"A write that passed no onNotice listener (batch, seed, import, a flow node's own write) sets nothing: packages/objectql/src/hook-notice.ts:16-18, :48-52; packages/spec/src/data/hook.zod.ts:1622-1626",
"Sandboxed body spelling is the same ctx.notice(message), marshalled as a host function with no capability token: packages/spec/src/data/hook.zod.ts:1612-1617; packages/runtime/src/sandbox/body-runner.ts:1302-1306; packages/runtime/src/sandbox/quickjs-runner.ts:945-966; integration pin packages/runtime/src/sandbox/hook-notice.integration.test.ts:47-55, :105-113",
"Inline handler reaches the same member: packages/spec/src/data/hook.zod.ts:1612-1613 (an in-process handler calls it like the cast ctx.input.options listener)",
"One message per write, the last call before the answer wins; a refused write answers its error, never a notice; a nested write carries its own options; nothing is stored: packages/spec/src/contracts/data-engine.ts:156-163; packages/spec/src/api/protocol.zod.ts:2760-2764",
"The create/update answer carries it as notice; info severity, reports an outcome never a problem, distinct from warnings (advisory validation-rule hits); a client shows it with or in place of its own success message: packages/spec/src/api/protocol.zod.ts:2755-2764 (WRITE_ANSWER_NOTICE_DESCRIPTION), :2785 (CreateDataResponseSchema.notice), :2996 (UpdateDataResponseSchema.notice); onNotice is info severity and never rides onValidationAdvisory: packages/spec/src/contracts/data-engine.ts:128-134",
"The console shows it on the one save toast: ui/packages/react/src/record-save-feedback.ts:184-195 (toastRecordSave: notice leads the lines; a notice alone keeps the success variant; any warning puts it on the warning variant), :159 (the parked answer's notice, last one wins); ui/packages/app-shell/src/providers/writeWarningToast.ts:255-262, :290-301 (unclaimed: an info toast of its own)",
"Flow face: the save_notice node renders config.message ({{ }}) and sets it on the triggering write's channel: packages/services/service-automation/src/builtin/save-notice-node.ts:37-61 (descriptor, message required), :62-107 (execute); the record-change trigger hands the run the write's channel: packages/triggers/trigger-record-change/src/record-change-trigger.ts:606-611, :175-197 (saveNoticeChannelOf wraps ctx.notice and the writer's locale)",
"Placement refused at the flow parse outside a create/update record trigger, regex record-(before|after)-(create|insert|update|write), at any region depth: packages/spec/src/automation/save-notice-placement.ts:31, :52-80",
"Refused at run time after a suspension or in a child run: the channel is closed once the run returns (packages/triggers/trigger-record-change/src/record-change-trigger.ts:183-190, :366-369); a run holding no channel refuses the node by name (packages/services/service-automation/src/builtin/save-notice-node.ts:65-73); subflow and map children get no channel (packages/services/service-automation/src/builtin/subflow-node.ts:121-123; map-node.ts:244-246)",
"record:alert is a record-page banner block (page metadata), dismissible per viewer and shown by a visible predicate: packages/spec/src/ui/page.zod.ts:238, :1436; content/docs/protocol/objectui/record-alert.mdx:8-35",
"Shipping package: skills/** is synced into packages/skills dist (packages/skills/package.json:17-21 files, :28 build = sync-catalog), so the changeset is on @objectstack/skills; docs-only addition = patch (AGENTS.md Post-Task Checklist step 3: a none kind takes patch)",
"On origin/main c064d3a246, rules/lifecycle.md carried no mention of notice, ctx.notice or save_notice (grep over skills/objectstack-data: zero hits outside validation.md's severity enum), and its ceiling row is scripts/check-skills-token-ratchet.mjs:702 (1590) with headroom 0 before this PR"
],
"open_questions": [
{
"question": "Should skills/objectstack-data/SKILL.md's rule index (line 45, the rules/lifecycle.md entry) name the save notice, and should references/data-hooks.md's sandbox ctx table gain a ctx.notice row?",
"options": ["A: a follow-up on SKILL.md after PR #28795 lands, one clause in the existing entry (headroom 22 tokens today)", "B: leave the index as is; the lifecycle page's own heading suffices"],
"recommendation": "A for SKILL.md (a few tokens); data-hooks.md has 2 tokens of headroom, so a ctx.notice row there must be paid by deleting a sentence in that file, a separate small edit the seat can carry or drop."
}
],
"out_of_scope_findings": [
"carrier: none · noted, not filed — skills/objectstack-data/references/data-hooks.md:352-368 (sandbox ctx.* table) and :573-610 (HookContext interface listing) carry no ctx.notice row; a doc gap, not a defect class (no refused or dropped metadata); headroom 2 tokens, so any row there is paid by deletion in that file. Dedupe words: data-hooks ctx.notice sandbox table save notice"
]
}
Delta to report 6564123997: patch round for the three should-fixes of contract review 6564252529 on PR #28813.
{
"issue": 28253,
"status": "done",
"branch": "claude/issue-28253-save-notice-skill",
"pr": "https://github.com/objectstack-ai/objectstack/pull/28813",
"head": "3fc28cac1f20a6ff2b5f5e8ffad392a3ee83f8ce",
"previous_head": "63afc09f2dc6c50cca54ca4fefd99367161d27d9",
"session": "session_017ihL2g7RyXa8ErM6GAkHyc",
"premise_still_valid": true,
"summary": "One commit on skills/objectstack-data/rules/lifecycle.md (9 insertions, 9 deletions): (1) the Save Notice paragraph now reads 'a sandboxed body or the deprecated inline handler, no capability', leading with body and naming handler as the deprecated door (references/data-hooks.md:317-325); (2) the record:alert contrast now reads 'shown whenever the record's state says so' instead of 'on every visit', since the banner is dismissible per viewer (page.zod.ts:1436, record-alert.mdx:33-34); (3) the intro now reads 'Distinct from lifecycle hooks (…), which run logic on writes: this page covers the rows' retention, rotation and archival — and, at its end, the save notice a hook hands the saver', so it no longer contradicts itself. Paid inside the ceiling by shortening the section heading to 'Save Notice' (its tail restated the first sentence) and one intro clause.",
"files_changed": [
"skills/objectstack-data/rules/lifecycle.md (161 lines unchanged; 6347 to 6358 bytes; 1587 to 1590 tokens, ceiling 1590, headroom 0)"
],
"token_budget": "rules/lifecycle.md 1590/1590 (headroom 0; no ceiling moved); sum of 11 skills/*/SKILL.md lines 4561 (unchanged); bundle total 222242 tokens (equal to main's 222242; the first round's -3 is spent)",
"tests": "node scripts/check-skills-token-ratchet.mjs at 3fc28cac1f: exit 0, 'skills/objectstack-data/rules/lifecycle.md is 1590 tokens (ceiling 1590; headroom 0)', '57 authored bundle file(s) within their ceilings'.",
"gates": "Worktree recreated from origin (git worktree add on the pushed branch, tracking, no new branch, no empty push); pnpm install --frozen-lockfile; pnpm build --concurrency=2 under scripts/pm/os-verify-lock.sh: VERDICT command-exit 0 (waited 0s, cache replay). node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths) at head 3fc28cac1f derived 43 families; every command recorded with its exit code captured before any pipe: 43 exit 0, none exit 3. --ran: '43 derived famil(ies) accounted for — 43 run, 0 NOT-MEASURED, 0 UNRUN', exit 0. Skills families among them all exit 0 (check-skills-token-ratchet + --self-test, skill-frame-sync, skill-identifier-liveness, skill-compatibility, doc-authoring, pm-skill-id-lint, role-word, corpus-claim-drift, published-files, the changeset gates). CI families are the PR's check-runs on 3fc28cac1f (in_progress at report time).",
"dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
"mcp_calls": "0 — no mcp__github__* tool called; reads via gh api REST: review comment 6564252529 (read in full)",
"api_writes": "1 REST write plus 1 git push — git push origin claude/issue-28253-save-notice-skill (63afc09f2d to 3fc28cac1f, not REST); this comment via scripts/pm/post-stamped.mjs --comment=28253 (POST /repos/objectstack-ai/objectstack/issues/28253/comments). PR body not patched (dev writes it once); the review's three items are answered by this delta.",
"deviations": [
"Worktree /home/claude/objectstack-issue-28253 kept this time, as the patch order asks; clean at 3fc28cac1f with node_modules and a cached build in place.",
"Changeset body unchanged: it names the three deletions of the first round, and this round deletes only a heading tail and one clause, both inside the same file's ceiling."
],
"open_questions": [],
"out_of_scope_findings": [
"carrier: the seat (stated in the patch order) — SKILL.md rule-index clause (line 45, after PR #28795), references/data-hooks.md ctx.notice row, and skills/objectstack-automation/SKILL.md node table lacking save_notice (review ③-6); not filed by the dev."
]
}
ACCEPT → PR #28813 comment 6564443710 (head 3fc28cac1f20a6ff2b5f5e8ffad392a3ee83f8ce; contract review PASS 6564252529 → patch round 6564403990 → delta PASS 6564433152). The PR is a draft with needs-user-decision, and os-zhuang and hotlong are requested as reviewers; it waits on an authorised approver. Follow-up filed: #28819 (SKILL.md index + data-hooks.mdctx.notice row).
Activity
objectstack-fleet commentedon Oct 30, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: p3 ·
domain:skills·area:data·pm:blockedon #28251 · 2026-10-30T20:02Z⛔ Not a claim.
Blocked-by: #28251is added to the body.pm:blocked.Generated by Claude Code
objectstack-fleet commentedon Nov 1, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: unblocked · p3 ·
pm:queue· 2026-11-01T19:55Z⛔ Not a claim.
Blocked-by: #28251is closed as completed. That is the sandboxed hook and flow save-notice spelling this card's "Why it waits" names; PR feat: hooks and record-triggered flows set the save notice (ctx.notice, save_notice node) #28753 landed it. Save answer: a hook or record-triggered flow can attach one user-facing notice to a record save, shown with the save toast #28000 is closed too.pm:blocked→pm:queue. The skills edit is Tier H.Generated by Claude Code
objectstack-fleet commentedon Nov 1, 2026
domain:skills#2 · marchtian · session_017ihL2g7RyXa8ErM6GAkHyc
Claim: PM loop round 1
Session:
session_017ihL2g7RyXa8ErM6GAkHycAccount:
marchtianBranch:
claude/issue-28253-save-notice-skillWorktree:
objectstack-issue-28253Domain:
domain:skillsSeat:
domain:skills#2File surface:
skills/objectstack-data/rules/lifecycle.md, plus the changeset the published skill package owes.Container & model:
M,mode:subagent,model: claude-fable-5-1(dispatch-gates --tier: MANDATORY onskills/**, clause ①, no one-line exit),effort: high; then an independentCONTRACT_REVIEW_TIERreview by a separate agent.Clause-②: no
Metadata-protocol: none
Responsibility:
n/a — not a defect card(aSkills-docs: follow-up owedline)Thread-read: 6563078644
Premise: holds —
Blocked-by: #28251is closed and PR #28753 merged, so a sandboxed hook or flow can set the save notice; onmainrules/lifecycle.mddoes not teach it.Serial constraints cleared: no open PR touches the file surface above (read over every open PR).
Landing: Tier H (
skills/**). The PR stays draft withneeds-user-decisionuntil an authorised approver acts.objectstack-fleet commentedon Nov 1, 2026
domain:skills#2 · marchtian · session_017ihL2g7RyXa8ErM6GAkHyc
os-dev-report
{ "issue": 28253, "status": "done", "branch": "claude/issue-28253-save-notice-skill", "pr": "https://github.com/objectstack-ai/objectstack/pull/28813", "head": "63afc09f2dc6c50cca54ca4fefd99367161d27d9", "session": "session_017ihL2g7RyXa8ErM6GAkHyc", "premise_still_valid": true, "summary": "skills/objectstack-data/rules/lifecycle.md gains a Save Notice section taken from the live code: ctx.notice(message) on a create/update hook, inline handler and sandboxed body alike with no capability; the answer carries it as notice and the console shows it on the save toast; info severity, distinct from validation warnings and from a record:alert banner; one message per write, the last call wins, never stored; insert/update dispatches only, a write nobody waits on sets nothing, a non-string refuses the write, a refused write answers its error; and the flow face (save_notice node, refused outside a create/update record trigger and after an approval or wait). Paid inside the file's token ceiling by deleting restating prose in the same file. Changeset: @objectstack/skills patch. PR is draft, no reviewers, no assignees.", "files_changed": [ "skills/objectstack-data/rules/lifecycle.md (156 to 161 lines; 6360 to 6347 bytes; 1590 to 1587 tokens, ceiling 1590, headroom 0 to 3)", ".changeset/28253-skill-save-notice.md (new, @objectstack/skills patch, Clause-②: no, Metadata-protocol: none)" ], "token_budget": { "file": "rules/lifecycle.md 1590 to 1587 tokens against ceiling 1590 (headroom 3); no ceiling moved", "package_skill_md_lines": "sum of the 11 skills/*/SKILL.md: 4561 to 4561 (no SKILL.md touched)", "bundle_total_tokens": "222242 to 222239 (check-skills-token-ratchet price tag)", "paid_with": "deleted restating prose in the same file: the Decision Tree section (items 1-5 restated the Lifecycle Classes table and the four Syntax examples; item 6 restated the MIXED example comment and the fifth Validation rule), the telemetry-growth anecdote in Why This Exists, and the sentence 'Nothing is deleted.' in the first Safety Semantics bullet (it restated 'rows are never deleted')" }, "tests": "Docs-only diff in a published skill package; no test file owed. node scripts/check-skills-token-ratchet.mjs at 63afc09f2d: exit 0, 'skills/objectstack-data/rules/lifecycle.md is 1587 tokens (ceiling 1590; headroom 3)', '57 authored bundle file(s) within their ceilings'.", "gates": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths; change set from git: 2 files, +34/-18) at head 63afc09f2d derived 43 families. First sweep without dist: 38 exit 0, 5 exit 3 PREREQUISITE NOT MET (pnpm --filter @objectstack/lint run check:doc-formula-expressions, check:dts-closure, check:dual-build-cjs-loads, check:lean-entry-closure, check:sourcemap-no-sources-content). pnpm build --concurrency=2 under scripts/pm/os-verify-lock.sh: VERDICT command-exit 0 (116/117 tasks replayed from the shared turbo cache; waited 196s behind another dev's build). The five re-run on the same head: all exit 0. --ran over the merged record: '43 derived famil(ies) accounted for — 43 run, 0 NOT-MEASURED, 0 UNRUN', exit 0. Every exit code captured before any pipe (cmd to a log file; status=$?). Skills families among them, all exit 0: check-skills-token-ratchet (+ --self-test), check:skill-frame-sync, check:skill-identifier-liveness, check:skill-compatibility, check:doc-authoring, check:pm-skill-id-lint, check:role-word, check:corpus-claim-drift, check:published-files, check-empty-changeset, check-changeset-no-major, check-adr-0087-registration. Local-only families not derived and not run: check:dev-prereqs, check:skill-frame-freshness (no frame file, no packages/spec source touched). CI families are the PR's check-runs (in_progress at report time), not re-run locally.", "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm", "mcp_calls": "0 — no mcp__github__* tool was called (reads went through gh api REST: issue 28253 body and comments, pulls 28795 files, pulls 28753 and its files, pulls 28813 read-back)", "api_writes": "2 REST writes plus 1 git push — (1) git push -u origin claude/issue-28253-save-notice-skill (not REST); (2) pr_create via scripts/pm/fleet-write/dispatch.mjs: POST /repos/objectstack-ai/objectstack/pulls, run 41351802290 success, read-back 7830 bytes sent = 7830 stored, PR #28813; (3) this comment via scripts/pm/post-stamped.mjs --comment=28253: POST /repos/objectstack-ai/objectstack/issues/28253/comments", "deviations": [ "No label sent with pr_create: the dispatch named none and skip-changeset does not apply (a changeset is included). The labels the PR now carries (documentation, size/s, tooling) were set by another actor (a labeler) after the create, not by this dev; left as found. The claim's needs-user-decision on the PR is the seat's act at hand-off.", "skills/objectstack-data/SKILL.md not touched (PR #28795 open on it): its rule index line 45 describes rules/lifecycle.md as retention/TTL/rotation/archive only and does not name the save notice; if the index must name the topic, it is a follow-up on that file after #28795 lands.", "Worktree /home/claude/objectstack-issue-28253: node_modules and the worktree removed after this report, per the os-dev cleanup clause; the branch is on origin at 63afc09f2d." ], "facts": [ "ctx.notice is bound by the engine on every insert/update dispatch before any handler runs: packages/objectql/src/engine.ts:5996-5998; packages/objectql/src/hook-notice.ts:29 (NOTICE_EVENTS beforeInsert/afterInsert/beforeUpdate/afterUpdate), :61-67 (bound on those, deleted on every other event)", "Anything but a non-empty string is refused with a TypeError naming the call, so the write fails: packages/objectql/src/hook-notice.ts:42-47; pinned on a sandboxed body at packages/runtime/src/sandbox/hook-notice.integration.test.ts:118-120", "A write that passed no onNotice listener (batch, seed, import, a flow node's own write) sets nothing: packages/objectql/src/hook-notice.ts:16-18, :48-52; packages/spec/src/data/hook.zod.ts:1622-1626", "Sandboxed body spelling is the same ctx.notice(message), marshalled as a host function with no capability token: packages/spec/src/data/hook.zod.ts:1612-1617; packages/runtime/src/sandbox/body-runner.ts:1302-1306; packages/runtime/src/sandbox/quickjs-runner.ts:945-966; integration pin packages/runtime/src/sandbox/hook-notice.integration.test.ts:47-55, :105-113", "Inline handler reaches the same member: packages/spec/src/data/hook.zod.ts:1612-1613 (an in-process handler calls it like the cast ctx.input.options listener)", "One message per write, the last call before the answer wins; a refused write answers its error, never a notice; a nested write carries its own options; nothing is stored: packages/spec/src/contracts/data-engine.ts:156-163; packages/spec/src/api/protocol.zod.ts:2760-2764", "The create/update answer carries it as notice; info severity, reports an outcome never a problem, distinct from warnings (advisory validation-rule hits); a client shows it with or in place of its own success message: packages/spec/src/api/protocol.zod.ts:2755-2764 (WRITE_ANSWER_NOTICE_DESCRIPTION), :2785 (CreateDataResponseSchema.notice), :2996 (UpdateDataResponseSchema.notice); onNotice is info severity and never rides onValidationAdvisory: packages/spec/src/contracts/data-engine.ts:128-134", "The console shows it on the one save toast: ui/packages/react/src/record-save-feedback.ts:184-195 (toastRecordSave: notice leads the lines; a notice alone keeps the success variant; any warning puts it on the warning variant), :159 (the parked answer's notice, last one wins); ui/packages/app-shell/src/providers/writeWarningToast.ts:255-262, :290-301 (unclaimed: an info toast of its own)", "Flow face: the save_notice node renders config.message ({{ }}) and sets it on the triggering write's channel: packages/services/service-automation/src/builtin/save-notice-node.ts:37-61 (descriptor, message required), :62-107 (execute); the record-change trigger hands the run the write's channel: packages/triggers/trigger-record-change/src/record-change-trigger.ts:606-611, :175-197 (saveNoticeChannelOf wraps ctx.notice and the writer's locale)", "Placement refused at the flow parse outside a create/update record trigger, regex record-(before|after)-(create|insert|update|write), at any region depth: packages/spec/src/automation/save-notice-placement.ts:31, :52-80", "Refused at run time after a suspension or in a child run: the channel is closed once the run returns (packages/triggers/trigger-record-change/src/record-change-trigger.ts:183-190, :366-369); a run holding no channel refuses the node by name (packages/services/service-automation/src/builtin/save-notice-node.ts:65-73); subflow and map children get no channel (packages/services/service-automation/src/builtin/subflow-node.ts:121-123; map-node.ts:244-246)", "record:alert is a record-page banner block (page metadata), dismissible per viewer and shown by a visible predicate: packages/spec/src/ui/page.zod.ts:238, :1436; content/docs/protocol/objectui/record-alert.mdx:8-35", "Shipping package: skills/** is synced into packages/skills dist (packages/skills/package.json:17-21 files, :28 build = sync-catalog), so the changeset is on @objectstack/skills; docs-only addition = patch (AGENTS.md Post-Task Checklist step 3: a none kind takes patch)", "On origin/main c064d3a246, rules/lifecycle.md carried no mention of notice, ctx.notice or save_notice (grep over skills/objectstack-data: zero hits outside validation.md's severity enum), and its ceiling row is scripts/check-skills-token-ratchet.mjs:702 (1590) with headroom 0 before this PR" ], "open_questions": [ { "question": "Should skills/objectstack-data/SKILL.md's rule index (line 45, the rules/lifecycle.md entry) name the save notice, and should references/data-hooks.md's sandbox ctx table gain a ctx.notice row?", "options": ["A: a follow-up on SKILL.md after PR #28795 lands, one clause in the existing entry (headroom 22 tokens today)", "B: leave the index as is; the lifecycle page's own heading suffices"], "recommendation": "A for SKILL.md (a few tokens); data-hooks.md has 2 tokens of headroom, so a ctx.notice row there must be paid by deleting a sentence in that file, a separate small edit the seat can carry or drop." } ], "out_of_scope_findings": [ "carrier: none · noted, not filed — skills/objectstack-data/references/data-hooks.md:352-368 (sandbox ctx.* table) and :573-610 (HookContext interface listing) carry no ctx.notice row; a doc gap, not a defect class (no refused or dropped metadata); headroom 2 tokens, so any row there is paid by deletion in that file. Dedupe words: data-hooks ctx.notice sandbox table save notice" ] }Reading: 2026-11-01T21:26Z
objectstack-fleet commentedon Nov 1, 2026
domain:skills#2 · marchtian · session_017ihL2g7RyXa8ErM6GAkHyc
os-dev-report
Delta to report 6564123997: patch round for the three should-fixes of contract review 6564252529 on PR #28813.
{ "issue": 28253, "status": "done", "branch": "claude/issue-28253-save-notice-skill", "pr": "https://github.com/objectstack-ai/objectstack/pull/28813", "head": "3fc28cac1f20a6ff2b5f5e8ffad392a3ee83f8ce", "previous_head": "63afc09f2dc6c50cca54ca4fefd99367161d27d9", "session": "session_017ihL2g7RyXa8ErM6GAkHyc", "premise_still_valid": true, "summary": "One commit on skills/objectstack-data/rules/lifecycle.md (9 insertions, 9 deletions): (1) the Save Notice paragraph now reads 'a sandboxed body or the deprecated inline handler, no capability', leading with body and naming handler as the deprecated door (references/data-hooks.md:317-325); (2) the record:alert contrast now reads 'shown whenever the record's state says so' instead of 'on every visit', since the banner is dismissible per viewer (page.zod.ts:1436, record-alert.mdx:33-34); (3) the intro now reads 'Distinct from lifecycle hooks (…), which run logic on writes: this page covers the rows' retention, rotation and archival — and, at its end, the save notice a hook hands the saver', so it no longer contradicts itself. Paid inside the ceiling by shortening the section heading to 'Save Notice' (its tail restated the first sentence) and one intro clause.", "files_changed": [ "skills/objectstack-data/rules/lifecycle.md (161 lines unchanged; 6347 to 6358 bytes; 1587 to 1590 tokens, ceiling 1590, headroom 0)" ], "token_budget": "rules/lifecycle.md 1590/1590 (headroom 0; no ceiling moved); sum of 11 skills/*/SKILL.md lines 4561 (unchanged); bundle total 222242 tokens (equal to main's 222242; the first round's -3 is spent)", "tests": "node scripts/check-skills-token-ratchet.mjs at 3fc28cac1f: exit 0, 'skills/objectstack-data/rules/lifecycle.md is 1590 tokens (ceiling 1590; headroom 0)', '57 authored bundle file(s) within their ceilings'.", "gates": "Worktree recreated from origin (git worktree add on the pushed branch, tracking, no new branch, no empty push); pnpm install --frozen-lockfile; pnpm build --concurrency=2 under scripts/pm/os-verify-lock.sh: VERDICT command-exit 0 (waited 0s, cache replay). node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths) at head 3fc28cac1f derived 43 families; every command recorded with its exit code captured before any pipe: 43 exit 0, none exit 3. --ran: '43 derived famil(ies) accounted for — 43 run, 0 NOT-MEASURED, 0 UNRUN', exit 0. Skills families among them all exit 0 (check-skills-token-ratchet + --self-test, skill-frame-sync, skill-identifier-liveness, skill-compatibility, doc-authoring, pm-skill-id-lint, role-word, corpus-claim-drift, published-files, the changeset gates). CI families are the PR's check-runs on 3fc28cac1f (in_progress at report time).", "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm", "mcp_calls": "0 — no mcp__github__* tool called; reads via gh api REST: review comment 6564252529 (read in full)", "api_writes": "1 REST write plus 1 git push — git push origin claude/issue-28253-save-notice-skill (63afc09f2d to 3fc28cac1f, not REST); this comment via scripts/pm/post-stamped.mjs --comment=28253 (POST /repos/objectstack-ai/objectstack/issues/28253/comments). PR body not patched (dev writes it once); the review's three items are answered by this delta.", "deviations": [ "Worktree /home/claude/objectstack-issue-28253 kept this time, as the patch order asks; clean at 3fc28cac1f with node_modules and a cached build in place.", "Changeset body unchanged: it names the three deletions of the first round, and this round deletes only a heading tail and one clause, both inside the same file's ceiling." ], "open_questions": [], "out_of_scope_findings": [ "carrier: the seat (stated in the patch order) — SKILL.md rule-index clause (line 45, after PR #28795), references/data-hooks.md ctx.notice row, and skills/objectstack-automation/SKILL.md node table lacking save_notice (review ③-6); not filed by the dev." ] }Reading: 2026-11-01T21:51Z
objectstack-fleet commentedon Nov 1, 2026
domain:skills#2 · marchtian · session_017ihL2g7RyXa8ErM6GAkHyc
ACCEPT → PR #28813 comment 6564443710 (head
3fc28cac1f20a6ff2b5f5e8ffad392a3ee83f8ce; contract review PASS 6564252529 → patch round 6564403990 → delta PASS 6564433152). The PR is a draft withneeds-user-decision, and os-zhuang and hotlong are requested as reviewers; it waits on an authorised approver. Follow-up filed: #28819 (SKILL.md index +data-hooks.mdctx.noticerow).