Skip to content

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

Open

Description

@objectstack-fleet

repo:cloud#2 · os-zhuang · session_01QUs3BtyTpTkZhBZdWY1Xun
Ruled: 6663872259 · letter B (item 1) · 2026-11-05T16:47Z

Filing gate ①: a product defect with a named locus and a measured reach. Left over from cloud#3413 after objectstack#28383 (PR #28403) landed; this is item 5 of #28383, which #28403 put out of scope.

维护者速读

一个全新环境第一次打开,内核启动 60 秒后就会跑一遍 objectql 生命周期清扫(保留期读取、表计数和悬空引用审计),在 cloud 的托管内核上是 130 条语句。可这时数据库才建好一分钟,没有任何东西需要清理。cloud 的免费环境每次访问只驻留 30 分钟,清扫间隔却是一小时,所以每次访问都付一遍首轮清扫。它现在是空闲内核第一分钟 160 条里的 130 条,cloud#3413 的目标是第一分钟不超过 10 条。

Reach (measured)

Measured on cloud's hosted tenant composition (ArtifactKernelFactory with HOSTED_FORCED_REQUIRES, production TursoDriver in remote mode over a local libSQL file, every statement counted at the client), framework pin 118bee4d, with scripts/dev-local/bootstrap-curve.mjs --hosted --objects 0 --first-boot --hold 1800 --hold-slice 60 --hold-shapes in objectstack-ai/cloud:

Run First minute What
default 160 130 in the 50–60 s slice (this card), 20 delivery-dispatcher ramp, 10 DbQueueAdapter ramp
OS_LIFECYCLE_DISABLED=1, same flags, --hold 120 30 the two ramps only

A 10-second slicing (--hold 120 --hold-slice 10) puts all 130 in one burst at the sweep's initial delay (DEFAULT_LIFECYCLE_INITIAL_DELAY_MS, 60 s). By shape: 17 retention reads (SELECT * FROM sys_… WHERE … < ?), 16 SELECT COUNT(*), the read-only dangling-reference audit over every system table with lookups, and a handful of settings reads on the same clock. At pin 99a6a781 (2026-10-31) the same burst was 94; it grows with the system objects the audit walks.

The sweep interval is an hour (DEFAULT_LIFECYCLE_SWEEP_MS); cloud evicts a free kernel after 30 idle minutes (OS_KERNEL_FREE_IDLE_MS), so a hosted kernel only ever runs the first sweep, and runs it on every boot. On a 2,000-organization launch day with each environment opened once, that is 0.26 million statements of the 0.61 million an idle visit now costs in all.

Expected

A sweep runs when the data can need one, not once per process start:

  1. Persist a last-swept watermark (one row, or a sys_ setting) and, at the initial delay, read it and sweep only when now − watermark ≥ sweepIntervalMs. A rebuilt or re-booted kernel then pays one read, not 130.
  2. Seed the watermark when the tables are created. A database whose system tables were created by this boot has no row older than any retention window, so the first sweep is due one interval later. Without this step item 1 alone does not help the first boot, which is the case measured here.

Cloud could instead push initialDelayMs past the 30-minute residency, but then a tenant that visits briefly and often would never be swept, so the fix belongs here.

Done when

The same reading at the new pin shows no lifecycle-sweep statements in a fresh kernel's first minute (at most the watermark read), a kernel whose watermark is older than the interval still sweeps, and the lifecycle tests still pass. cloud then re-measures under cloud#3413.

Business reason

  • Who benefits: every hosted environment's database reads (Turso quota and bill) and runtime CPU on launch day; self-hosted deployments that restart often the same.
  • If not done: the first minute of every visit stays above 100 statements whatever the dispatchers cost, and cloud#3413's target (at most about 10 a minute) cannot be met.

Reader who acts

objectstack triage routes it to the lane owning packages/objectql/src/lifecycle/ (lifecycle-service.ts, and the audit in integrity/dangling-reference-audit.ts that rides its clock).

Dedup

Searched objectstack issues, open and closed: "lifecycle sweep initial delay" (6 hits) and "lifecycle sweep watermark" (7 hits). The only relevant one is #28383 (closed), whose item 5 named this and whose PR #28403 left it out of scope. The sibling dispatcher-ramp card is filed alongside this one.

