Repository navigation
feat(objectql): the lifecycle sweep keeps a last-swept watermark in sys_lifecycle_state, so a reboot skips a sweep the interval has not come due for - #30751
Conversation
…ys_lifecycle_state A kernel that reboots onto an existing datastore reads one row at the initial delay and skips a first sweep the interval has not come due for. The watermark lives in a new engine-owned, tenant-less object, sys_lifecycle_state, declared and registered by platform-objects, with its roster line, census row and ownership-inventory row in this same commit because the roster test holds both directions. Carries the released branch claude/project-thread-0jjbbb@4a9c5296 (session_01Utp6CaGhTo68T8rcfBh8Zv) onto current main as one commit, and reattaches the docblock of firstSweepDueAtInitialDelay to its method. Claude-Session: https://claude.ai/code/session_016WV4cxAY2io7s5aVJa6YL4 Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016WV4cxAY2io7s5aVJa6YL4 Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 5 package(s): 80 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 18 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 182 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin b59a30c2874734fc9b1c4e536b0a1348a3d70e10 && git checkout b59a30c2874734fc9b1c4e536b0a1348a3d70e10
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin b3098661cd3ad78634dd77a31ae68530ab9a3a83 29c59f4d04e02b2953856d14c1bb46668715f371 && git checkout -B drift-repro b3098661cd3ad78634dd77a31ae68530ab9a3a83 && git merge --no-ff 29c59f4d04e02b2953856d14c1bb46668715f371
node scripts/docs-audit/affected-docs.mjs --json b3098661cd3ad78634dd77a31ae68530ab9a3a83
|
|
domain:server#1 · os-elon-musk · session_016WV4cytLmJxfAmgPVjUbbW Contract-review-claim: session_016WV4cytLmJxfAmgPVjUbbW · head f9f4c1b · 2026-11-06T00:41Z |
|
domain:server#1 · os-elon-musk · session_016WV4cytLmJxfAmgPVjUbbW Contract reviewServed-tier: Governing text: ruling B 6663872259 (confirmed 6670551310); spec consent 6669304514 / 6670346930; claim 6672243354 + amendment 6672382820; dev report 6673429255. Read-only shape held: diff at the head, both cards and every comment, the head's check-runs. PR ① Derived judgments
② Semver level
③ Boundary flags
Author note: PR ④ Design judgment
Should-fix (non-blocking, for the seat's ACCEPT note or a follow-up): (a) Implemented-by: VERDICT: PASS |
… audit ledger Every completed sweep wrote its sys_lifecycle_state row through the generic audit writers, so each sweep fed sys_audit_log and sys_activity a row the next sweep archives. The archive integration tests of os migrate audit-key-material / audit-mail-bodies counted it as a second archived row. Claude-Session: https://claude.ai/code/session_016WV4cxAY2io7s5aVJa6YL4 Co-authored-by: Claude <noreply@anthropic.com>
|
domain:server#1 · os-elon-musk · session_016WV4cytLmJxfAmgPVjUbbW Contract reviewServed-tier: Fresh record for the new head. The prior PASS 6673587300 named The delta. One commit on top of ① Derived judgments
② Semver level
③ Boundary flags
④ Design judgment
Check-runs on this head (API, at review time): 64 runs, 21 Should-fix (non-blocking): (a) the PR-body prose named in ③; (b) baseline should-fixes (a) and (b) from 6673587300 remain open, prose only. Implemented-by: VERDICT: PASS |
|
domain:server#1 · os-elon-musk · session_016WV4cytLmJxfAmgPVjUbbW 席位核对 · ACCEPT · 2026-11-06T02:18Z · #29918 (and #30641)Head-sha: 29c59f4
|
Requested by Elon · project thread
domain:server#1 · os-elon-musk · session_016WV4cxAY2io7s5aVJa6YL4
Fixes #29918
Fixes #30641
Clause-②: yes (widening) —
PLATFORM_OBJECTS_BY_PACKAGE['platform-objects'](and with itPLATFORM_PROVIDED_OBJECT_NAMES/isPlatformProvidedObjectName) lists one more real platform object,sys_lifecycle_state, and the exportedLifecycleEngineLikegains three optional members (findOne,insert,update); nothing that was accepted is refused.Metadata-protocol: added
Skills-docs: none —
skills/**andcontent/docs/**enumerate no platform-object roster and teach nothing about the lifecycle sweep's schedule; the object is internal clock state with no data API, so there is nothing for an author to write against it.Claim: 6672243354 (amended 6672382820) · Branch:
claude/project-thread-ie3sda· Ruling-ref: 6663872259 (letter B), confirmed 6670551310 · Claim correction: 6670586478 · Spec consent: 6669304514, 6670346930 · Resumes: release 6671108523 (claude/project-thread-0jjbbb@4a9c5296)维护者速读
sys_lifecycle_state(引擎自有、无租户列、每个数据库一行)。内核启动 60 秒时读这一行:距上次扫完不到一小时就不扫,只花这一条读;超过一小时、没有这一行、读失败,照旧扫。全新建库那一轮仍按 fix(objectql): defer the first lifecycle sweep one interval on a datastore this boot created #30214 延后一个间隔,并顺手写下这一行。spec 名册加一行sys_lifecycle_state,与对象同一提交。packages/objectql/src/lifecycle/lifecycle-service.ts(runFirstScheduledSweep、watermarkIsYoung、writeWatermark,以及sweep()末尾只在完整成功时推进水位线),packages/platform-objects/src/system/sys-lifecycle-state.object.ts,packages/spec/src/system/constants/platform-object-names.ts那一行。pnpm --filter @objectstack/objectql exec vitest run src/lifecycle/lifecycle-service.test.ts -t "sweep watermark",预期 9 条全绿,其中「younger than the interval」断言初始延迟时只发出一次读。What changes
@objectstack/objectql—LifecycleService(lifecycle/lifecycle-service.ts)LIFECYCLE_STATE_OBJECT = 'sys_lifecycle_state'andLIFECYCLE_STATE_ROW_ID = 'lifecycle'.LifecycleEngineLikegains optionalfindOne/insert/update. An engine missing any of the three, or whose registry does not registersys_lifecycle_state, keeps no watermark and sweeps exactly as before; the object is never read when unregistered.runFirstScheduledSweep):wasDatastoreCreatedFromEmpty) still defers one interval, and now seeds the row at now without reading it;findOneof the row: alast_swept_atyounger than the interval ⇒ no sweep, the read is the only statement; the interval is armed either way;warn) ⇒ sweep at the initial delay, as before.sweep()advances the row (insert the first time, update after; the row's presence is remembered so no second read) only for a COMPLETED sweep: no policy error, the reference-audit leg ran (or was never meant to), and no teardown cut it short. Stamped with the sweep's start, which the cutoffs were taken from. A failed write is a functional degradation (the next boot sweeps again), logged atwarnonce anddebugafter, never thrown.sweep()call always sweeps.OS_LIFECYCLE_DISABLED=1still arms nothing.@objectstack/platform-objects— newsys_lifecycle_state(system/sys-lifecycle-state.object.ts):isSystem,managedBy: 'engine-owned',systemFields: { tenant: false }(ADR-0131 D7 deployment-level state),requiredPermissions: ['manage_platform_settings'],apiEnabled: false, nolifecycleblock (never swept), fieldsid,last_swept_at,created_at,updated_at. Registered byPlatformObjectsPluginbesidesys_migrationand exported fromsystem/index.ts.@objectstack/spec— the one roster line'sys_lifecycle_state'inPLATFORM_OBJECTS_BY_PACKAGE['platform-objects'](sorted betweensys_jwksandsys_member), in the same commit as the object, perdomain:spec#3's consent (6669304514, 6670346930): the roster test holds the roster to the declared*.object.tsset both ways, so #30641 cannot land alone. Nothing else underpackages/specmoves.@objectstack/cli—ORGANIZATION_OWNERSHIP_INVENTORYgains thesys_lifecycle_staterow (deployment-level,column-drop).scripts/platform-object-tenancy-census.json— the new row (reach: out,systemFields.tenant: false) and the totals (registered 94, out of reach 47).Changeset —
minorfor spec / objectql / platform-objects / cli / plugin-audit, carrying both declaration lines and theSkills-docs:line.Tests
packages/objectql/src/lifecycle/lifecycle-service.test.ts, new describe "LifecycleService sweep watermark (objectql: the lifecycle sweep runs 60 s after every kernel boot, even on a database created seconds earlier, so a fresh tenant kernel pays 130 statements in its first minute #29918)", 9 pins: a younger watermark issues only the read; an interval-old watermark sweeps and advances; no row sweeps and writes it; a created-from-empty store defers, seeds without reading, and sweeps one interval later; a failed policy or a throwing audit leaves the row unchanged; a failing read sweeps and warns; an engine without the object is never asked; a directsweep()sweeps and advances;OS_LIFECYCLE_DISABLEDreads and writes nothing.packages/platform-objects:plugin.test.ts(registration order, six infra objects, no routing triple),deployment-level-no-tenant-column.test.ts(no tenant column, platform-only reads).platform-object-names.test.ts) and CLI inventory (organization-ownership-inventory.test.ts) unchanged and green with the new rows.Acceptance notes
28a633a3) and the roster line (2de76a73) in two commits; the dispatch order requires them in one because the roster test holds both directions, so this branch squashes the released work onto currentmain(6ef7f583). The released branch is untouched.runFirstScheduledSweep's docblock had been inserted betweenfirstSweepDueAtInitialDelay's docblock and its method, leaving that method undocumented and two docblocks stacked; this branch moves the new methods above it. No code moved.Skills-docs:spelling. The release and the dispatch order spelled the valueno move; the declared grammar (pm-dispatch SKILL.md) isupdated here | follow-up owed — FILES | follow-up #N | none — REASON, so the changeset and this body saynone — REASONwith the same reason.sys_audit_logand asys_activityrow about its own clock, which the next sweep archived (CI: theaudit-key-material/audit-mail-bodiesarchive integration tests counted 2 archived rows where 1 is right). Ruling 6663872259 forbids an audit row per sweep and ADR-0057 section 3.3 forbids cleanup re-feeding the tables it drains. No write option bypasses the code-registered audit hooks and there is no object-level opt-out, sosys_lifecycle_statejoinsSKIP_OBJECTSbesidesys_job_queue;audit-writers.test.tspins zero rows for its insert and update, and the changeset gains'@objectstack/plugin-audit': minor.Verification
Rework head
29c59f4d: the rework report 6674667474 on #29918 records 96/96 derived gates andpnpm lintexit 0 (0 NOT-MEASURED), full dogfood 421 files / 3513 tests green, bothcliarchive integration tests 16/16, andplugin-audit1146/1146. The block below is the first head's.All on head
f9f4c1b6(basemain6ef7f583), after a whole-treeturbo run build(119/119, lock VERDICT command-exit 0).Targeted suites and typecheck — all exit 0
pnpm --filter @objectstack/objectql test: Test Files 556 passed (556), Tests 11078 passed (11078)pnpm --filter @objectstack/objectql typecheck: exit 0pnpm --filter @objectstack/platform-objects test: Test Files 116 passed (116), Tests 1581 passed (1581);typecheckexit 0pnpm --filter @objectstack/spec exec vitest run src/system/constants/platform-object-names.test.ts: 14 passed (14)pnpm --filter @objectstack/spec check:generated: all 14 generated artifacts up to datepnpm --filter @objectstack/cli exec vitest run --project unit src/utils/organization-ownership-inventory.test.ts: 26 passed (26);cli typecheckexit 0Ablations — both gates turn their pin red; restore proven against HEAD
Through
scripts/ablation-replace.mjs,vitest run src/lifecycle/lifecycle-service.test.ts -t "sweep watermark"(the suite imports the service from source, so no build sits between mutation and run):watermarkIsYoungthat returns false for an unparsable, future or interval-old watermark (theNumber.isFinite/intervalMscheck) replaced byreturn Boolean(0);(anchor 1 to 0, blobba3f2b02to2f1db1a3): 1 failed, 8 passed, the failure being "a reboot whose watermark is younger than the interval issues only the watermark read at the initial delay". A first attempt with the replacementreturn false;was refused by the tool as a no-op (the replacement is a substring of the anchor, count 7 to 7) and measured nothing.if (audited && report.errors.length === 0 && !this.stopped) {replaced byif (true) {(blobba3f2b02to7bd5ebd0): 1 failed, 8 passed, the failure being "a sweep that fails a policy, or whose audit throws, leaves the watermark unchanged".ba3f2b02,git diff HEADempty.Derived gates — 94 of 94 run, all exit 0, plus pnpm lint
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderived 94 families onf9f4c1b6; each ran sequentially, exit code captured before any pipe, pluspnpm lint(exit 0).--ran: "94 derived famil(ies) accounted for — 94 run, 0 NOT-MEASURED";pnpm lintlisted as outside the derivation.Full dogfood — green
pnpm --filter @objectstack/dogfood testunder the verify lock onf9f4c1b6: Test Files 421 passed, 3 skipped (424); Tests 3513 passed, 36 skipped (3549); VERDICT command-exit 0.Generated by Claude Code