From 63afc09f2dc6c50cca54ca4fefd99367161d27d9 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 1 Nov 2026 21:05:27 +0000 Subject: [PATCH 1/2] docs(skills): objectstack-data rules/lifecycle.md teaches the save notice The data skill's lifecycle rule gains a Save Notice section: ctx.notice for inline handler and sandboxed body alike, info severity distinct from validation warnings and from a record:alert banner, one message per write with the last call winning, and the save_notice flow node. Paid inside the file's token ceiling by deleting the Decision Tree, the telemetry-growth anecdote and a Safety Semantics restatement. Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_017ihL2g7RyXa8ErM6GAkHyc --- .changeset/28253-skill-save-notice.md | 12 +++++++ skills/objectstack-data/rules/lifecycle.md | 40 ++++++++++++---------- 2 files changed, 34 insertions(+), 18 deletions(-) create mode 100644 .changeset/28253-skill-save-notice.md diff --git a/.changeset/28253-skill-save-notice.md b/.changeset/28253-skill-save-notice.md new file mode 100644 index 00000000000..1cca3b1c748 --- /dev/null +++ b/.changeset/28253-skill-save-notice.md @@ -0,0 +1,12 @@ +--- +'@objectstack/skills': patch +--- + +docs(skills): objectstack-data rules/lifecycle.md teaches the save notice + +Clause-②: no +Metadata-protocol: none + +- **Before.** A hook can tell the person who made a create or update what the save set in motion (`ctx.notice(message)`, answered as the create/update `notice`), and a flow a record create/update trigger starts can set the same message with a `save_notice` node, but no published skill taught it, so an AI author writing a save hook from `objectstack-data` kept reaching for a `record:alert` banner. +- **Now.** `objectstack-data/rules/lifecycle.md` gains *Save Notice — what the save set in motion*: the `ctx.notice` spelling for inline `handler` and sandboxed `body` alike (no capability), info severity and the distinction from validation `warnings` and from a `record:alert` banner, one message per write with the last call winning, the dispatches that carry it, the non-string refusal, and the flow face (`save_notice`, refused outside a create/update record trigger and after an `approval` / `wait`). +- The file stays under its token ceiling: the new text is paid for by deleting the Decision Tree (which restated the class table and the syntax examples), the telemetry-growth anecdote and a Safety Semantics restatement. No ceiling moves; no schema, key or behaviour changes. diff --git a/skills/objectstack-data/rules/lifecycle.md b/skills/objectstack-data/rules/lifecycle.md index 8bdd63b4513..baa2b1f306b 100644 --- a/skills/objectstack-data/rules/lifecycle.md +++ b/skills/objectstack-data/rules/lifecycle.md @@ -2,17 +2,15 @@ Guide for declaring how long an object's data lives and how its space is reclaimed (ADR-0057 — system data lifecycle & retention). Not to be -confused with **lifecycle hooks** (`beforeInsert` / `afterUpdate` …) — -those run business logic on data operations; *this* page is about -retention, rotation, and archival of the rows themselves. +confused with **lifecycle hooks** (`beforeInsert` / `afterUpdate` …): *this* +page is the rows' retention, rotation and archival, plus the save notice a +hook hands the saver. ## Why This Exists Every object without a `lifecycle` block is **immortal**: rows are never deleted, and on SQLite the file never shrinks. That's correct for business -records — and a guaranteed disk leak for anything append-only. A scheduled -flow writing telemetry every 20 seconds once grew a dev database from 2 MB -to 260 MB with zero business data in it. +records — and a guaranteed disk leak for anything append-only. **Rule: any append-only, high-write-rate object (event log, run history, delivery outbox, ephemeral tokens) MUST declare a `lifecycle` block.** @@ -119,8 +117,7 @@ lifecycle: { ## Safety Semantics -- **No `lifecycle` block = today's behavior.** Nothing is deleted. `record` - is the implicit default. +- **No `lifecycle` block = today's behavior**: `record` is the implicit default. - **Archive-then-delete is atomic-ish and safe by default**: an object declaring `archive` is never hot-deleted before the copy to the archive datasource succeeded. If no datasource is registered under the `archive.to` @@ -144,13 +141,20 @@ lifecycle: { | `growth_alert_rows` | settings | Per-sweep growth spike alert | | `OS_LIFECYCLE_DISABLED=1` | env | Disables sweeping entirely | -## Decision Tree - -1. Users create/edit it and it represents business state? → `record` (omit the block). -2. Is it a compliance/audit trail? → `audit` + `retention`, add `archive` if cold storage exists. -3. Written by machines at high frequency, value decays in days/weeks? → `telemetry` + `retention` (add rotation `storage` for the hottest tables). -4. Meaningless after a deadline (tokens, receipts, read-state)? → `transient` + `ttl` on the natural expiry field. -5. Bus/fan-out messages? → `event` + a short `ttl` (hours). -6. Table mixes prunable history with live state (e.g. terminal vs paused runs)? - → add `retention.onlyWhen: { status: { $in: [...] } }` so rows outside the - filter are retained regardless of age. Not combinable with rotation/archive. +## Save Notice — what the save set in motion + +A create/update hook calls `ctx.notice('Sent to your manager for approval')` +— inline `handler` and sandboxed `body` alike, no capability. The answer +carries it as `notice`; the console shows it on the save toast. **Info +severity**: an outcome, never a problem — distinct from validation +`warnings`. A `record:alert` banner is 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. +- Not a non-empty string ⇒ the write refuses; a refused write answers its + error, no notice. +- A flow a record create/update trigger starts sets it with a `save_notice` + node (`config.message`, `{{ }}` renders); refused in any other flow, and + after an `approval` / `wait`. From 3fc28cac1f20a6ff2b5f5e8ffad392a3ee83f8ce Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 1 Nov 2026 21:40:26 +0000 Subject: [PATCH 2/2] =?UTF-8?q?docs(skills):=20lifecycle.md=20save=20notic?= =?UTF-8?q?e=20=E2=80=94=20lead=20with=20body,=20soften=20the=20banner=20c?= =?UTF-8?q?ontrast,=20reword=20the=20intro?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Contract-review should-fixes: the sandboxed body is named first and the inline handler as the deprecated door, matching the hooks reference; a record:alert banner shows whenever the record's state says so rather than on every visit, since a viewer can dismiss it; the intro no longer reads as contradicting itself about hooks. Paid inside the file's token ceiling by shortening the section heading and one intro clause. Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_017ihL2g7RyXa8ErM6GAkHyc --- skills/objectstack-data/rules/lifecycle.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/skills/objectstack-data/rules/lifecycle.md b/skills/objectstack-data/rules/lifecycle.md index baa2b1f306b..4495b44c81a 100644 --- a/skills/objectstack-data/rules/lifecycle.md +++ b/skills/objectstack-data/rules/lifecycle.md @@ -1,10 +1,10 @@ # Data Lifecycle & Retention Guide for declaring how long an object's data lives and how its space is -reclaimed (ADR-0057 — system data lifecycle & retention). Not to be -confused with **lifecycle hooks** (`beforeInsert` / `afterUpdate` …): *this* -page is the rows' retention, rotation and archival, plus the save notice a -hook hands the saver. +reclaimed (ADR-0057 — system data lifecycle & retention). Distinct from +**lifecycle hooks** (`beforeInsert` / `afterUpdate` …), 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. ## Why This Exists @@ -141,14 +141,14 @@ lifecycle: { | `growth_alert_rows` | settings | Per-sweep growth spike alert | | `OS_LIFECYCLE_DISABLED=1` | env | Disables sweeping entirely | -## Save Notice — what the save set in motion +## Save Notice A create/update hook calls `ctx.notice('Sent to your manager for approval')` -— inline `handler` and sandboxed `body` alike, no capability. The answer -carries it as `notice`; the console shows it on the save toast. **Info +— a sandboxed `body` or the deprecated inline `handler`, no capability. The +answer carries it as `notice`; the console shows it on the save toast. **Info severity**: an outcome, never a problem — distinct from validation -`warnings`. A `record:alert` banner is page metadata shown by the record's -state on every visit; the notice is for what *this* save started. +`warnings`. A `record:alert` banner is page metadata shown whenever the +record's state says so; 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,