Repository navigation
Commit 56bd596
docs(skills): objectstack-data rules/lifecycle.md teaches the save notice (#28813)
<!-- claude-projects-attribution: {"github_login":"marchtian"} -->
_Requested by **田騏** · [project
thread](https://claude.ai/code/project/chan_01VmuUhXU9SSUzqJYyXSmYSi?thread=cmsg_01VmuUhXU9SSUzqJYyXSmYSiHXKqxUqDPJKZyNHP7BoqTb)_
Fixes #28253
Clause-②: no
Metadata-protocol: none
## 维护者速读
- **改了什么**:`objectstack-data` 技能的 `rules/lifecycle.md` 新增「Save
Notice」一节:保存钩子用 `ctx.notice('…')` 告诉保存者这次保存触发了什么(inline `handler` 与沙箱
`body` 同一拼法、不需要能力声明),记录触发的 flow 用 `save_notice` 节点设同一条消息;info
级、一次保存一条、后设者胜、与校验 `warnings` 和 `record:alert` 横幅的区别。另附
changeset(`@objectstack/skills` patch)。
- **为什么改**:运行时早已支持(hook 面与 flow 面都已合入主干),但技能里没教,AI 作者写保存钩子时仍去拼
`record:alert` 横幅来「告诉用户」;这是 #28000 的 Skills-docs 欠账。
- **风险与回滚**:纯文档,不改 schema、不改运行时;整文件 token 不超原天花板(1590 → 1587)。回滚即 revert
本 PR。
- **先看**:`skills/objectstack-data/rules/lifecycle.md` 末尾新节(主要
hunk),以及被删去的「Decision Tree」一节(用它付 token 账)。
- **两分钟验证**:`node scripts/check-skills-token-ratchet.mjs`,预期看到
`rules/lifecycle.md is 1587 tokens (ceiling 1590; headroom 3)` 且整体 `✓`。
- **需要你做的**:`skills/**` 为受管面(Tier H),需要授权账号的 APPROVED 评审后由席位落地。
## What changes
`skills/objectstack-data/rules/lifecycle.md` gains **Save Notice — what
the save set in motion**, teaching what the live code does:
- A create/update hook calls `ctx.notice('Sent to your manager for
approval')` — inline `handler` and sandboxed `body` alike, no capability
(`HookContext.notice`, bound by the engine on every insert/update
dispatch; the body runner marshals it as a host function).
- The create/update answer carries it as `notice` and the console shows
it on the save toast. Info severity: an outcome, never a problem —
distinct from validation `warnings`, and from a `record:alert` banner
(page metadata shown by the record's state on every visit); the notice
is for what *this* save started.
- One message per write, the last call wins, never stored; insert/update
dispatches only; a write nobody waits on (batch, seed, import, a nested
write) sets nothing; a non-string refuses the write; a refused write
answers its error, no notice.
- A flow a record create/update trigger starts sets the same channel
with a `save_notice` node (`config.message`, `{{ }}` renders); refused
in any other flow, and after an `approval` / `wait`.
The intro's "not to be confused with lifecycle hooks" sentence now says
the page also carries the save notice.
**Paid inside the file's token ceiling** (no ceiling moves), by deleting
restating prose in the same file:
- the **Decision Tree** section — items 1–5 restated the Lifecycle
Classes table (class, contract, typical use) and the four Syntax
examples; item 6 restated the MIXED syntax example's comment and the
fifth Validation rule (`onlyWhen` + rotation/archive);
- the telemetry-growth anecdote in *Why This Exists* (an incident
narrative; the rule sentence that follows it stays);
- "Nothing is deleted." in the first Safety Semantics bullet, which
restated *Why This Exists* ("rows are never deleted").
`.changeset/28253-skill-save-notice.md` — `@objectstack/skills: patch`
(the skill ships in that package's `dist`).
**Skills readings** (`skills/**` diff, two readings each):
| Reading | before | after |
|:--|:--|:--|
| `rules/lifecycle.md` lines | 156 | 161 |
| `rules/lifecycle.md` bytes / tokens (`ceil(bytes/4)`) | 6360 / 1590
(ceiling 1590, headroom 0) | 6347 / 1587 (ceiling 1590, headroom 3) |
| package: sum of all 11 `skills/*/SKILL.md` lines | 4561 | 4561 (no
SKILL.md touched) |
| bundle total (whole shipped tree, tokens) | 222242 | 222239 |
Not touched: `skills/objectstack-data/SKILL.md` (PR #28795 is open on
it). Its rule index entry for `rules/lifecycle.md` (line 45) still
describes the page as retention/TTL/rotation/archive only and does not
name the save notice; if the index must name the new topic, that is a
follow-up on that file once #28795 lands.
## Tests
Docs-only diff in a published skill package: no test file is owed. The
gate family that measures the page is the token ratchet, run on the
final head:
- `node scripts/check-skills-token-ratchet.mjs` → exit 0 —
`rules/lifecycle.md is 1587 tokens (ceiling 1590; headroom 3)`; `57
authored bundle file(s) within their ceilings`.
## Acceptance notes
- Every sentence in the new section is taken from the live code, not
from the card's summary: `packages/objectql/src/hook-notice.ts`
(binding, the non-string refusal, the no-listener no-op),
`packages/spec/src/data/hook.zod.ts` (`HookContext.notice`, handler and
body alike, no capability),
`packages/runtime/src/sandbox/body-runner.ts` and `quickjs-runner.ts`
(the sandbox marshal), `packages/spec/src/contracts/data-engine.ts`
(last call wins, refused write answers its error, nothing stored),
`packages/spec/src/api/protocol.zod.ts` (the answer's `notice`, info
severity, distinct from `warnings`),
`ui/packages/react/src/record-save-feedback.ts` (the save toast),
`packages/spec/src/automation/save-notice-placement.ts` and
`packages/services/service-automation/src/builtin/save-notice-node.ts`
(the flow node and its refusals).
- Noted, not filed (no defect class):
`skills/objectstack-data/references/data-hooks.md` — the sandbox `ctx.*`
table and the `HookContext` interface listing carry no `ctx.notice` row;
that file sits at 2 tokens of headroom, so a row there must be paid for
by deletion in that file. Carrier: none.
- No label was sent with the create: the dispatch named none, and
`skip-changeset` does not apply (a changeset is included).
## Verification
Gate derivation on the final head (`node scripts/pm/dispatch-gates.mjs
--commands --repo objectstack-ai/objectstack`, no paths, change set
derived from git: 2 files, +34 / −18): 43 families derived; every
command recorded with its exit code captured before any pipe, then
reconciled with `--ran`.
- First sweep at head `63afc09f2d` (install only, no `dist`): 38
families exit 0; 5 exit 3 (`PREREQUISITE NOT MET`, no built `dist`):
`check:doc-formula-expressions` (lint), `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), then the five re-run on the same head: all exit 0.
- `node scripts/pm/dispatch-gates.mjs --ran` over the merged record: `43
derived famil(ies) accounted for — 43 run, 0 NOT-MEASURED, 0 UNRUN`
(exit 0).
- Among them, every skills family the derivation names:
`check:skills-token-ratchet` (+ `--self-test`),
`check:skill-frame-sync`, `check:skill-identifier-liveness`,
`check:skill-compatibility`, `check:doc-authoring` (the no-tracker-id
rule over `skills/**`), `check:pm-skill-id-lint`, `check:role-word`,
`check:corpus-claim-drift`, `check:published-files`, the changeset gates
(`check-empty-changeset`, `check-changeset-no-major`,
`check-adr-0087-registration`) — all exit 0.
- Local-only families not derived and not run: `check:dev-prereqs`,
`check:skill-frame-freshness` (the diff touches no frame file and no
`packages/spec` source).
- CI families (`Lint & Repo Gates`, the type-check lanes and the rest)
are the PR's check-runs; nothing there is re-run locally.
Dogfood battery: n/a — no `!` in the title and no `(narrowing)` arm.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 7107bde commit 56bd596
2 files changed
Lines changed: 35 additions & 19 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | 3 | | |
4 | | - | |
5 | | - | |
6 | | - | |
7 | | - | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
8 | 8 | | |
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
12 | 12 | | |
13 | | - | |
14 | | - | |
15 | | - | |
| 13 | + | |
16 | 14 | | |
17 | 15 | | |
18 | 16 | | |
| |||
119 | 117 | | |
120 | 118 | | |
121 | 119 | | |
122 | | - | |
123 | | - | |
| 120 | + | |
124 | 121 | | |
125 | 122 | | |
126 | 123 | | |
| |||
144 | 141 | | |
145 | 142 | | |
146 | 143 | | |
147 | | - | |
148 | | - | |
149 | | - | |
150 | | - | |
151 | | - | |
152 | | - | |
153 | | - | |
154 | | - | |
155 | | - | |
156 | | - | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
0 commit comments