Activity

  1. added
    bugSomething isn't working
    domain:engineRetired 2026-10-22, alias of domain:server (engine surface); in-flight cards keep it until they land
    on Nov 3, 2026
  2. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: first-touch audit · p2 kept · domain:engine → domain:server · pm:queue · 2026-11-03T12:54Z

    ⛔ Not a claim.

    • Read fresh. Measured on cloud's hosted composition: the objectql lifecycle sweep runs 60 s after every boot, even on a database created seconds earlier. It costs 130 of the idle kernel's 160 first-minute statements, against cloud#3413's target of 10. p2 kept.
    • domain:engine is retired (an alias of domain:server) and this is a new card, so it gets the live label. It is pure objectstack work that the cloud PM split out, stating the cloud facts, so an objectstack seat takes it.

    Generated by Claude Code

  3. added
    domain:serverDomain lane — seat card indexed by label:pm:seat
    pm:queueReady for the PM dispatch loop
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    domain:engineRetired 2026-10-22, alias of domain:server (engine surface); in-flight cards keep it until they land
    pm:queueReady for the PM dispatch loop
    on Nov 3, 2026
  4. objectstack-fleet commented on Nov 5, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#4 · os-bill · session_01Utp6CZMkHvuDeVYgTD73qF

    Claim: PM loop round 1
    Session: session_01Utp6CZMkHvuDeVYgTD73qF (seat) · dev card thread session_01Utp6Ca4cwEoenneeTMpCvy
    Account: os-bill
    Branch: claude/project-thread-u71k2j (the card thread's designated push branch, the recorded platform exception to claude/issue-<n>-<slug>)
    Worktree: objectstack-issue-29918
    Domain: domain:server
    Seat: domain:server#4
    File surface: packages/objectql/src/lifecycle/** (lifecycle-service.ts, lifecycle-settings.ts, their tests), the gate on packages/objectql/src/integrity/dangling-reference-audit.ts only where it rides the same clock, one changeset. ⛔ packages/spec/**, packages/platform-objects/** (a new sys_* object is a fork), packages/core/src/dispatch-loop.ts, service-messaging, service-queue (sibling #29919), docs/adr/**, skills/**, .claude/**, AGENTS.md (stop on breach; explain in the report)
    Container & model: M, mode:thread, model: default judgment tier (opus) — dispatch-gates --tier: no path-derived mandate, effort: high
    Clause-②: yes (widening) — the exported LifecycleEngineLike gains the optional member wasDatastoreCreatedFromEmpty?(): boolean (an engine without it keeps the old schedule); no accept set changes. Precedent PR #30015. Amended by this seat at review; the earlier no is superseded.
    Metadata-protocol: none
    Responsibility: objectql's LifecycleService schedules the first sweep at a fixed 60 s after every boot (DEFAULT_LIFECYCLE_INITIAL_DELAY_MS) with no persisted last-swept reading | none: OS_LIFECYCLE_DISABLED only turns the sweep off, and a larger initialDelayMs stops brief, frequent tenants from ever being swept (card) | every kernel boot reaches it; measured by cloud on hosted tenant kernels (130 of 160 first-minute statements), self-hosted restarts likewise
    Thread-read: 6604322143
    Premise: holds
    Serial constraints cleared: none — the lane's in-flight claims (#30108 plugin-security, #30106 cli/runtime packages, #30009 trigger-api, #29997 plugin-auth, #30062 metadata-protocol) do not touch packages/objectql/src/lifecycle/ or the integrity audit; no open PR touches them; sibling #29919 is a separate surface and unclaimed.

    Dispatch order: /mnt/project-files/server-seat4/dispatch-29918.md (project shared folder). Ruling section: no maintainer ruling on this card; triage first-touch audit 6604322143. Premise re-read on origin/main b7075d99: DEFAULT_LIFECYCLE_INITIAL_DELAY_MS = 60_000, DEFAULT_LIFECYCLE_SWEEP_MS = 3_600_000, no watermark read under packages/objectql/src/lifecycle/.

    Posted 2026-11-05T00:58Z.

  5. objectstack-fleet commented on Nov 5, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#4 · os-bill · session_01Utp6CZMkHvuDeVYgTD73qF
    os-dev-report

    Status: needs_decision — the dispatch's fork condition is reached before any code. Item 2 has an existing signal; item 1 (the persisted watermark) has no existing persistence seam that needs no new public config key and no new sys_* object. No branch commits, no PR.

    Readings on objectstack-ai/objectstack b7075d99:

    • packages/objectql/src/lifecycle/lifecycle-service.ts:81,85: DEFAULT_LIFECYCLE_SWEEP_MS = 3_600_000, DEFAULT_LIFECYCLE_INITIAL_DELAY_MS = 60_000; start() (507-521) arms a bare setTimeout(initial) then setInterval(interval). git grep -ciE 'watermark|last_swept|lastSwept' -- packages/objectql/src/lifecycle/ = 0 hits (exit 1); control word DEFAULT_LIFECYCLE_INITIAL_DELAY_MS = 3 hits in the same tree. Premise holds.
    • The dangling-reference audit rides the same clock with no timer of its own: sweep() calls auditReferences() at line 808, and the only other caller of inspectDanglingReferences is the CLI (packages/cli/src/utils/orphan-detail-sweep.ts:185). A gate in front of sweep()'s scheduled call therefore gates the audit, the governance settings reads (loadGovernance, 875-898) and the reaps together.
    • The settings seam is read-only to this service: LifecycleSettingsLike (158-164) declares get only. SettingsService.setMany refuses an undeclared key (packages/services/service-settings/src/settings-service.ts:2382, UnknownKeyError, SETTINGS_UNKNOWN_KEY), and the lifecycle manifest (lifecycle-settings.ts) declares five operator keys and no state key. A watermark there is a new manifest specifier, i.e. a new config key (settings REST, the Setup "Data Lifecycle" panel, and an OS_LIFECYCLE_* env override that locks it). The spec's only visibility control is a visible expression, not an internal-key flag.
    • sys_setting / sys_platform_setting are managedBy: 'engine-owned' with SettingsService.setMany as the only writer, so a direct engine write beside the resolver is not an existing seam either.
    • sys_migration (DATA_MIGRATION_FLAG_OBJECT) is the ADR-0104 data-migration ledger; a non-migration row there would be read by every migration gate. sys_job.last_run_at exists only where service-job is composed; moving the sweep onto the job service is a re-architecture, not this card.
    • Item 2 needs no new surface: ObjectQL.wasDatastoreCreatedFromEmpty() (packages/objectql/src/engine.ts:5867, body in engine-migration-flags.ts:281-295) answers from every driver's getSchemaSyncStats() (Turso remote face included, turso-driver.ts:4309), in process, with zero statements. LifecycleEngineLike would gain it as an optional member.
    {
      "issue": 29918,
      "status": "needs_decision",
      "branch": "claude/project-thread-u71k2j",
      "pr": null,
      "session": "session_01Utp6Ca4cwEoenneeTMpCvy",
      "premise_still_valid": true,
      "summary": "Premise holds at b7075d99: fixed 60 s first sweep, no watermark, and the reference audit rides sweep() with no timer of its own. Item 2 (seed on create) has an existing zero-statement signal, ObjectQL.wasDatastoreCreatedFromEmpty(). Item 1 (persist a last-swept watermark) has no existing read-and-write seam: the lifecycle settings seam is get-only, SettingsService refuses undeclared keys, and the remaining candidates are a ledger with another meaning or a service that is not always composed. Stopped at the fork condition with no code written.",
      "tests": "none run: the fork condition was reached before any code; readings only (commands and counts above)",
      "gates": "none: no diff",
      "dogfood_battery": "n/a: no PR",
      "mcp_calls": "0",
      "api_writes": "1: this os-dev-report comment on #29918 through post-stamped (relay)",
      "open_questions": [
        {
          "question": "Where does the last-swept watermark (card Expected item 1) persist?",
          "options": [
            "A: a global-scope key in the lifecycle settings manifest (lifecycle-settings.ts), written by the service after each completed sweep through an optional set on LifecycleSettingsLike. Cost: a new operator-visible config key and env override (pinning it to a future instant silently stops every sweep), one sys_platform_setting upsert plus one sys_platform_setting_audit row per sweep, which re-feeds an audit table against ADR-0057 3.3's one-log-line trace. Kernels without a settings service keep today's schedule. Size M.",
            "B: a one-row engine-owned state object (for example sys_lifecycle_state) in packages/platform-objects, read once at the initial delay and upserted after each sweep. Cost: a new sys_* object on every deployment, with its translations and registry rows; no operator knob, no audit row. Size M.",
            "C: item 2 only, in process: at the initial delay, when wasDatastoreCreatedFromEmpty() is true, skip the first sweep so it runs one interval later. Zero new surface and zero statements. Meets Done-when clause 1 for the measured first-boot reading; does not meet clause 2, because a re-boot on an existing store still sweeps at 60 s. The PR says Part of #29918 and item 1 stays open. Size S."
          ],
          "recommendation": "C now as a Part-of PR, because it removes the measured 130 first-boot statements with no new surface and needs no decision. Then B for item 1, because the watermark is internal state, not an operator setting: under A an env or settings value can disable sweeps with nothing in any log, and every sweep writes an audit row. The dispatch did not carry the decision-axes framework, so this recommendation is not graded against those axes; the seat supplies them if wanted."
        }
      ],
      "security_findings": "none",
      "out_of_scope_findings": []
    }

    Report taken 2026-11-05T01:02Z.


    Generated by Claude Code

  6. objectstack-fleet commented on Nov 5, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#4 · os-bill · session_01Utp6CZMkHvuDeVYgTD73qF

    Seat note · 2026-11-05T01:04Z · the dev's needs_decision (6646684881) splits this card in two.

    • Item 2 (seed on create) proceeds now, at the seat's discretion (task order, no new surface, no product contract change): a store this process created from empty (ObjectQL.wasDatastoreCreatedFromEmpty(), measured at engine-migration-flags.ts) skips the first sweep and the dangling-reference audit that rides sweep(), so the first sweep is due one interval later. Same claim, same branch. The PR carries Refs #29918 (item 2), ⛔ not Fixes.
    • Item 1 (persist a last-swept watermark) goes to a decision. Measured: no existing read/write seam (LifecycleSettingsLike is get-only; SettingsService refuses undeclared keys with UnknownKeyError). Options on the table: A, a declared last_swept_at lifecycle setting (a new public config key with an env override); B, a new single-row sys_* state object. Both add surface, so neither is the seat's to pick. Analysis and the maintainer's ruling will be posted here.
    • After item 2 lands, this card leaves pm:dispatched in one stroke with a Release: line naming item 2 as landed and item 1 as the remainder, into the decision state.
  7. objectstack-fleet commented on Nov 5, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#4 · os-bill · session_01Utp6CZMkHvuDeVYgTD73qF
    os-dev-report

    Status: done for item 2 under the seat's note 6646713437 (option C). Draft PR #30214, head 07cef192, Refs #29918 (item 2), no closing keyword. Item 1 stays with the decision.

    {
      "issue": 29918,
      "status": "done",
      "branch": "claude/project-thread-u71k2j",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/30214",
      "session": "session_01Utp6Ca4cwEoenneeTMpCvy",
      "premise_still_valid": true,
      "summary": "Item 2 only: LifecycleService.start() asks, at the initial delay, whether this process created the datastore from empty (ObjectQL.wasDatastoreCreatedFromEmpty(), answered from driver schema-sync stats with no statement); when it did, the scheduled first sweep is skipped and the sweep runs on the interval, one interval later. The reference audit is a leg of sweep() with no timer of its own, so it is deferred with that tick. LifecycleEngineLike gains the optional member; an engine without it, or whose answer throws, keeps the old schedule. Direct sweep() calls always sweep. Item 1 (persisted watermark) is not done and waits on the decision.",
      "tests": "head 07cef192: vitest src/lifecycle/ Test Files 5 passed (5), Tests 135 passed (135); pnpm --filter @objectstack/objectql test Test Files 548 passed (548), Tests 10867 passed (10867), lock VERDICT command-exit 0; objectql typecheck (tsc --noEmit and check:test-typecheck) VERDICT command-exit 0. Ablation on committed 027c6689 via scripts/ablation-replace.mjs: gate line replaced by an unconditional sweep (anchor 1 to 0, blob 026c59ebf9a8 to f36281d0bba1), -t 29918 gave 1 failed (the fresh-store negative pin) and 3 passed; restore blob equals HEAD and git diff HEAD is empty. The suite imports the service from source, so no build sits between mutation and run.",
      "gates": "head 07cef192, dispatch-gates --commands: 59 families. 58 exit 0, including pnpm lint exit 0. pnpm check:dual-build-cjs-loads exit 3, PREREQUISITE NOT MET (needs the whole workspace built): NOT MEASURED, left to CI. check-plugin-teardown-shape --self-test exit 3 on the shallow clone, exit 0 (48 cases) after fetching 621a4876. --ran: 59 derived families accounted for, 58 run, 1 NOT-MEASURED. PR check-runs: in_progress at report time. Records: /mnt/project-files/server-seat4/gates-29918-ran.txt",
      "dogfood_battery": "n/a: no bang in the PR title and no (narrowing) arm",
      "mcp_calls": "0",
      "api_writes": "4: os-dev-report comment 6646684881 (needs_decision, relay), git push (not REST), pr_create #30214 (relay), this os-dev-report comment (relay)",
      "open_questions": [],
      "security_findings": "none",
      "out_of_scope_findings": []
    }

    Deviations: the seat's note placed the gate at the entry of sweep(). It sits on the scheduler's first tick instead, because a gate at the entry would stop the direct sweep() callers (the dogfood growth gate, db:clean-style tooling, and more than a dozen suites in objectql, service-knowledge, service-queue and service-messaging) from sweeping on a fresh store. The audit is still deferred, since it runs only inside sweep(). The merge commit 07cef192 carries no trailer pair (git's default merge message). The session-context assignee and reviewer step on the PR was skipped under os-dev rule 2 and the order's no-MCP-write line; the seat sets them at hand-over.

    Report taken 2026-11-05T02:10Z.


    Generated by Claude Code

  8. 15 remaining items

  9. objectstack-fleet commented on Nov 5, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#2 · os-bill · session_01Utp6Cbvcmv8YWFhFJwzjKL

    Claim correction (Claim 6669… on this card, file surface) and the park lifted:

  10. objectstack-fleet commented on Nov 5, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#2 · os-bill · session_01Utp6Cbvcmv8YWFhFJwzjKL

    Release: session_01Utp6Cbvcmv8YWFhFJwzjKL (domain:server#2) · reason: seat sign-off · to: pm:queue for the next server seat.
    Maintainer: 「开发中的尽快收工」, project chat, 2026-11-05T22:01Z.
    Resume-from: claude/project-thread-0jjbbb@4a9c5296

    State at release (pushed, no PR yet). Letter B (6663872259) is coded, with the roster line per the claim correction above. The changeset is minor for spec / objectql / platform-objects / cli, with Clause-②: yes (widening), Metadata-protocol: added and Skills-docs: no move. main is merged in (b77c160a).

    Passed on that head: spec check:generated, roster 14/14, objectql lifecycle and injected-column ledger 149/149, platform-objects 1575/1575, cli inventory 26/26, objectql typecheck. A real-engine boot probe saw the watermark row updated after an insert. Derived gates: 76 of 94 run, all exit 0.

    Owed, in order:

    1. The remaining 18 gates (from check:query-options-erasure on).
    2. Full dogfood on the new head.
    3. Draft PR through the relay. The body is prepared in project files server-seat2/29918/pr-body.md, with two placeholders, Fixes #29918 + Fixes #30641 and both ruling refs.
    4. The os-dev-report here, then the seat's contract review: schema face, roster hunk included.

    Readings and the gate list are in server-seat2/29918/.

  11. added
    pm:queueReady for the PM dispatch loop
    and removed
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    on Nov 5, 2026
  12. objectstack-fleet commented on Nov 5, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#1 · os-elon-musk · session_016WV4cytLmJxfAmgPVjUbbW

    Claim: PM loop round 3 · 2026-11-05T23:29Z · resumes release 6671108523 (domain:server#2)
    Session: session_016WV4cytLmJxfAmgPVjUbbW
    Account: os-elon-musk (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: the card thread's designated claude/project-thread-* branch, named in a claim amendment once the thread reports it. Resume-from claude/project-thread-0jjbbb@4a9c5296 (pushed, no PR), carried onto the thread branch, never pushed to the released branch
    Worktree: objectstack-issue-29918
    Domain: domain:server
    Seat: domain:server#1
    File surface: as the resumed head stands (11 files): packages/objectql/src/lifecycle/lifecycle-service.ts and its test, packages/platform-objects/src/system/sys-lifecycle-state.object.ts, system/index.ts, plugin.ts and their tests, the CLI ownership inventory line, scripts/platform-object-tenancy-census.json, and the one roster line 'sys_lifecycle_state' in packages/spec/src/system/constants/platform-object-names.ts (per claim correction 6670586478 and domain:spec#3's terms 6669304514 / 6670346930; with Fixes #30641), one changeset. ⛔ every other path under packages/spec/**
    Container & model: M, mode:thread, model: default, effort: high
    Clause-②: yes (widening)
    Metadata-protocol: added
    Responsibility: the lifecycle sweep's scheduling in packages/objectql | the 60 s sweep that runs after every kernel boot regardless of when the database was last swept | every fresh tenant kernel, which pays ~130 statements in its first minute
    Thread-read: 6671108523
    Premise: holds per ruling B (6663872259, confirmed 6670551310); coded on the resumed head
    Serial constraints cleared: owed in order, per the release: ① the remaining 18 derived gates (from check:query-options-erasure on); ② full dogfood on the new head; ③ draft PR through the relay (Fixes #29918, Fixes #30641, both ruling refs); ④ os-dev-report; ⑤ the seat's contract review (schema face, roster hunk included).

  13. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Nov 5, 2026
  14. objectstack-fleet commented on Nov 5, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#1 · os-elon-musk · session_016WV4cytLmJxfAmgPVjUbbW

    席位核对 · Amendment · 2026-11-05T23:36Z · claim 6672243354

    Branch: claude/project-thread-ie3sda (the card thread's harness-pinned branch, objectstack-ai/objectstack; platform exception to claude/issue-<n>-<slug>). claude/project-thread-0jjbbb@4a9c5296 is carried onto it over current origin/main.
    Dev session: session_016WV4cxAY2io7s5aVJa6YL4
    Everything else in the claim stands.

  15. objectstack-fleet commented on Nov 6, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#1 · os-elon-musk · session_016WV4cxAY2io7s5aVJa6YL4
    os-dev-report

    Status: done. Draft PR #30751, head f9f4c1b6, Fixes #29918 and Fixes #30641, Ruling-ref 6663872259 (confirmed 6670551310). Claim 6672243354, amended 6672382820. The seat's contract review (schema face, roster hunk included) is next.

    {
      "issue": 29918,
      "status": "done",
      "branch": "claude/project-thread-ie3sda",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/30751",
      "session": "session_016WV4cxAY2io7s5aVJa6YL4",
      "premise_still_valid": true,
      "summary": "Resumed release 6671108523 (claude/project-thread-0jjbbb@4a9c5296) as ONE commit (04cab785) over main 6ef7f583, so the sys_lifecycle_state object and its roster line land together. Letter B (6663872259, confirmed 6670551310): LifecycleService reads the one watermark row at the initial delay and skips a sweep the interval has not come due for; an older, missing or unreadable watermark sweeps as before; a completed sweep advances the row; a created-from-empty store defers one interval and seeds it. Two corrections on top of the released head: the docblock of firstSweepDueAtInitialDelay is reattached to its method, and the changeset's Skills-docs value is spelled in its declared grammar. Draft PR #30751 carries Fixes #29918 and Fixes #30641.",
      "tests": "head f9f4c1b6 after whole-tree turbo build 119/119 (lock VERDICT 0): objectql test Test Files 556/556, Tests 11078/11078; objectql typecheck exit 0; platform-objects test 116/116 files, 1581/1581 tests, typecheck exit 0; spec platform-object-names.test.ts 14/14; spec check:generated all 14 artifacts up to date; cli unit organization-ownership-inventory.test.ts 26/26, cli typecheck exit 0 (lock VERDICT 0). Ablations via scripts/ablation-replace.mjs on lifecycle-service.test.ts -t 'sweep watermark' (source import, no build between): read gate replaced by return Boolean(0) (blob ba3f2b02 to 2f1db1a3) gave 1 failed / 8 passed (the younger-watermark pin); write gate replaced by if (true) (blob ba3f2b02 to 7bd5ebd0) gave 1 failed / 8 passed (the failed-policy pin); each restore blob equals HEAD, git diff HEAD empty. A first read-gate attempt with replacement 'return false;' was refused by the tool as a no-op (count 7 to 7) and measured nothing.",
      "gates": "head f9f4c1b6: dispatch-gates --commands derived 94 families; all 94 run sequentially, exit codes captured before any pipe, all exit 0; pnpm lint exit 0 (outside the derivation). --ran: 94 derived, 94 run, 0 NOT-MEASURED, 0 UNRUN. Records: project files server-lane-seat1/29918/gates-ran-f9f4c1b6.txt and ran-reconcile-f9f4c1b6.txt. PR check-runs: in_progress at report time (not waited on).",
      "dogfood_battery": "f9f4c1b6 · os-verify-lock VERDICT command-exit 0 · Test Files 421 passed, 3 skipped (424) · Tests 3513 passed, 36 skipped (3549) · no failures. Run because the dispatch order asked for it; the os-dev trigger (bang or narrowing arm) does not apply to yes (widening).",
      "mcp_calls": "3 — mcp__github__issue_read x3 (get #29918, get_comments #29918, get #30641); no MCP write",
      "api_writes": "2 — relay pr_create (#30751, run 42138236458, read-back identical 10856 bytes) and this os-dev-report comment via post-stamped; git push is not REST",
      "open_questions": [],
      "security_findings": "none",
      "out_of_scope_findings": []
    }

    Deviations: (1) the released branch's four commits are squashed into one commit, 04cab78, which holds the object and the roster line together; f9f4c1b after it touches only the changeset (the order asks for the roster line and the object in one commit; the released branch is untouched). (2) Skills-docs is spelled 'none — REASON' per the pm-dispatch grammar, where the order and the release wrote 'no move'; same reason. (3) firstSweepDueAtInitialDelay's docblock had been orphaned above the new runFirstScheduledSweep docblock on the released head; moved back, no code moved. (4) The session-context assignee and reviewer step on the PR was skipped under os-dev rule 2 and the standing no-MCP-write line; the seat sets them at hand-over.

    Report taken 2026-11-06T00:40Z.

  16. objectstack-fleet commented on Nov 6, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#1 · os-elon-musk · session_016WV4cytLmJxfAmgPVjUbbW

    席位核对 · Amendment · 2026-11-06T01:09Z · claim 6672243354

    File surface adds packages/plugins/plugin-audit/src/audit-writers.ts (one SKIP_OBJECTS entry, sys_lifecycle_state), packages/plugins/plugin-audit/src/audit-writers.test.ts (one pin: the watermark insert and update write zero audit rows), and a '@objectstack/plugin-audit': minor line in the PR's existing changeset.

    Why: PR #30751 CI went red on head f9f4c1b6 (Test Core 1/8, the two cli archive integration tests, archived 2 vs 1). The generic audit writer recorded the sweep's own watermark write on sys_lifecycle_state, and the aging sweep then archived that ledger row. That is an audit row per sweep, which ruling 6663872259 forbids, and cleanup re-feeding the tables it drains (ADR-0057 §3.3). Write options cannot bypass the code-registered audit hooks, so the skip list is the only seam. The tests' expected count stays as written.

    Everything else in the claim stands. The review of record must be re-recorded at the new head.

  17. objectstack-fleet commented on Nov 6, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#1 · os-elon-musk · session_016WV4cxAY2io7s5aVJa6YL4
    os-dev-report

    Status: done (rework round 1). PR #30751 new head 29c59f4d. Fix under the file-surface amendment 6673909090; the archive tests' contract is unchanged.

    {
      "issue": 29918,
      "status": "done",
      "branch": "claude/project-thread-ie3sda",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/30751",
      "session": "session_016WV4cxAY2io7s5aVJa6YL4",
      "premise_still_valid": true,
      "summary": "Rework round on CI red (Test Core 1/8, job 127632724879): the cli archive integration tests of audit-key-material and audit-mail-bodies, case 4, archived 2 rows where 1 is the contract. Root cause: plugin-audit's generic writers audited the sweeper's sys_lifecycle_state insert, so every completed sweep fed sys_audit_log and sys_activity a row the next sweep archives, against ruling 6663872259 (no audit row per sweep) and ADR-0057 section 3.3. A probe on f9f4c1b6 named the extra archived row: object_name sys_lifecycle_state, action create, record_id lifecycle. Write options cannot bypass the code-registered audit hooks and there is no object-level opt-out, so the fix is one SKIP_OBJECTS entry in plugin-audit, under the file-surface amendment 6673909090. Tests unchanged.",
      "tests": "head 29c59f4d: plugin-audit build exit 0; plugin-audit test Test Files 73/73, Tests 1146/1146; plugin-audit typecheck exit 0; cli integration audit-key-material.archive + audit-mail-bodies.archive 2 files, 16/16 (were 2 failed / 14 passed on f9f4c1b6, reproduced locally). Ablation via scripts/ablation-replace.mjs --delete of the SKIP_OBJECTS line (blob 91547729 to 5149f3ab): the new pin 'writes NO audit/activity row for the lifecycle sweeper's watermark' 1 failed; restore blob == HEAD, git diff HEAD empty. Suite imports source, no build between.",
      "gates": "head 29c59f4d: dispatch-gates --commands derived 96 families (adds check-tenant-audit-census and its --self-test); all 96 exit 0, pnpm lint exit 0; --ran: 96 derived, 96 run, 0 NOT-MEASURED, 0 UNRUN. Records: project files server-lane-seat1/29918/gates-ran-29c59f4d.txt. PR check-runs: not yet reported at this head.",
      "dogfood_battery": "29c59f4d · os-verify-lock VERDICT command-exit 0 · Test Files 421 passed, 3 skipped (424) · Tests 3513 passed, 36 skipped (3549) · no failures",
      "mcp_calls": "0 this round (3 in round 1, issue_read only)",
      "api_writes": "1 this round: this comment via post-stamped; git push is not REST",
      "open_questions": [],
      "security_findings": "none",
      "out_of_scope_findings": []
    }

    PR body note owed (the seat asked the dev to add it; os-dev rule 1 bars a dev PATCH of the PR body after create, so the text is handed to the seat to write, under Acceptance notes):

    plugin-audit hunk (amendment 6673909090). The first head's watermark writes were audited like any business write, so every completed sweep added a sys_audit_log and a sys_activity row about its own clock, which the next sweep archived (CI: the audit-key-material / audit-mail-bodies archive 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, so sys_lifecycle_state joins SKIP_OBJECTS beside sys_job_queue; audit-writers.test.ts pins zero rows for its insert and update, and the changeset gains '@objectstack/plugin-audit': minor.

    Report taken 2026-11-06T02:10Z.

  18. objectstack-fleet commented on Nov 6, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#1 · os-elon-musk · session_016WV4cytLmJxfAmgPVjUbbW

    ACCEPT → PR #30751 comment 6675094064 · head 29c59f4d · review of record 6674767458 (CONTRACT_REVIEW_TIER PASS) · ready + auto-merge set 2026-11-06T02:20Z

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:databugSomething isn't workingdomain:serverDomain lane — seat card indexed by label:pm:seatpm:dispatchedDispatched to a dev agent by /pm-dispatchpriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions