Skip to content

feat(lifecycle): ADR-0057 P1–P4 — lifecycle contract, Reaper/Rotator/Archiver, datasource separation, governance (#2786)#2791

Merged
os-zhuang merged 7 commits into
mainfrom
claude/adr-0057-data-lifecycle-azuf45
Jul 11, 2026
Merged

feat(lifecycle): ADR-0057 P1–P4 — lifecycle contract, Reaper/Rotator/Archiver, datasource separation, governance (#2786)#2791
os-zhuang merged 7 commits into
mainfrom
claude/adr-0057-data-lifecycle-azuf45

Conversation

@os-zhuang

@os-zhuang os-zhuang commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Implements ADR-0057 data lifecycle P1–P4 (tracking issue #2786) — platform-generated data becomes bounded by construction. Grew out of the 260 MB dev.db incident: every platform-generated object defaulted to immortal, and the file never shrank.

P1 — contract (spec, objectql, driver-sql)

  • New lifecycle object property: class: record | audit | telemetry | transient | event + retention.maxAge / ttl.{field, expireAfter} / storage(rotation) / archive.{after, to, keep} / reclaim, with duration literals ('6h', '14d', '7y'). Parse-time invariants (ADR §3.5 enforce-or-remove): a non-record class with no bounding policy is rejected; policies on record are rejected; archive.after must equal retention.maxAge.
  • LifecycleService registered by ObjectQLPlugin (platform-owned, default-on; OS_LIFECYCLE_DISABLED=1 or lifecycle.enabled=false to opt out). The Reaper bulk-deletes rows past retention.maxAge/ttl hourly under a system context — one afterDelete hook max per object per sweep, and telemetry sys_* objects are in the audit writer's SKIP_OBJECTS, so cleanup never re-feeds the tables it drains (ADR-0052 self-audit trap).
  • SqlDriver.reclaimSpace() — the PRAGMA incremental_vacuum hook the ADR's P0 text described but which had never actually landed.
  • 11 platform objects annotated: sys_activity 14d+rotation, sys_audit_log 90d→archive 7y, sys_metadata_audit 365d→archive, sys_job_run/sys_automation_run/sys_http_delivery 30d, notification pipeline (event/delivery/receipt/inbox) one 90d window, sys_device_code expires_at+1d.
  • Gates: spec-liveness classifies object/lifecycle and binds it to a dogfood proof (HIGH_RISK_CLASSES data-lifecycle); new dogfood storage-growth gate (@proof: adr0057-lifecycle-bounded-growth) boots showcase, reaps a backdated runaway writer, and asserts record-class data is untouched.

P2 — Rotator (driver-sql)

storage: { strategy: 'rotation', shards, unit } physically time-shards the table on SQLite (ServiceNow Table Rotation model): writes land in the current shard (<table>__r<key>), reads go through a UNION-ALL view under the base name, expiry is an O(1) DROP of shards past the shards × unit window. Legacy tables are adopted as the first shard on upgrade (no data loss); by-id and bulk writes fan out shard-wise; other dialects fall back to an equivalent age-based reap so the declared bound holds everywhere. (Transient TTL expiry shipped with the P1 Reaper — TTL and age reaping are one mechanism per §3.3.)

P3 — separation + Archiver (objectql)

  • Registering a datasource named telemetry routes telemetry/event/audit objects to it (engine getDriver step 3; opt-in purely by the datasource's existence). transient deliberately stays on the primary — some of those objects (better-auth's sys_device_code) are accessed outside the engine.
  • Archiver: audit objects with archive declared get retain → archive → delete — batched idempotent copies to the cold store, hot-delete of exactly the copied ids, archive.keep pruning of the archive itself. Hard rule: no archive datasource ⇒ rows retained and reported archive-pending; a compliance ledger is never dropped unarchived.

P4 — governance (objectql + settings)

New lifecycle settings namespace: runtime enable switch, retention_overrides (tenant-scoped — regulated tenants set years, dev sets days, ADR §3.2; per-tenant windows sweep each overriding tenant on its own cutoff while the global pass covers everyone else including NULL-org rows), quotas/quota_defaults and growth_alert_rows — quota/growth breaches are observe-and-alert only (report.alerts + onAlert sink), never deletion beyond declared policy.

Verification

  • 26 LifecycleService unit tests (Reaper/Rotator/Archiver/governance), 5 driver rotation tests, 3 datasource-routing tests, 9 spec schema tests
  • Dogfood storage-growth gate: 6 end-to-end cases through a real showcase boot (contract coverage, reap, rotation via engine→driver, archive-safety, hot→cold drain to a real second SQL store)
  • Full test suites of all 11 touched packages green (68 turbo tasks, incl. the complete dogfood suite); spec-liveness gate green

Also included: the initial doc-only commit syncing the ADR header (P0 shipped in #2079), and a final commit marking §4 P1–P4 implemented + the changeset (all minor, per the launch-window convention).

Closes #2786.

🤖 Generated with Claude Code

https://claude.ai/code/session_01BNBzMWmSECrbiEDdVzwBt3

…racked in #2786

The ADR header still said 'Proposed' with no pointer to the shipped P0
implementation or the new P1–P4 tracking issue. Per repo convention
(cf. ADR-0030/0055/0069 headers), reflect implementation reality:

- Status: Accepted — P0 shipped with this ADR (#2079); P1–P4 proposed,
  tracked in #2786
- §4 Rollout: link #2079 on the P0 row; add the #2786 tracking pointer

Doc-only change; no code or policy content altered.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BNBzMWmSECrbiEDdVzwBt3
@vercel

vercel Bot commented Jul 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
spec Ready Ready Preview, Comment Jul 10, 2026 3:30pm

Request Review

@github-actions github-actions Bot added size/xs documentation Improvements or additions to documentation labels Jul 10, 2026
claude added 5 commits July 10, 2026 14:45
…eaper, liveness gate, dogfood growth gate (#2786)

The platform can declare an object's structure, validations and permissions —
but not how long its data lives. P1 makes data lifecycle a first-class,
declarable, runtime-enforced metadata concern:

spec:
- `lifecycle` object property (LifecycleSchema): class (record | audit |
  telemetry | transient | event) + retention.maxAge / ttl / storage(rotation)
  / archive / reclaim, with duration literals ('6h', '14d', '7y')
- §3.5 enforce-or-remove invariants in a superRefine: a non-record class with
  no bounding policy is rejected; record-class policies are rejected; the
  archive window must start where the hot window ends
- IDataDriver.reclaimSpace?() contract for post-sweep space reclamation

objectql:
- LifecycleService (Reaper), registered by ObjectQLPlugin as a platform
  service (default-on, OS_LIFECYCLE_DISABLED=1 / lifecycle.enabled=false to
  opt out): hourly sweep deletes rows past retention.maxAge (created_at) and
  past ttl.field + expireAfter, bounds rotation-declared telemetry by
  shards×unit until the P2 Rotator lands, and never hot-deletes an
  archive-declared audit ledger before the Archiver copies it out
- Sweeps run bulk multi-deletes under a system context — at most ONE
  afterDelete hook per object per sweep, and telemetry sys_* objects are in
  the audit writer's SKIP_OBJECTS, so cleanup never re-feeds the tables it
  drains (the ADR-0052 self-audit trap)

driver-sql:
- reclaimSpace(): SQLite `PRAGMA incremental_vacuum` (the P0 ADR text
  claimed this hook shipped; it had not — this closes it), no-op on
  Postgres/MySQL

platform objects (11 annotated): sys_activity 14d+rotation,
sys_audit_log 90d→archive 7y, sys_metadata_audit 365d→archive,
sys_job_run/sys_automation_run/sys_http_delivery 30d, notification pipeline
(sys_notification / delivery / receipt / inbox) one 90d window,
sys_device_code expires_at+1d.

gates:
- spec-liveness: object/lifecycle classified + bound to a dogfood proof
  (HIGH_RISK_CLASSES 'data-lifecycle')
- dogfood storage-growth gate (@proof: adr0057-lifecycle-bounded-growth):
  boots showcase, proves contract coverage, reaps a backdated runaway
  writer, asserts record-class data untouched and archive-declared ledgers
  retained

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BNBzMWmSECrbiEDdVzwBt3
…(1) shard DROP (#2786)

High-frequency telemetry declared with lifecycle.storage.strategy='rotation'
is now physically time-sharded on SQLite (the ServiceNow Table Rotation
model, ADR-0057 §3.3):

driver-sql:
- rotateShards(objectDef, nowMs): idempotent Rotator entry — ensures the
  current shard (<table>__r<key>: day/week/month UTC keys), adopts a legacy
  pre-rotation table as its first shard (no data loss), column-syncs every
  retained shard so the UNION stays uniform, DROPs shards past the
  shards×unit time window (O(1) reclaim), and rebuilds the read view
- The base name becomes a READ-ONLY UNION ALL view (SQLite views reject
  writes and RETURNING), so every write path redirects shard-wise instead:
  create/bulkCreate/upsert target the current shard; by-id update/delete
  probe shards newest-first; updateMany/deleteMany/bulkDelete fan out and
  sum — reads (find/count/aggregate) are untouched
- Per-shard bookkeeping aliases (datetime/date/json/boolean coercion, tenant
  scope, timestamp stamping) so a shard builder behaves exactly like the view
- initObjects routes rotation-declared objects to the Rotator (the plain
  create/alter DDL path would collide with the view)
- supportsRotation gates on the dialect; declared indexes get per-shard names

objectql LifecycleService:
- Rotator invocation each sweep when the driver supports it; the fallback
  age-based reap covers every other dialect so the declared bound holds
  everywhere, only the reclamation mechanics differ
- An explicit retention.maxAge still trims inside live shards after rotation
  (shard drops are unit-granular; this also immediately bounds a legacy
  table the Rotator just adopted whole)
- Dropped shards trigger the datasource incremental-vacuum pass

Transient TTL expiry (the other P2 line item) shipped with the P1 Reaper —
TTL and age reaping are one mechanism (§3.3).

Proof: sql-driver-rotation.test.ts drives the full shard lifecycle (create →
rotate → window drop → adoption → cross-shard writes); the dogfood
storage-growth gate now shards a probe stream through the real
engine→driver path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BNBzMWmSECrbiEDdVzwBt3
…store (#2786)

Separation (§3.6): objects whose lifecycle.class is telemetry / event /
audit now route to a dedicated 'telemetry' datasource whenever one is
registered — engine getDriver() step 3, between datasourceMapping rules and
the package-manifest defaultDatasource. Opt-in purely by the datasource's
existence: no 'telemetry' driver ⇒ resolution is byte-for-byte what it was.
Even on SQLite that is a separate file, so platform-generated growth can
never again pollute the business DB. 'transient' deliberately stays on the
primary: those objects are user-session data and some (better-auth's
sys_device_code) are accessed outside the engine — splitting their storage
would split their brain.

Archiver (§3.3): audit-class objects with 'archive' declared now get the
full retain → archive → delete flow instead of the P1 retain-only skip:
- copies rows past archive.after to the archive datasource in bounded
  batches (500 × 20/sweep — a backlog drains across sweeps), as per-row
  idempotent upserts so an interrupted sweep re-converges
- hot-deletes exactly the copied ids (bulkDelete), then the reclaim pass
  vacuums the hot store
- archive.keep prunes the cold store itself
- the cold schema is mirrored via idempotent syncSchema
- no archive datasource registered ⇒ rows retained, object reported
  'archive-pending' — a compliance ledger is never dropped unarchived

Proof: engine-lifecycle-datasource.test.ts (routing incl. explicit-datasource
precedence), Archiver unit tests (copy/delete id-exactness, keep-pruning,
missing-datasource retention), and a dogfood case that provisions a real
second SQL store mid-run and watches the ledger drain hot → cold.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BNBzMWmSECrbiEDdVzwBt3
…ant retention overrides (#2786)

Adds the operations layer over the P1-P3 enforcement machinery, resolved
from a new 'lifecycle' settings namespace (registered by ObjectQLPlugin at
kernel:ready when a SettingsService is present; absent settings = declared
policies apply unmodified):

- enabled — runtime master switch (on top of OS_LIFECYCLE_DISABLED)
- retention_overrides — per-object window overrides { object: { maxAge,
  expireAfter } }. TENANT-scoped: the sweep enumerates organizations, and a
  tenant whose override is genuinely tenant-scoped (not inherited) gets its
  own cutoff on its own rows, while the global pass covers everyone else
  INCLUDING NULL-org rows (a bare $nin would silently skip them). This is
  ADR-0057 §3.2 verbatim: regulated tenants set years, dev sets days — the
  same knob at different settings. An unparseable override keeps the
  declared window (never fails open into unbounded growth).
- quotas / quota_defaults — per-object and per-class row ceilings
- growth_alert_rows — per-sweep growth spike threshold

Quota and growth breaches are OBSERVE-AND-ALERT only (report.alerts +
onAlert sink, default logger warning) — governance never deletes beyond the
declared policy; an operator decides.

Every settings read is best-effort and snapshotted once per sweep; the
tenant scan is capped (200) and skipped entirely on single-tenant kernels.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BNBzMWmSECrbiEDdVzwBt3
… lifecycle release (#2786)

- ADR-0057 header + §4 rollout table now record P1–P4 as implemented in
  #2791; §3.3 gains the archive-safety hard rule and §3.6 the as-implemented
  'telemetry' datasource routing note (transient exclusion rationale).
- Changeset: minor bumps across spec / objectql / driver-sql /
  driver-sqlite-wasm / platform-objects / metadata-core / service-messaging /
  service-automation / plugin-audit, documenting the bounded-by-default
  behavior change and the override knobs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BNBzMWmSECrbiEDdVzwBt3
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 8 package(s): @objectstack/dogfood, @objectstack/metadata-core, @objectstack/objectql, @objectstack/platform-objects, @objectstack/driver-sql, @objectstack/plugin-audit, packages/services, @objectstack/spec.

104 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/agents.mdx (via @objectstack/spec)
  • content/docs/ai/skills-reference.mdx (via @objectstack/spec)
  • content/docs/ai/skills.mdx (via @objectstack/spec)
  • content/docs/api/client-sdk.mdx (via @objectstack/spec)
  • content/docs/api/environment-routing.mdx (via @objectstack/spec)
  • content/docs/api/error-catalog.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-client.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx (via @objectstack/spec)
  • content/docs/api/index.mdx (via @objectstack/spec)
  • content/docs/automation/approvals.mdx (via packages/spec)
  • content/docs/automation/flows.mdx (via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx (via packages/spec)
  • content/docs/automation/hooks.mdx (via @objectstack/spec)
  • content/docs/automation/index.mdx (via @objectstack/spec)
  • content/docs/automation/webhooks.mdx (via packages/services, @objectstack/spec)
  • content/docs/automation/workflows.mdx (via @objectstack/spec)
  • content/docs/concepts/architecture.mdx (via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx (via packages/spec)
  • content/docs/concepts/index.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx (via @objectstack/metadata-core, @objectstack/objectql, packages/spec)
  • content/docs/concepts/north-star.mdx (via packages/spec)
  • content/docs/data-modeling/analytics.mdx (via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx (via @objectstack/driver-sql, @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx (via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx (via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx (via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx (via packages/objectql, @objectstack/spec)
  • content/docs/data-modeling/index.mdx (via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx (via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx (via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx (via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx (via @objectstack/spec)
  • content/docs/deployment/migration-from-objectql.mdx (via @objectstack/objectql)
  • content/docs/deployment/production-readiness.mdx (via @objectstack/plugin-audit)
  • content/docs/deployment/troubleshooting.mdx (via @objectstack/spec)
  • content/docs/deployment/vercel.mdx (via @objectstack/objectql)
  • content/docs/getting-started/build-with-claude-code.mdx (via @objectstack/spec)
  • content/docs/getting-started/cli.mdx (via @objectstack/plugin-audit, @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx (via @objectstack/spec)
  • content/docs/getting-started/examples.mdx (via @objectstack/spec)
  • content/docs/getting-started/glossary.mdx (via @objectstack/driver-sql)
  • content/docs/getting-started/quick-reference.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx (via @objectstack/spec)
  • content/docs/getting-started/validating-metadata.mdx (via @objectstack/spec)
  • content/docs/kernel/cluster.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx (via packages/spec)
  • content/docs/kernel/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/audit-service.mdx (via packages/services)
  • content/docs/kernel/runtime-services/email-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/index.mdx (via packages/services, packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/settings-service.mdx (via packages/services)
  • content/docs/kernel/runtime-services/sharing-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx (via packages/spec)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/objectql, @objectstack/spec)
  • content/docs/kernel/services.mdx (via @objectstack/objectql)
  • content/docs/permissions/authentication.mdx (via @objectstack/objectql)
  • content/docs/permissions/authorization.mdx (via packages/dogfood, @objectstack/spec)
  • content/docs/permissions/delegated-administration.mdx (via packages/dogfood)
  • content/docs/permissions/permission-sets.mdx (via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx (via @objectstack/spec)
  • content/docs/permissions/positions.mdx (via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx (via @objectstack/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx (via @objectstack/spec)
  • content/docs/plugins/anatomy.mdx (via @objectstack/driver-sql)
  • content/docs/plugins/development.mdx (via @objectstack/spec)
  • content/docs/plugins/index.mdx (via @objectstack/objectql, @objectstack/spec)
  • content/docs/plugins/packages.mdx (via @objectstack/objectql, @objectstack/platform-objects, @objectstack/driver-sql, @objectstack/plugin-audit, packages/services, @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx (via @objectstack/spec)
  • content/docs/protocol/diagram.mdx (via packages/spec)
  • content/docs/protocol/knowledge.mdx (via @objectstack/spec)
  • content/docs/protocol/objectos/config-resolution.mdx (via @objectstack/spec)
  • content/docs/protocol/objectos/i18n-standard.mdx (via packages/services, @objectstack/spec)
  • content/docs/protocol/objectos/index.mdx (via @objectstack/objectql, @objectstack/driver-sql)
  • content/docs/protocol/objectos/lifecycle.mdx (via @objectstack/driver-sql, @objectstack/spec)
  • content/docs/protocol/objectos/plugin-spec.mdx (via @objectstack/spec)
  • content/docs/protocol/objectos/runtime-capabilities.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx (via packages/spec)
  • content/docs/protocol/objectql/query-syntax.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx (via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/objectql, @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx (via packages/spec)
  • content/docs/protocol/objectui/record-alert.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx (via @objectstack/spec)
  • content/docs/releases/implementation-status.mdx (via @objectstack/objectql, @objectstack/driver-sql, @objectstack/plugin-audit, @objectstack/spec)
  • content/docs/releases/index.mdx (via @objectstack/spec)
  • content/docs/releases/v9.mdx (via @objectstack/objectql, @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx (via @objectstack/spec)
  • content/docs/ui/dashboards.mdx (via @objectstack/spec)
  • content/docs/ui/forms.mdx (via @objectstack/spec)
  • content/docs/ui/index.mdx (via @objectstack/spec)
  • content/docs/ui/setup-app.mdx (via @objectstack/platform-objects, @objectstack/spec)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

@os-zhuang os-zhuang changed the title docs(adr): ADR-0057 header/rollout sync — P0 shipped (#2079), P1–P4 tracked in #2786 feat(lifecycle): ADR-0057 P1–P4 — lifecycle contract, Reaper/Rotator/Archiver, datasource separation, governance (#2786) Jul 10, 2026
…le exports

The 5 additions (LifecycleSchema / LifecycleClassSchema / Lifecycle /
LifecycleClass / LIFECYCLE_DURATION_REGEX) are the intentional P1 spec
surface; the api-surface gate flagged them as uncommitted snapshot drift.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BNBzMWmSECrbiEDdVzwBt3
@os-zhuang
os-zhuang marked this pull request as ready for review July 11, 2026 01:31
@os-zhuang
os-zhuang merged commit 7953832 into main Jul 11, 2026
17 checks passed
@os-zhuang
os-zhuang deleted the claude/adr-0057-data-lifecycle-azuf45 branch July 11, 2026 01:32
os-zhuang pushed a commit that referenced this pull request Jul 11, 2026
…gistry conflicts

Both this branch and the merged ADR-0057 lifecycle work (#2791) appended a
HIGH_RISK_CLASSES entry and extended the BOUND_PROOF_PATHS expectation:
kept BOTH ('delegation' → position/delegatable, 'data-lifecycle' →
object/lifecycle). proof-registry tests and the spec-liveness gate pass on
the merged tree.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BNBzMWmSECrbiEDdVzwBt3
os-zhuang added a commit that referenced this pull request Jul 11, 2026
…ow-up) (#2818)

The lifecycle contract shipped in #2791 with no hand-written docs or skill
coverage — anyone modeling an append-only object through the docs or the
objectstack-data skill would still build the unbounded-growth bug the ADR
exists to prevent. Four gaps closed:

- data-modeling/objects.mdx: "Data Lifecycle (Retention & Rotation)" under
  Enterprise Features — classes, three valid policy examples (each verified
  against the real LifecycleSchema parse), enforcement rules, archive
  safety, telemetry-datasource separation, and the governance knobs.
- kernel/services.mdx: `lifecycle` row in the Standard Services table.
- data-modeling/drivers.mdx: SQLite space reclamation (auto_vacuum +
  reclaimSpace) and physical table rotation; PG/MySQL fallback semantics.
- deployment/production-readiness.mdx: go-live checklist item — review the
  default retention windows / tenant overrides / archive datasource before
  launch.
- skills/objectstack-data: new rules/lifecycle.md (decision tree,
  correct/incorrect examples, ops table) + SKILL.md property-table row and
  Quick Reference link, explicitly disambiguated from lifecycle *hooks*.

Docs/skills only — no runtime change, no changeset (skip-changeset).


Claude-Session: https://claude.ai/code/session_01BNBzMWmSECrbiEDdVzwBt3

Co-authored-by: Claude <noreply@anthropic.com>
os-zhuang added a commit that referenced this pull request Jul 11, 2026
…v telemetry datasource + db:clean, Studio lifecycle form (#2835)

Implements #2834 ①–③ (④ PG-partition rotation design-sketched on the issue, deferred until CI has a PostgreSQL service).

① JobRunRetention / NotificationRetention and their retentionDays/retentionSweepMs options removed — the LifecycleService is the one sweeper (windows tuned via the lifecycle settings namespace). Includes a fix: the blanket 30d lifecycle retention on sys_automation_run (from #2791) could strand suspended approval runs; removed — bounding stays with the automation store's terminal-only sweep.
② objectstack dev provisions a dedicated telemetry datasource (<primary>.telemetry.db; OS_TELEMETRY_DB to opt out/override) and new `os db clean` runs the one-time VACUUM legacy files need to adopt auto_vacuum=INCREMENTAL.
③ object.form.ts exposes the lifecycle block; metadata-forms i18n bundles regenerated with curated zh-CN.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation protocol:data size/xl tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ADR-0057 data lifecycle P1–P4: retention contract, rotation, separation, governance (tracking)

2 participants