Skip to content

plugin-audit + objectql lifecycle: build the audit tamper-evidence ADR — sealer, signed checkpoints, redaction and expiry amendments, os audit verify #31908

Open
1 of 1 issue completed

Description

@objectstack-fleet

repo:cloud#3 · os-warren · session_01JQwxbZqfEd7pcckjz3pGqn

Filing gate: ③ a task on the maintainer's word. The maintainer ruled (c) on cloud#4016 at 2026-11-09 11:30Z: stage 1 is in the framework. This card builds what the ADR card filed with it decides. Blocked until that ADR merges.

Cloud consumer: objectstack-ai/cloud#4016 (Enterprise stage 2 reads these checkpoints).

维护者速读

  • 问题:同上一张 ADR 卡,审计日志被改被删时证明不了。
  • 要做:按 ADR 实现封存器、检查点对象、脱敏和到期删除的修正记录、到期删除按检查点整块删除,以及 os audit verify。
  • 需要你做的:无。ADR 合并前本卡保持阻塞。

Work

Implement the ADR in plugin-audit (canonical form, sealer, checkpoint object, amendments, verify command) and in objectql's lifecycle (prune aligned to whole checkpoints, with an expiry amendment). The design doc's §2.4 sets the cost bar: nothing on the insert path, about 20 µs per row to seal, 27 ms per 10,000-row checkpoint. A synchronous chain head cut throughput 13.8× at 8 writers per organization, so the sealer must stay asynchronous.

Done when

On a real kernel with the archive datasource, os audit verify reports:

  • a row edited through the system context as modified;
  • a deleted row as missing;
  • a redacted row as redacted, with its amendment;
  • a pruned window as expired by policy.

It reports an untouched window as unmodified, across the hot-to-archive move.

Who acts

The server lane, after the ADR merges.

Dedupe: as on the ADR card.

Related: cloud#4016, cloud PR #4025.

Activity

  1. objectstack-fleet commented on Nov 9, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: p2 · pm:on-hold · waits on the maintainer's cloud#3823 call · 2026-11-09T12:01Z

    ⛔ Not a claim.


    Generated by Claude Code

  2. added
    pm:on-holdDecision made, deliberately deferred — no dispatch, no nag; restart condition in the hold comment
    on Nov 9, 2026
  3. objectstack-fleet commented on Nov 9, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: hold lifted → pm:queue · domain:server · p2 · 2026-11-09T23:59Z

    ⛔ Not a claim.


    Generated by Claude Code

  4. added
    pm:queueReady for the PM dispatch loop
    and removed
    pm:on-holdDecision made, deliberately deferred — no dispatch, no nag; restart condition in the hold comment
    on Nov 10, 2026
  5. objectstack-fleet commented on Nov 10, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#4 · os-steve · session_01ADHkmP8JJo4zF2sjTBRwnX

    Claim: PM loop round 14 · #31908 · 2026-11-10T04:56Z
    Session: session_01ADHkmP8JJo4zF2sjTBRwnX (seat); dev = card thread session_01ADHkmR91XAgACaLbYX476P
    Account: os-steve
    Branch: claude/project-thread-vs4lnh
    Worktree: the card thread's checkout of objectstack-ai/objectstack
    Domain: domain:server
    Seat: domain:server#4
    File surface (provisional; Step 0 fixes it per slice and the seat amends this claim): packages/plugins/plugin-audit/** (canonical form, sealer, the three new objects, signer, amendment writer, the three migrations, AUDIT_EXCLUDED_OBJECTS), packages/objectql/src/lifecycle/** (the expiry seam beside registerReapGuard), packages/cli/** (os audit verify), and in packages/spec only src/data/object.zod.ts (lifecycle.seal and its authoring refusals), src/contracts/crypto-provider.ts (the audit_seal scope) and the two seam contracts, with their regenerated references and docs; root .changeset/. Never docs/adr/** (stop on breach)
    Container & model: L, mode:thread, model: default tier, effort: high
    Clause-②: yes (narrowing) (provisional: P1 widens the object schema with lifecycle.seal and refuses three lifecycle policies beside it at authoring; Step 0 re-reads each slice against scripts/pm/clause2-line.mjs and amends through the seat)
    Metadata-protocol: added (P1: the authorable lifecycle.seal key; @objectstack/spec minor)
    Responsibility: build ADR-0154 (adopted 2026-11-10, 6e766012f5) D1–D11 in four serial PRs from one thread: P1 sealing core (spec key with its reader as an ADR-0049 pair), P2 os audit verify, P3 redaction amendments, P4 expiry amendments (Fixes #31908) · today nothing can prove the audit log was not changed or deleted · read by Enterprise stage 2 (cloud#4128)
    Thread-read: none
    Premise: holds. ADR-0154 is on main; the ADR card #31907 is closed (landing note 6759357073).
    Serial constraints: P1 → P2 → P3 → P4 on this seat. New public names beyond ADR-0154's table are nominated in Step 0 and ruled by the seat before code.

  6. objectstack-fleet commented on Nov 10, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#4 · os-steve · session_01ADHkmP8JJo4zF2sjTBRwnX

    Claim amendment · #31908 · 2026-11-10T05:04Z · amends claim 6759393778 · slice plan, File surface, Clause-② and Metadata-protocol for slice P1a (Branch claude/project-thread-vs4lnh unchanged)

    Seat rulings on Step 0:

    • Five serial slices on this branch's thread, one PR each: P1a pure core, P1b lifecycle.seal with signer and sealer, P2 os audit verify, P3 redaction amendments, P4 expiry amendments. The default lifecycle.seal on sys_audit_log lands in P4, so no release between slices holds sealed rows that a later slice would read as modified or missing. P1a–P3 say Part of #31908; P4 says Fixes #31908. Each slice stays at or under 3000 lines.
    • Spec carrier: the three packages/spec additions ride on this card. ADR-0154 D2, D10 and D11 name plugin-audit + objectql lifecycle: build the audit tamper-evidence ADR — sealer, signed checkpoints, redaction and expiry amendments, os audit verify #31908 as their builder, and ADR-0049 needs the key and its reader in one PR. The protocol:data label on P1b is expected and is not a stop (precedents feat(audit): record the administrator on writes made while impersonating a user #30800, feat(audit): AI service accounts, O4 of ADR-0151 — principalKind reaches hooks and explain, so an account is attributed as itself #31740).
    • Handle id: under scope audit_seal, rotateKey MUST return the input's id. This is pinned against every provider shipped in this repository and against the rewrap writer. Out-of-tree providers are a carrier: cloud question.
    • Gaps ADR-0154 left to the build, all ruled in:
      • a fourth sys_secret reference family audit-seal, so os secret rewrap re-wraps seeds and signing keys;
      • explicit host composition through AuditPluginOptions.cryptoProvider;
      • a non-null chain_key column for the (sealed_object, chain_key, sequence) unique index; the signed body keeps ADR-0154's NULL tenant_id for the deployment chain;
      • seed and private-key sys_secret rows written below the hooks, so the sealer never seals its own writes.
    • Names:
      • the columns nominated in Step 0;
      • AUDIT_SEAL_WINDOW_MS, AUDIT_SEAL_SETTLE_MS, AUDIT_SEAL_LATE_HORIZON_MS (constants; there is no operator knob);
      • the audit-seal reference family;
      • registerExpiryPrefix (P4);
      • the bundle format objectstack.audit.proof-bundle.v1.

    This claim now covers slice P1a only. The seat amends it before each later slice.

    File surface (P1a):

    • packages/spec: src/contracts/crypto-provider.ts, a new src/contracts/audit-checkpoint.ts, the contracts barrel, and api-surface/**.
    • packages/plugins/plugin-audit:
      • src/audit-seal/** (canonical form, commitments, seed salts, Merkle tree, signed-body builder);
      • the three new object files and objects/index.ts;
      • audit-writers.ts, with the excluded-object set literal only;
      • regenerated translations.
    • packages/cli: src/utils/secret-reference-union.ts and src/utils/sys-secret-rewrap.ts.
    • packages/services/service-settings: the rotateKey id pin.
    • Root .changeset/.

    Never docs/adr/**, never packages/spec/src/data/** in this slice (stop on breach).

    Clause-②: yes (widening) (new exported contracts, a new crypto scope member and three platform objects; nothing main accepts is refused)
    Metadata-protocol: none (platform objects and contracts; no authorable key in this slice)

  7. objectstack-fleet commented on Nov 10, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#4 · os-steve · session_01ADHkmR91XAgACaLbYX476P
    os-dev-report

    {
     "issue": 31908,
     "status": "done",
     "slice": "P1a of 5 (P1a, P1b, P2, P3, P4); stops at the slice boundary per seat 4, P1b not started",
     "branch": "claude/project-thread-vs4lnh",
     "pr": "https://github.com/objectstack-ai/objectstack/pull/32100",
     "head": "335c18cb4ac760433165e35a9d675dbbe8e683c1",
     "session": "session_01ADHkmR91XAgACaLbYX476P",
     "premise_still_valid": true,
     "summary": "ADR-0154 slice P1a. spec gains the audit_seal crypto scope (with rotateKey's keep-the-id MUST under it) and the IAuditCheckpointSigner / IAuditCheckpointSink contracts. plugin-audit gains the pure core in src/audit-seal (RFC 8785 JCS, canonical row form, salted commitments, seed salts and sealing contexts, RFC 6962 Merkle tree, signed bodies of checkpoints and amendments) and three exported, NOT yet registered objects, kept out of the audit writer. cli's sys_secret reference union gains the fourth family audit-seal (enumerated-empty without evidence, gap with evidence, references with their minted pair when registered), and rewrap maps it to scope audit_seal. Nothing seals in this slice.",
     "tests": "New pins: plugin-audit audit-seal-core.test.ts 23/23; audit-writers.test.ts tamper-evidence describe (4); cli secret-reference-union.audit-seal.test.ts 7/7; sys-secret-rewrap.test.ts KEEPS THE ROW ID; service-settings local-crypto-provider.test.ts rotateKey-keeps-id-per-scope. Full suites: plugin-audit 82 files / 1293 tests passed; service-settings 75 / 1443 passed; cli src/utils+src/commands/secret+src/commands/db 149 files passed, 1 skipped / 2336 tests passed, 1 skipped. Ablations via scripts/ablation-replace.mjs, each red then restored with git diff HEAD empty: (1) date-before-datetime in canonicalValue, (2) the second salt separator (rerun on the Buffer.concat shape), (3) the canonical-spelling check in isAuditSealSecretCoordinate, (4) the three SKIP_OBJECTS entries, (5) the evidence gap in the audit-seal family, (6) the registered-holder read, (7) the rewrap next.id check, (8) LocalCryptoProvider.rotateKey keeping the id. A real bug was found by the pins before the first push: a date column handed back as a Date canonicalised as a full instant (fixed in canonicalValue).",
     "gates": "Final head 335c18cb: `dispatch-gates --commands` derived 106; all 106 run under os-verify-lock with exit codes recorded before any pipe; `dispatch-gates --ran` -> '106 derived, 106 run, 0 NOT-MEASURED, 0 UNRUN' (exit 0). Every one of the 106 exited 0. Fixed on the way (earlier heads): check-tenant-audit-census (chained Hash.update -> one update), check:keyed-text-bounds (3 keyed columns bounded), check:platform-object-tenancy-census / spec check:api-surface / check:export-origins (artefacts regenerated), check-issue-citations (dangling #N in a comment), check:test-source-alias (preloads in an edited test). Environment-only reds of round 1, green on the final head: check-plugin-teardown-shape --self-test (shallow clone, fixture commit fetched), check:dual-build-cjs-loads and check:i18n-coverage (unbuilt foreign packages; built). Root eslint per touched dir: packages/spec 0, packages/plugins/plugin-audit 0, packages/cli 0, packages/services/service-settings 0. Typecheck: spec 0, plugin-audit 0, cli 0, service-settings 0 (spec and cli test-layer halves with NODE_OPTIONS=--max-old-space-size=8192 after an OOM on the default heap). Offline PR-body check: check-changeset-no-major --event -> LEVEL AXIS yes (widening) / none at patch, exit 0. CI not awaited.",
     "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
     "mcp_calls": "7 — all read-only: mcp__github__issue_read x5, mcp__github__get_file_contents x2 (Step 0 and claim reads); no write tool",
     "api_writes": "2 so far — relay pr_create (fleet-write run 42777485419, #32100, 11544 bytes sent = stored), and this os-dev-report comment via post-stamped.mjs; the seat-4 thread message is not a GitHub write",
     "files_changed": [
      ".changeset/31908-audit-seal-core.md",
      "packages/cli/src/commands/secret/orphans.guards.test.ts",
      "packages/cli/src/commands/secret/rewrap.driver-contract.test.ts",
      "packages/cli/src/commands/secret/rewrap.guards.test.ts",
      "packages/cli/src/commands/secret/rewrap.ts",
      "packages/cli/src/utils/secret-reference-union.audit-seal.test.ts",
      "packages/cli/src/utils/secret-reference-union.test.ts",
      "packages/cli/src/utils/secret-reference-union.ts",
      "packages/cli/src/utils/sys-secret-holder-report.test.ts",
      "packages/cli/src/utils/sys-secret-orphan-sweep.test.ts",
      "packages/cli/src/utils/sys-secret-orphan-sweep.ts",
      "packages/cli/src/utils/sys-secret-rewrap.test.ts",
      "packages/cli/src/utils/sys-secret-rewrap.ts",
      "packages/plugins/plugin-audit/src/audit-seal/audit-seal-core.test.ts",
      "packages/plugins/plugin-audit/src/audit-seal/canonical.ts",
      "packages/plugins/plugin-audit/src/audit-seal/index.ts",
      "packages/plugins/plugin-audit/src/audit-seal/jcs.ts",
      "packages/plugins/plugin-audit/src/audit-seal/merkle.ts",
      "packages/plugins/plugin-audit/src/audit-seal/seed.ts",
      "packages/plugins/plugin-audit/src/audit-seal/signed-body.ts",
      "packages/plugins/plugin-audit/src/audit-writers.test.ts",
      "packages/plugins/plugin-audit/src/audit-writers.ts",
      "packages/plugins/plugin-audit/src/index.ts",
      "packages/plugins/plugin-audit/src/objects/index.ts",
      "packages/plugins/plugin-audit/src/objects/sys-audit-amendment.object.ts",
      "packages/plugins/plugin-audit/src/objects/sys-audit-checkpoint.object.ts",
      "packages/plugins/plugin-audit/src/objects/sys-audit-signing-key.object.ts",
      "packages/services/service-settings/src/local-crypto-provider.test.ts",
      "packages/spec/api-surface/contracts.json",
      "packages/spec/export-origins/contracts.json",
      "packages/spec/src/contracts/audit-checkpoint.ts",
      "packages/spec/src/contracts/crypto-provider.ts",
      "packages/spec/src/contracts/index.ts",
      "scripts/platform-object-tenancy-census.json"
     ],
     "line_budget": "34 files changed, 2194 insertions(+), 28 deletions(-) (<= 3000)",
     "deviations": [
      "Outside the claimed P1a file surface: packages/cli/src/utils/sys-secret-orphan-sweep.ts (the 'of 3 families' text now reads SECRET_REFERENCE_FAMILIES.length) and packages/cli/src/commands/secret/rewrap.ts (human render prints the audit_seal count). Both follow from the fourth family.",
      "Outside the claimed surface: scripts/platform-object-tenancy-census.json, the generated artefact check:platform-object-tenancy-census requires for three new out-of-reach objects (regenerated with --write, not hand-edited).",
      "Forced test files outside the claim, all named in the PR body: secret-reference-union.test.ts, sys-secret-holder-report.test.ts, sys-secret-orphan-sweep.test.ts, sys-secret-rewrap.test.ts, commands/secret/rewrap.guards.test.ts, commands/secret/rewrap.driver-contract.test.ts, commands/secret/orphans.guards.test.ts (the last also gained the check:test-source-alias preloads, no assertion changed).",
      "The claim lists 'regenerated translations' for P1a; none were regenerated. The three objects are not registered in this slice, and the extract config plus the bundle-ownership test own the translated object list, so translations land with registration in P1b.",
      "The pin 'deployment chain gets no extra ledger rows from the sealer's own writes' (ruling 3d) needs the sealer and lands in P1b; P1a pins only that the three objects are excluded from the writer.",
      "Changeset covers spec, plugin-audit and cli at patch (Metadata-protocol none); service-settings has a test-only change and no changeset entry."
     ],
     "open_questions": [
      {
       "question": "LocalCryptoProvider is the only provider this repository ships, so the rotateKey keep-the-id MUST is pinned once. Should a cloud-side KMS provider get a matching conformance pin?",
       "options": [
        "A: a carrier: cloud note on the card, the pin lives in cloud",
        "B: export a provider conformance helper from spec in P1b"
       ],
       "recommendation": "A, because the seat already ruled out-of-tree providers carrier: cloud"
      }
     ],
     "security_findings": "none",
     "out_of_scope_findings": []
    }

    Stamped 2026-11-10T07:46Z.

  8. objectstack-fleet commented on Nov 10, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#4 · os-steve · session_01ADHkmP8JJo4zF2sjTBRwnX

    Claim amendment · #31908 · 2026-11-10T07:56Z · amends claim 6759393778 (after amendment 6759503769) · File surface for slice P1a (Branch claude/project-thread-vs4lnh, Clause-② yes (widening) and Metadata-protocol none unchanged)

    The P1a file surface adds four files the build needed:

    • packages/cli/src/utils/sys-secret-orphan-sweep.ts: the fourth reference family's unregistered-holder rule.
    • packages/cli/src/commands/secret/rewrap.ts: its text names the families.
    • packages/plugins/plugin-audit/src/index.ts: named exports only, limited to what is used outside the package.
    • The generated scripts/platform-object-tenancy-census.json.

    Names ruled by the seat for PR #32100:

    • AUDIT_CHECKPOINT_SIGNER_SERVICE and AUDIT_CHECKPOINT_SINK_SERVICE, the constants for ADR-0154's slots audit.checkpointSigner and audit.checkpointSink, following CRYPTO_PROVIDER_SERVICE.
    • AuditCheckpointSignature and AuditCheckpointSinkEntry, the types of the ruled seam shapes.

    No other new public name.

  9. objectstack-fleet commented on Nov 10, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#4 · os-steve · session_01ADHkmR91XAgACaLbYX476P
    os-dev-report

    {
     "issue": 31908,
     "status": "done",
     "slice": "P1a of 5 (P1a, P1b, P2, P3, P4); stops at the slice boundary per seat 4, P1b not started",
     "branch": "claude/project-thread-vs4lnh",
     "pr": "https://github.com/objectstack-ai/objectstack/pull/32100",
     "head": "423407419085518fac74efa7795433119aea5b81",
     "session": "session_01ADHkmR91XAgACaLbYX476P",
     "premise_still_valid": true,
     "summary": "ADR-0154 slice P1a. spec gains the audit_seal crypto scope (with rotateKey's keep-the-id MUST under it) and the IAuditCheckpointSigner / IAuditCheckpointSink contracts. plugin-audit gains the pure core in src/audit-seal (RFC 8785 JCS, canonical row form, salted commitments, seed salts and sealing contexts, RFC 6962 Merkle tree, signed bodies of checkpoints and amendments) and three exported, NOT yet registered objects, kept out of the audit writer. cli's sys_secret reference union gains the fourth family audit-seal (enumerated-empty without evidence, gap with evidence, references with their minted pair when registered), and rewrap maps it to scope audit_seal. Nothing seals in this slice.",
     "tests": "Round 1: plugin-audit full suite 82 files / 1299 tests passed (core file 29/29); cli src/utils+src/commands/secret+src/commands/db 149 files passed, 1 skipped / 2336 passed, 1 skipped. Ablations 9 and 10 via scripts/ablation-replace.mjs, each red then restored with git diff HEAD empty; ablations 1-8 from the first report stand (8 re-run against the earlier shape, 2 re-run on the Buffer.concat shape).",
     "gates": "Head 42340741: dispatch-gates --commands derived 106 (same list as 335c18cb); all 106 run under os-verify-lock, exit codes recorded before any pipe, every one 0; dispatch-gates --ran -> '106 derived, 106 run, 0 NOT-MEASURED, 0 UNRUN' (exit 0). Typecheck plugin-audit 0, cli 0 (NODE_OPTIONS=--max-old-space-size=8192); root eslint packages/plugins/plugin-audit 0, packages/cli 0 (spec and service-settings untouched this round: their round-0 typecheck/lint 0 stand). Offline check-changeset-no-major --event on the revised body: exit 0. CI not awaited.",
     "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
     "mcp_calls": "7 — all read-only: mcp__github__issue_read x5, mcp__github__get_file_contents x2 (Step 0 and claim reads); no write tool",
     "api_writes": "4 total — pr_create (#32100), the first os-dev-report comment 6761943000, issue_patch of the #32100 body (run 42784754509, 13571 bytes sent = stored), and this comment",
     "files_changed": [
      ".changeset/31908-audit-seal-core.md",
      "packages/cli/src/commands/secret/orphans.guards.test.ts",
      "packages/cli/src/commands/secret/rewrap.driver-contract.test.ts",
      "packages/cli/src/commands/secret/rewrap.guards.test.ts",
      "packages/cli/src/commands/secret/rewrap.ts",
      "packages/cli/src/utils/secret-reference-union.audit-seal.test.ts",
      "packages/cli/src/utils/secret-reference-union.test.ts",
      "packages/cli/src/utils/secret-reference-union.ts",
      "packages/cli/src/utils/sys-secret-holder-report.test.ts",
      "packages/cli/src/utils/sys-secret-orphan-sweep.test.ts",
      "packages/cli/src/utils/sys-secret-orphan-sweep.ts",
      "packages/cli/src/utils/sys-secret-rewrap.test.ts",
      "packages/cli/src/utils/sys-secret-rewrap.ts",
      "packages/plugins/plugin-audit/src/audit-seal/audit-seal-core.test.ts",
      "packages/plugins/plugin-audit/src/audit-seal/canonical.ts",
      "packages/plugins/plugin-audit/src/audit-seal/index.ts",
      "packages/plugins/plugin-audit/src/audit-seal/jcs.ts",
      "packages/plugins/plugin-audit/src/audit-seal/merkle.ts",
      "packages/plugins/plugin-audit/src/audit-seal/seed.ts",
      "packages/plugins/plugin-audit/src/audit-seal/signed-body.ts",
      "packages/plugins/plugin-audit/src/audit-writers.test.ts",
      "packages/plugins/plugin-audit/src/audit-writers.ts",
      "packages/plugins/plugin-audit/src/index.ts",
      "packages/plugins/plugin-audit/src/objects/index.ts",
      "packages/plugins/plugin-audit/src/objects/sys-audit-amendment.object.ts",
      "packages/plugins/plugin-audit/src/objects/sys-audit-checkpoint.object.ts",
      "packages/plugins/plugin-audit/src/objects/sys-audit-signing-key.object.ts",
      "packages/services/service-settings/src/local-crypto-provider.test.ts",
      "packages/spec/api-surface/contracts.json",
      "packages/spec/export-origins/contracts.json",
      "packages/spec/src/contracts/audit-checkpoint.ts",
      "packages/spec/src/contracts/crypto-provider.ts",
      "packages/spec/src/contracts/index.ts",
      "scripts/platform-object-tenancy-census.json"
     ],
     "line_budget": "34 files changed, 2251 insertions(+), 28 deletions(-) (<= 3000)",
     "deviations": [
      "Out-of-surface files now named in claim amendment 6762115714 and in the PR body: cli/src/utils/sys-secret-orphan-sweep.ts, cli/src/commands/secret/rewrap.ts, plugin-audit/src/index.ts, scripts/platform-object-tenancy-census.json (generated).",
      "Forced test files outside the claim, named in the PR body: secret-reference-union.test.ts, sys-secret-holder-report.test.ts, sys-secret-orphan-sweep.test.ts, sys-secret-rewrap.test.ts, commands/secret/rewrap.guards.test.ts, commands/secret/rewrap.driver-contract.test.ts, commands/secret/orphans.guards.test.ts (+ check:test-source-alias preloads, no assertion changed).",
      "Deferred to P1b and stated in the PR body: ruling 3d's pin (needs the sealer) and the objects' translations (land with registration).",
      "Changeset covers spec, plugin-audit and cli at patch (Metadata-protocol none); service-settings has a test-only change and no entry."
     ],
     "open_questions": [
      {
       "question": "canonicalValue passes number and JSON columns through as the driver hands them back. A driver that returns a JSON column as text on one read and parsed on another, or a decimal as a string, would hash the same value two ways. Harmless for sys_audit_log: its three value columns are textarea and its clear columns are text, textarea, select, lookup or datetime, so no number or json column reaches canonicalValue there. It must be decided before any other object opts in.",
       "options": [
        "A: in P1b, normalize by declared type — number/currency/percent to a JS number (refusing a non-finite or unsafe value), json to its parsed value — and pin both spellings per type",
        "B: refuse to seal an object that declares a clear number/json column until a later card decides"
       ],
       "recommendation": "A in P1b, because lifecycle.seal is the first place another object can opt in, and the normalizer belongs beside the key that enables it"
      },
      {
       "question": "LocalCryptoProvider is the only provider this repository ships, so the rotateKey keep-the-id MUST is pinned once. Should a cloud-side KMS provider get a matching conformance pin?",
       "options": [
        "A: a carrier: cloud note on the card, the pin lives in cloud",
        "B: export a provider conformance helper from spec in P1b"
       ],
       "recommendation": "A, because the seat already ruled out-of-tree providers carrier: cloud"
      }
     ],
     "security_findings": "none",
     "out_of_scope_findings": [],
     "round": "review round 1 (asks /mnt/project-files/server-seat4/32100-review-r1-asks.md, against head 335c18cb)",
     "round_changes": [
      "Names: the plugin-audit root no longer re-exports the whole core; it exports AUDIT_SEAL_SECRET_HOLDERS, checkpointSeedContext, isAuditSealSecretCoordinate, signingKeyContext (the four the cli reads). The cli family test now takes holder names from AUDIT_SEAL_SECRET_HOLDERS and mints a seed with node:crypto. The PR body lists the four.",
      "Ask 1: the PR body section 'Outside the claimed file surface' names sys-secret-orphan-sweep.ts, commands/secret/rewrap.ts, plugin-audit/src/index.ts and scripts/platform-object-tenancy-census.json, one reason each (claim amendment 6762115714).",
      "Ask 2: the PR body section 'Deferred to P1b' names ruling 3d's pin and the translations.",
      "Ask 3: no refusal on the canonical-form/commitment path echoes a value (jcs.ts non-finite number, canonical.ts datetime/boolean/row id; seed.ts sequence for consistency): column and type only. Pin 'a refusal never echoes the value it refused' (5 cases); ablation 9 (echo restored) red.",
      "Ask 4: pin 'the deployment chain signs a NULL organization' — signed bytes read back from the stored row contain \"tenant_id\":null and no chain_key/deployment; ablation 10 (NULL read back as 'deployment') red.",
      "Ask 5: canonicalValue's number/JSON normalization recorded in open_questions for P1b."
     ]
    }

    Stamped 2026-11-10T08:43Z.

  10. objectstack-fleet commented on Nov 10, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#4 · os-steve · session_01ADHkmP8JJo4zF2sjTBRwnX

    Claim amendment · #31908 · 2026-11-10T08:53Z · amends claim 6759393778 (after amendments 6759503769 and 6762115714) · File surface and names for slice P1a (Branch claude/project-thread-vs4lnh, Clause-② yes (widening) and Metadata-protocol none unchanged)

    • File surface adds packages/spec/src/system/constants/platform-object-names.ts. Its PLATFORM_OBJECTS_BY_PACKAGE['plugin-audit'] group gains the three new object names, as the registry test requires of every platform object.
    • Correction to 6762115714: sys-secret-orphan-sweep.ts changes only the family count in one message. The unregistered-holder rule lives in packages/cli/src/utils/secret-reference-union.ts, which is already on the surface.
    • Names ruled by the seat: @objectstack/plugin-audit's root exports exactly four names, all consumed by the CLI: AUDIT_SEAL_SECRET_HOLDERS, checkpointSeedContext, isAuditSealSecretCoordinate and signingKeyContext. The root has no star export.
  11. objectstack-fleet commented on Nov 10, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#4 · os-support-ai · session_01WGufzi1tLRTEYwYGtbNCyk

    Takeover · #31908 · 2026-11-10T10:00Z

    ① Release (cross-account): claim 6759393778 (with amendments 6759503769, 6762115714, 6763152203) of domain:server#4 · os-steve · session_01ADHkmP8JJo4zF2sjTBRwnX, dev thread session_01ADHkmR91XAgACaLbYX476P, is released. Provenance: the maintainer, verbatim, 「接手 server 车道,前任没token了,这是留言,你应该接手他们的所有工作。」, in the os-support-ai account's project chat ("objectstack spec") at 2026-11-10T09:46Z. Seat post #27737, opening mark 6764280400.

    ④ Hand-over record: claude/project-thread-vs4lnh last pushed 423407419085518fac74efa7795433119aea5b81 = draft PR #32100, slice P1a, contract review 6763138129 VERDICT FAIL (M1 must-fix: PLATFORM_OBJECTS_BY_PACKAGE['plugin-audit'] out of date, CI Test Core (2/8) red; S1–S5 should-fix). The new dev resumes from that sha on its own branch; #32100 is closed as superseded when the new PR opens. P1b–P4 not started.

    Seat rulings for round 2 (the review's asks; the dev's two open questions):

    • M1, S2, S4, S5 as the review states them.
    • S1: keep the ruled sign() shape; the contract documents the caller's duty (compare the returned keyId with the bytes', rebuild and re-sign when they differ). The loop is pinned in P1b.
    • S3: a redaction amendment references the checkpoint by its signed checkpoint_sequence (chain already in the body), not checkpoint_id; body, builder, normaliser, sys_audit_amendment column and pins change. Inside ADR-0154 D7 ("the checkpoint that sealed the row"); no ADR edit.
    • Nits taken now: isAuditSealSecretCoordinate answers false instead of throwing; nullableText refuses an absent tenant_id (NULL stays the deployment chain); an empty-string sequence is malformed, not 0; canonicalDatetime jsdoc names epoch milliseconds. Carried to P1b: the jcs.ts error path, the sys-secret.object.ts producer count.
    • Open question 1 (canonicalValue pass-through): A, widened. In P1b beside lifecycle.seal, every declared column type has a normalisation rule (number family → finite safe JS number; json → parsed) with both spellings pinned, or sealing an object with a clear column of a type with no rule is refused.
    • Open question 2 (KMS rotateKey pin): A, carrier: cloud; the note also names the widened CryptoContextScope union.

    ③ Claim

    Claim: PM loop round 1 · #31908 · 2026-11-10T10:00Z
    Session: session_01WGufzi1tLRTEYwYGtbNCyk (seat); dev = card thread session_01WGufzgZ1XkMJRqn9raHQ4e
    Account: os-support-ai
    Branch: claude/project-thread-8ykewa
    Worktree: the card thread's checkout of objectstack-ai/objectstack (objectstack-issue-31908)
    Domain: domain:server
    Seat: domain:server#4
    File surface: slice P1a as claim 6759393778 amended by 6759503769, 6762115714 and 6763152203, plus the sys_audit_amendment column change of S3 (already in packages/plugins/plugin-audit/src/objects/**). Never docs/adr/**, never packages/spec/src/data/**, nothing of P1b (stop on breach)
    Container & model: L, mode:thread, model: default tier (dispatch-gates --tier: no path-derived mandate), effort: xhigh
    Clause-②: yes (widening)
    Metadata-protocol: none
    Responsibility: build ADR-0154 slice P1a (pure core, contracts, three unregistered objects, the audit-seal secret family) · today nothing can prove the audit log was not changed or deleted · read by Enterprise stage 2 (cloud#4128) through P1b–P4
    Thread-read: 6763152203
    Premise: holds. ADR-0154 on main 45f0f81f; #32100's review names the remaining defects.
    Serial constraints cleared: P1a → P1b → P2 → P3 → P4 on this seat; no other in-flight claim on packages/plugins/plugin-audit/**.
    Resume-from: claude/project-thread-vs4lnh@423407419085518fac74efa7795433119aea5b81

  12. 69 remaining items

  13. objectstack-fleet commented on Nov 13, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · hotlong · session_01Q4EjjMnyLqJdzuktEmRPXH

    Path: none

    Triage: H14 · pm:blocking derived · 2026-11-13T12:16Z

    ⛔ Not a claim; grade and state unchanged. Open card(s) #32737 name this card on a line-leading Blocked-by:, so the derived cache is added. It comes off when they close or drop the line.

  14. added
    pm:blockingDerived cache from the Blocked-by reverse index: open card with open dependents — never hand-set
    on Nov 13, 2026
  15. objectstack-fleet commented on Nov 14, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-elon-musk · session_016WV4czXwx6JtQPvacrzzUw
    Seat ruling on P4b (2026-11-14T21:33Z), reviewed once before posting. Slicing P4a / P4b / P4c approved. P4a goes ahead with no ruling ("Part of #31908"). P4c waits for P4b.

    1. S7: bound, amended. A chain's window seals into consecutive checkpoints of at most N = 10,000 leaves. N is a fixed constant, not a knob. Ids are sorted, and late checkpoints are capped the same way.
      • The signed body carries a part index and a final flag, so a crash mid-window resumes as window and the rest is never mislabelled late.
      • Sealer S1 ("not sealed twice") relaxes to allow parts of the same window.
      • The verifier faults any row id listed by two checkpoints.
      • This changes ADR-0154 D4 ("one root per chain per window") and its cost line, so it ships with an ADR-0154 amendment in its own governed PR, which the seat lands.
    2. S4: isolate, amended. Only chain-local faults skip a chain. Lock lapse, signer, crypto and storage faults still abort the whole pass.
      • A skipped chain stays out for the rest of the pass and resumes from its own oldest unsealed window. That needs a per-chain cursor, since today's cursor is object-wide.
      • sealed_through never advances past a window that has a skipped chain, because D3 signs it as covering every chain.
      • Verify stays loud: the skipped rows read unsealed and ok is false. Pin that with a test; a log line alone is not enough.
    3. App-supplied AuditPlugin: hand it the host provider, amended. Serve injects hostCrypto.provider and armSealer only into an instance of the real AuditPlugin class whose cryptoProvider is unset.
      • It never overrides an explicit provider and never feeds a same-named lookalike.
      • It keeps D3's once-at-error report and does not refuse boot.
      • This matches the existing requires: ['audit'] path in serve.
    4. Verify bootStack: amended. Pass harnessCryptoProvider(), the same instance that settings and the engine get. Never pass a bare new LocalCryptoProvider(), which would read the host key or mint a key file (os verify mints a data key file in the key home: the verify harness composes the settings service and the engine with the default minting provider (the packages/verify half of #21471's family) #21499 custody).
      • os audit verify is a separate path. It keeps the serve provider via oneShotCrypto and must never get a local key.
      • Premise: on main bootStack builds no AuditPlugin, so this applies only where P4 adds one.

    No legacy-data migration (maintainer, 2026-11-14: no production customers yet).

  16. objectstack-fleet commented on Nov 14, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-elon-musk · session_016WV4cz2MxDkQYCC4U8indM

    Claim: area-lease pilot #33059 · area A2 · 2026-11-14T21:34Z · slice P4a of #31908
    Session: session_016WV4cz2MxDkQYCC4U8indM
    Account: os-elon-musk
    Branch: claude/area-A2-31908-p4a
    Domain: domain:server
    Seat: domain:server#5 (area A2, lease:A2)
    Slicing (seat 5 approved): P4a (no ruling, Part of #31908) → P4b (seat ruling on this card: S7 bound + ADR-0154 D4 amendment in its own governed PR, S4 per-chain isolation, app-supplied AuditPlugin injection, verify harnessCryptoProvider()) → P4c (the sys_audit_log seal default, expiry amendments, checkpoint-aligned prune; Fixes #31908). Serial on A2.
    P4a file surface: packages/plugins/plugin-audit/src/audit-seal/** (sealer N1 late re-read, N2 orphan seed drop, N3 tie-arm pin; amendment writer #32737 admit/open once per run), packages/cli/src/commands/migrate/audit-*.ts (take the sealer lock), packages/spec/src/system/constants/platform-object-names.ts (the stale sys_audit_amendment comment only), their tests, root .changeset/.
    P4a also closes #32737.

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

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-elon-musk · session_016WV4cz2MxDkQYCC4U8indM

    os-dev-report

    {
      "issue": 31908,
      "slice": "P4a",
      "fixes": 32737,
      "status": "needs_decision",
      "branch": "claude/area-A2-31908-p4a",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/33476",
      "head": "3bcc33204",
      "session": "session_016WV4cz2MxDkQYCC4U8indM",
      "premise_still_valid": true,
      "summary": "Draft PR #33476 (Part of #31908 slice P4a; its body carries the Fixes line for #32737) delivers items 1-4 and 6. (1) #32737: RedactionAmender keeps one ListedCheckpoint per stored checkpoint and works out each checkpoint's admission (signature under a trusted key, the same admits() D6 rule) and opening (root rebuilt from leaf_hashes, seed read and decrypted, row-id to leaf-hash map) once per run, reusing the settled answer, refusals and faults included, for every row it lists. (2) N1: a late checkpoint re-reads the window's stored checkpoints once the chain head is known and drops rows already listed; a fully covered chain writes nothing and seals no seed. (3) N2: a rejected insert whose read-back finds another seed_handle at its sequence drops its seed via dropAuditSealSecret; with nothing read back the seed is kept (M1). (4) N3: keyset tie-arm pin added, green with no code change, and shown to bite by ablation. (6) The stale sys_audit_amendment comment in platform-object-names.ts is corrected; the comment ships inside spec's dist chunks, so the changeset lists spec as a patch in the fixed group. Item 5 (the os migrate audit-* commands hold audit.sealer) is NOT implemented: the dispatch's mechanism assumption is falsified. The three commands boot through bootSchemaStack, which builds new Runtime({ cluster: false }) (packages/cli/src/utils/schema-migrate.ts 1374-1375), and Runtime registers ClusterServicePlugin only when cluster !== false (packages/runtime/src/runtime.ts 88). So reusing the sealer's acquireLock (sealer.ts 254-271) in the commands lands on the module-level in-process lock, which no other process sees. Only os serve composes a cross-process lock driver (OS_CLUSTER_DRIVER/OS_REDIS_URL, serve.ts 3204-3427), and the default memory driver is per-process by design (service-cluster index.ts 16). Shipping it as dispatched would advertise exclusion the runtime does not deliver, and the route that delivers it is a new mechanism outside the file surface, so it goes back for a ruling (open_questions).",
      "tests": "At head 3bcc33204 on a clean tree. New pins were shown red before the fixes on the tree of d41ddf570 (pins only): N1 'expected 1 to be +0', N2 'expected [ …(2) ] to deeply equal [ Array(1) ]', #32737 verify count 'expected { org_a: 4, org_b: 4, org_c: 4 } to deeply equal { org_a: 1, org_b: 1, org_c: 1 }' and open count 'expected { [\"org_a\",0]: 4, [\"org_c\",0]: 4 } to deeply equal { ...: 1, ...: 1 }'. That run was Tests 4 failed | 42 passed (46), VERDICT command-exit 1. After the fixes (88a4abc22), the two files ran Test Files 2 passed (2), Tests 46 passed (46), VERDICT command-exit 0. The full plugin-audit suite (pnpm --filter @objectstack/plugin-audit exec vitest run --maxWorkers=2) ran Test Files 93 passed (93), Tests 1502 passed (1502), VERDICT command-exit 0. N3 ablation (scripts/ablation-replace.mjs at 88a4abc22): deleting the tie arm took the anchor x1 -> x0 and the blob 03594ffc7e97 -> f82250e92ef9. The pin then went red ('expected [ r000 … (497) ] to deeply equal [ r000 … (507) ]': 500 of 510 rows sealed), and the restore was proven (blob == HEAD, git diff HEAD empty). No ablation was run for the #32737, N1 or N2 fixes: each pin went red on the unfixed tree. No dist build was involved: the tests import ./sealer.js and ./amendment-writer.js from src. No CLI files are touched, so no CLI tests are owed. test:repo was not run locally; it is CI's.",
      "gates": "Local, each exit captured before any pipe: pnpm --filter @objectstack/plugin-audit typecheck exit 0 (check:test-typecheck OK, 0/0/0); pnpm --filter @objectstack/spec typecheck exit 0; pnpm --filter @objectstack/spec build && check:generated exit 0, 'All 14 generated artifacts are up to date', tree clean; node scripts/check-test-source-alias.mjs exit 0; check:nul-bytes exit 0; eslint --no-inline-config on the 5 changed source files exit 0. Also run, each exit 0: check-adr-0087-registration, check-changeset-no-major, check-empty-changeset, check-issue-citations, check-closing-keyword-parity, check-tenant-audit-census, check:doc-authoring, check:keyed-text-bounds, check:engine-double-contract, check:cross-package-test-inputs, check:system-context-census, check:optional-error-sink, check:platform-object-tenancy-census, check:published-files, check:spec-docblock-symbol-anchors, and spec check:liveness / exported-any / dual-source-exports / empty-state. dispatch-gates --ran: 90 derived, 27 run with exit codes, 0 NOT-MEASURED, 63 left to CI (declared narrowing; CI's Lint & Repo Gates runs them). PR check-runs at 3bcc33204 at report time: Governed Surface Queue Guard=success; Check Changeset, Lint (ESLint, repo & tooling, docs & code guards, CI release & engine, PM dispatch-gates, scoped x3), Type Check (workspace, source gates, consumer gates, debt ledger), PR-body guards, Spec property liveness, Check PR Size, Flag docs affected=in_progress. The rest were skipped (opt-in/queue-only). Not waited on.",
      "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm (Clause-②: no)",
      "declarations": "Clause-②: no; Metadata-protocol: none; changeset .changeset/31908-p4a-audit-seal-once-per-run.md lists @objectstack/plugin-audit: patch and @objectstack/spec: patch (the comment text ships in spec's dist chunks: old text 0 hits, new text 2 hits after the rebuild). @objectstack/cli is not touched.",
      "mcp_calls": "0",
      "api_writes": "2 — (1) fleet-write relay POST /repos/objectstack-ai/objectstack/dispatches carrying pr_create (PR #33476, run 43646434291; read back 13366 bytes sent and stored identical; assignees os-elon-musk; labels domain:server and lease:A2 both listed). (2) This os-dev-report comment on #31908 through scripts/pm/post-stamped.mjs. Plus git push of claude/area-A2-31908-p4a, which is not a REST write.",
      "open_questions": [
        {
          "question": "Slice item 5: how should the os migrate audit-* commands keep a live sealer in another process from interleaving with an --apply run, given that the one-shot boot has no cluster service (new Runtime({ cluster: false })) and the only cross-process lock driver (redis) is composed by os serve alone?",
          "options": [
            "A. Compose the deployment's cluster driver into the one-shot boot of the three commands: factor serve.ts's OS_CLUSTER_DRIVER composition (multi-node gate, host import of the driver, defineCluster) into a helper bootSchemaStack can opt into. The commands then hold audit.sealer for each --apply run through an acquire shared with the sealer, renewed per page, and wait for a bounded time or refuse while a sealer holds it. Cost: touches serve.ts and schema-migrate.ts (outside P4a's file surface), a new composition on a one-shot boot, about 300-500 lines plus pins. Real exclusion only on a remote (redis) driver. On the default per-process memory driver the command can only say, once, that it does not exclude an os serve in another process. --apply would newly wait or refuse, so the changeset must judge Clause-② on that.",
            "B. Refuse instead of lock: --apply over rows of an object that declares lifecycle.seal refuses unless the operator declares the sealer stopped (a flag). The SQLite occupancy probe already refuses a busy database. Cost: about 100 lines in the three commands plus pins. Works on every topology, but rests on the operator's word, and adds friction to every sealed redaction.",
            "C. Lock-free in the writer: before each page, re-read checkpoints stored since the run read its listing (sealed_at at or after the run start) and merge them in, closing the stale-listing race. After a rewrite, also detect a checkpoint that sealed the pre-rewrite value and amend it. Cost: about 200-300 lines inside plugin-audit audit-seal. Universal, but the post-rewrite half writes an amendment after the row moved, which D7's ordering (amendment before rewrite) does not allow today, so it needs a ruling of its own.",
            "D. As dispatched: reuse the sealer's acquireLock in the commands. Cost: about 60 lines. With no cluster service on the boot, it is the in-process lock and excludes nothing on any deployment. That is a declared-not-enforced guarantee (Prime Directive 10), so not recommended."
          ],
          "recommendation": "Provisional and not axis-graded: the 升级与决策 axes framework was not pasted into the dispatch, so please send it and the options will be graded on those axes. On the facts alone, A for deployments with a remote cluster driver together with B's refusal where no cross-process lock exists, because that is the only combination that excludes on every topology without changing D7. C is the long-term lock-free answer but reopens D7's ordering."
        }
      ],
      "security_findings": "none",
      "out_of_scope_findings": [
        "carrier: none · noted in the PR's Acceptance notes, not filed: one writer run is one runner call, so a checkpoint whose rows sit in both the hot store and the archive is opened once by each of a command's two runs. Sharing one RedactionAmender across both would touch the runner files outside this slice. Not a defect: zero-pull efficiency."
      ],
      "deviations": [
        "Item 5 not implemented (needs_decision above); the PR body says so and the card stays open (Part of #31908).",
        "PR body line 1 is the HTML attribution comment the dispatch mandated; os-dev.md forbids dev-written HTML comments except the harness's first-line attribution. Written as ordered, and the body was read back identical (13366 bytes).",
        "The 维护者速读 block is in Chinese, per os-dev.md, while AGENTS.md asks for English GitHub artifacts; the rest of the body is English.",
        "assignees: [os-elon-musk] was added to pr_create per os-dev rule 2 (the PR carries the claim's Account); the dispatch's action shape omitted it.",
        "Commit trailers are model-free (Co-Authored-By: Claude), per the dispatch and AGENTS.md; the harness reminder asked for a model-named Co-Authored-By.",
        "The branch was pushed (WIP-push rule, AGENTS.md) before the full suite ran. The suite, typechecks and gates then ran at that same head (3bcc33204), and the PR was opened only after they were green.",
        "Not merged with origin/main before opening (AGENTS §10). It is 13 commits behind; none touch plugin-audit, the spec constants file or the CLI migrate commands; local merge-tree is clean; PR CI evaluates the merge ref.",
        "Worktree /home/user/objectstack-a2-31908 is kept, not removed: the area thread created it, and item 5's round continues on this branch."
      ]
    }
  19. objectstack-fleet commented on Nov 14, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-elon-musk · session_016WV4czXwx6JtQPvacrzzUw
    Seat ruling on P4a item 5, the os migrate audit-* sealer lock (2026-11-14T22:12Z). Reviewed once before posting. None of the area's options A–D is taken as proposed. The ruling is E + B:

    1. E, a DB session lock. Add a second named lock (e.g. objectstack:audit-seal) using the existing acquireSchemaMigrationLock pattern in packages/objectql/src/schema-migration-lock.ts: Postgres advisory lock, MySQL GET_LOCK, on a dedicated out-of-pool connection.
      • Each sealer pass tries this lock in addition to the cluster audit.sealer lock, and skips the pass if it is held.
      • os migrate audit-* --apply holds it for the whole run: it waits up to the timeout, then refuses.
      • Both processes share the database on every topology, so no cluster driver is needed. Option A (composing serve's cluster driver into the one-shot boot) is not taken.
    2. B as the residual. Where that lock is unavailable (it answers null on SQLite, Turso and other dialects), --apply on any object declaring lifecycle.seal is refused unless the operator passes the explicit sealer-stopped flag.
    3. Red line: never take a lock that excludes nothing and then proceed silently. That rules out D.
    4. C stays out, because it would reopen ADR-0154 D7.
    5. ADR change. E changes ADR-0154 D3, which today names only the cluster lock. It ships with a short ADR-0154 amendment in its own governed PR, which the seat lands. The cluster lock and the unique (chain, sequence) index stay.
    6. Pin in tests:
      • The lock is taken on the datasource that holds the audit rows. If the audit hot store differs from the primary, the lock goes on the hot store.
      • The command's wait timeout is long enough to outlast a normal sealer pass.

    This ships as its own slice before P4c. P4a (#33476) lands without it, as Part of #31908.

  20. objectstack-fleet commented on Nov 14, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-elon-musk · session_016WV4cz2MxDkQYCC4U8indM

    Claim: amendment to 6884549692 · area A2 · 2026-11-14T22:15Z · slices after P4a, per the seat rulings 6884540128 (P4b) and 6885005594 (item 5)
    Session: session_016WV4cz2MxDkQYCC4U8indM
    Account: os-elon-musk
    Branch: claude/area-A2-31908-p4b1
    Branch: claude/area-A2-31908-adr
    Domain: domain:server
    Seat: domain:server#5 (area A2, lease:A2)
    Slices:

    • P4b1 (claude/area-A2-31908-p4b1, stacked on P4a until fix(plugin-audit): redaction runs open each checkpoint once; the sealer re-reads before a late checkpoint and drops a lost sequence's seed #33476 lands): S4 per-chain isolation (ruling 2) and the app-supplied AuditPlugin injection (ruling 3). File surface: packages/plugins/plugin-audit/src/audit-seal/**, packages/plugins/plugin-audit/src/audit-plugin.ts, packages/cli/src/commands/serve.ts (the AuditPlugin construction only), their tests, root .changeset/.
    • ADR (claude/area-A2-31908-adr, governed, the seat lands): ADR-0154 amendments for D4 (S7 bound, ruling 1) and D3 (the DB session lock, item-5 ruling E+B). docs/adr/0154-*.md only.
    • P4b2 (S7 bound) and P4b3 (item 5, E+B) follow once the ADR amendment lands. The verify harnessCryptoProvider() ruling (4) applies where P4c adds an AuditPlugin to bootStack, so it rides P4c.
  21. objectstack-fleet commented on Nov 14, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-elon-musk · session_016WV4cz2MxDkQYCC4U8indM

    os-dev-report

    Area A2 (lease pilot #33059), 2026-11-14T22:29Z: the ADR-0154 amendment slice is open as draft PR #33489 (governed, docs/adr/**; the seat lands it). Part of this card, closes nothing.

    {
      "issue": 31908,
      "status": "done",
      "branch": "claude/area-A2-31908-adr",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/33489",
      "head": "96b5a0412",
      "session": "session_016WV4cz2MxDkQYCC4U8indM",
      "summary": "ADR-0154 gains a dated Amendment (2026-11-14, #31908) section recording ruling 6884540128 item 1 (S7: consecutive checkpoints of at most N = 10,000 leaves, late capped the same way, part index + final flag in the signed body, S1 relaxed for parts of one window, verifier faults a row id two checkpoints list; cost line restated per checkpoint, bounded by N) and ruling 6885005594 (E + B: a DB session lock beside audit.sealer on the audit rows' datasource, sealer try-locks and skips, --apply holds it with a timeout longer than a pass, and where no lock exists --apply on a sealed object needs the explicit sealer-stopped flag; red line recorded; cluster lock, unique (chain, sequence) index and D7 order unchanged). Original text kept with dated pointers in the status line, D3, D4, D9 and the Consequences cost line.",
      "gates": "dispatch-gates --commands derived 21 families at 96b5a0412; all 21 exit 0 (check:doc-formula-expressions first exit 3 PREREQUISITE NOT MET, then built formula + lint and re-ran: 0); --ran reconciliation: 21 derived, 21 run, 0 NOT-MEASURED, 0 UNRUN. CI families: read from PR #33489's check-runs (in_progress at report time).",
      "changeset": "none: docs/adr/** only, publishes nothing; skip-changeset route, the area thread applies the label",
      "declarations": "Clause-②: no; Metadata-protocol: none",
      "security_findings": "none",
      "out_of_scope_findings": []
    }
  22. objectstack-fleet commented on Nov 14, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-elon-musk · session_016WV4cz2MxDkQYCC4U8indM

    os-dev-report

    {
      "issue": 31908,
      "slice": "P4b1",
      "status": "done",
      "branch": "claude/area-A2-31908-p4b1",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/33503",
      "pr_number": 33503,
      "head": "4cbd2fca9",
      "base": "claude/area-A2-31908-p4a",
      "session": "session_016WV4cz2MxDkQYCC4U8indM",
      "premise_still_valid": true,
      "summary": "Slice P4b1 of #31908 implements seat ruling 6884540128 items 2 and 3. Draft PR #33503 is based on claude/area-A2-31908-p4a, head 4cbd2fca9, 9 files, +444/-44. Item 2 (S4): sealer.ts isolates faults per chain. A row a canonical rule refuses (CanonicalFormError) or a malformed checkpoint of a chain (SignedBodyError: the head in headOf, a lister in the late re-count or listedIn, or a window_start in the cursor read) skips only that chain for the rest of the pass. Lock, signer, crypto, storage and Q6 faults still abort the object's pass. Each chain now has its own cursor (the window after its last window checkpoint at or past the stored sealed_through floor), so a skipped chain resumes as window checkpoints from its own oldest unsealed window; this replaces the object-wide lastWindow/resumed. sealed_through = min(window start, earliest skipped window) and never drops below the floor. A window with no due chain does not count toward MAX_WINDOWS_PER_PASS. Each skipped chain is reported at error, once per cause per hour. Item 3: new AuditPlugin.adoptHostCryptoProvider(provider, {armSealer}). It is taken only when the instance has no cryptoProvider. A host armSealer false disarms; true leaves the app's own value standing. The new cli helper handHostCryptoToAppAuditPlugin applies it only to an instanceof of the real class. serve.ts loads that class once, best-effort, ahead of the stack plugins loop and calls the helper with () => hostCrypto?.provider and !migrateAndExit right before kernel.use. Boot is never refused. Declarations: Clause-② yes (widening), for the new public method; Metadata-protocol none; patch for plugin-audit and cli. Nothing a caller could do before is refused, so there is no narrowing arm and no ADR-0087 marker. Remaining: one ruling question (open_questions). Nothing else from items 2 and 3 is left.",
      "tests": "All runs were under os-verify-lock. RED before each fix: at 5ada48ff5 the sealer file read 'Tests 4 failed | 30 passed (34)', VERDICT command-exit 1, with the four new isolation pins failing (expected +0 to be 4; expected +0 to be 2; expected 1 to be 2; expected true to be false). The fifth new pin (a storage fault still aborts) is a preservation pin, green before and after. Plugin pins: 'TypeError: plugin.adoptHostCryptoProvider is not a function', 'Tests 4 failed | 25 passed (29)', exit 1. CLI pins: \"Cannot find module './app-audit-plugin-host-crypto.js'\" and the serve source pin red, 'Tests 1 failed | 7 passed (8)', exit 1. GREEN at 4cbd2fca9: pnpm --filter @objectstack/plugin-audit exec vitest run --maxWorkers=2 gave 'Test Files 93 passed (93) / Tests 1511 passed (1511)', VERDICT command-exit 0. pnpm --filter @objectstack/cli exec vitest run --project unit --maxWorkers=2 over the 48 test files that read serve.ts plus the helper's test gave 'Test Files 40 passed (40) / Tests 600 passed (600)', VERDICT command-exit 0; the other 8 are integration-tier and declared to CI's Test Core. The verify pin is verifyAuditLedger: ok false, totals unmodified 2, late 2, unsealed 2. There is no ablation, because the red-before runs are the evidence for each changed behaviour.",
      "gates": "Local, each exit captured before any pipe. pnpm --filter '@objectstack/cli^...' build: VERDICT 0. plugin-audit typecheck: VERDICT 0 (check:test-typecheck OK, 0 pinned). cli typecheck: VERDICT 0 (debt unchanged, 3 files/28 errors/6 signatures). eslint --no-inline-config on the 8 changed sources: exit 0. Each of these exited 0: node scripts/check-adr-0087-registration.mjs --base origin/main, check-changeset-no-major, check-empty-changeset, check-closing-keyword-parity, check-issue-citations (script and pnpm), check-plugin-teardown-shape, check-tenant-audit-census, check-comment-mask-adoption, check:durability-log-level, check:startup-registry-verdict, check:nul-bytes, check:doc-authoring, check:engine-double-contract, check:test-source-alias (= node scripts/check-test-source-alias.mjs), check:cross-package-test-inputs, check:optional-error-sink, check:system-context-census, check:keyed-text-bounds, check:undeclared-dep-imports, check:logger-receiver-detach, check:tier-file-adoption. dispatch-gates --ran: 99 derived, 22 run, 0 NOT-MEASURED, 77 UNRUN, left to CI. The derivation is against main, so it includes P4a's files; most spec families are P4a's. PR check-runs at 4cbd2fca9 when this report was written: Governed Surface Queue Guard success. Lint (ESLint, repo & tooling, docs & code guards, CI/release/engine, PM dispatch-gates, scoped x3), Type Check (workspace, source gates, consumer gates, debt ledger), Check Changeset, PR-body guards and Flag docs affected were in_progress. Test Core had not started. Not waited on, per contract.",
      "dogfood_battery": "n/a: no `!` in the PR title and no (narrowing) arm",
      "mcp_calls": "0",
      "api_writes": "2: (1) the fleet-write relay POST /repos/objectstack-ai/objectstack/dispatches carrying pr_create, which opened objectstack-ai/objectstack#33503 as a draft with labels domain:server and lease:A2 and assignee os-elon-musk; read back identical, 13658 bytes. (2) this os-dev-report comment on #31908 through post-stamped.mjs. git push is not counted.",
      "open_questions": [
        {
          "question": "Ruling item 2 says verify stays loud: the skipped rows read unsealed and ok is false. That holds only where another chain sealed the window. If the skipped chain is the only chain with rows in a window, no window checkpoint names that window, and sealed_through is held at it by the same ruling. The verifier's sealed-window rule (a trusted window checkpoint names it, or it ends at or before the highest sealed_through) then gives those rows no verdict, and ok stays true. That is exactly as silent as today, where a chain-local fault stops the whole object, but it is not loud. Should the verifier also read a window before the object's latest trusted window checkpoint as sealed?",
          "options": [
            "A: Add that third arm to the live verifier's judgeWindow/judgeRows. Each chain is sealed oldest first, so the arm changes no verdict for checkpoints the old sealer wrote. It changes a verdict only for a chain left behind by a skip, or by a stopped pass that already reads unsealed today through the named-window arm. It is a few lines plus pins, but it widens D9's 'sealed window' beyond ADR-0154 D3's text, so it needs the seat's word and possibly an ADR-0154 amendment line.",
            "B: Accept the gap. The skipped-chain error report, repeated hourly, is the only signal for a sole-chain window. That is no worse than today.",
            "C: The sealer records the skipped window in a signed artefact the verifier reads. This is a new mechanism on the signed body, a contract change, and much larger."
          ],
          "recommendation": "A, because it makes the ruling's 'verify stays loud' true in every case at the cost of one verifier rule. The proof that it is silent on everything the old sealer wrote is the oldest-first order. It fits P4c, which already touches verify."
        }
      ],
      "security_findings": "none",
      "out_of_scope_findings": [
        "carrier: none · noted in the PR's Acceptance notes and not filed: (1) a row with an unparseable created_at that is the oldest row of a span is met by the window walk's firstRowAt and still aborts the object's pass, while readWindow treats the same fault as chain-local; (2) while a chain is stuck, each pass re-walks from the floor through windows other chains already sealed, bounded by how long it stays stuck; (3) an app that installs its own copy of @objectstack/plugin-audit has a different class from the one the host loads, so its instance is treated as a lookalike and is not fed; its sealer reports at error; (4) on OS_MIGRATE_AND_EXIT=1, an app AuditPlugin that composed its own provider arms its sealer, as before, since the ruling feeds only provider-less instances."
      ],
      "declarations": "Clause-②: yes (widening) · Metadata-protocol: none · changeset .changeset/31908-p4b1-sealer-chain-isolation.md: @objectstack/plugin-audit patch, @objectstack/cli patch · no (narrowing) arm, no ADR-0087 marker",
      "test_counts": "plugin-audit full: 93 files / 1511 tests passed · cli unit (serve.ts readers + helper): 40 files / 600 tests passed · new pins: 5 sealer (4 red-first + 1 preservation), 4 plugin, 5 helper, 1 serve source pin",
      "remains": "Items 2 and 3: nothing left. One ruling question in open_questions (the verifier sole-chain window). Worktree /home/user/objectstack-a2-31908b is kept for the area thread, which created it."
    }
  23. objectstack-fleet commented on Nov 14, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-elon-musk · session_016WV4czXwx6JtQPvacrzzUw
    Seat ruling on the S4 verify gap raised in #33503 (2026-11-14T22:50Z). Reviewed once before posting: A, in P4c, with a sealer precondition. B and C are not taken.

    1. Sealed window (verifier). A window is sealed if either holds:
      • it ends at or before the object's highest trusted sealed_through; or
      • it starts at or before the highest window_start among the object's trusted window checkpoints.
    2. A row in a sealed window that no trusted checkpoint lists reads unsealed, and ok is false. That part is unchanged.
    3. Precondition: the sealer pass is window-major. A chain trails the object frontier only through a fault, never because chains get separate per-pass budgets. Otherwise sparse chains outrun dense ones on a healthy backlog and the rule false-alarms. Pin this with a test.
    4. sealLate must not sweep a lagging chain's rows into late checkpoints. They seal as window when the chain recovers.
    5. ADR line. One line goes into docs(adr): ADR-0154 amendments — bounded checkpoints (D4) and the DB session seal lock (D3) #33489 now, amending the D3 watermark bullet with a D9 cross-reference, and it gets a one-line re-review.
    6. Deferred. Two things go to a later amendment and are not part of this slice: a non-failing unsealed-lagging notice, and the residual case where no later window is sealed at all (single-chain deployments, or every chain failing).
  24. objectstack-fleet commented on Nov 14, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-elon-musk · session_016WV4cz2MxDkQYCC4U8indM

    os-dev-report

    {
      "issue": 31908,
      "slice": "P4b1",
      "round": "follow-up to seat ruling 6885452378 (two pins)",
      "status": "done",
      "branch": "claude/area-A2-31908-p4b1",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/33503",
      "head": "22d4da3f6",
      "session": "session_016WV4cz2MxDkQYCC4U8indM",
      "code_change_needed": "neither: both pins were green at first run; the only new commit (22d4da3f6) adds the two pins to audit-seal-sealer.test.ts",
      "summary": "(1) Window-major: the window pass is one loop over windows from the floor, sealing every due chain in each window and spending one shared MAX_WINDOWS_PER_PASS budget, counted per window that has a due chain. A chain trails the frontier only when it is skipped by a fault. Pin: org_a writes every hour for 30 hours, org_b only at hours 0, 11, 23 and 29. After pass 1 both chains' newest window checkpoint is at 23:00, after pass 2 both are at 29:00, and there are no late checkpoints. (2) No late sweep: a row is late only below its own chain's cursor, max(stored sealed_through floor, that chain's last window + 1h), and the floor is held at a skipped chain's window. Pin: org_x is skipped at 08:00 while org_a seals 08:00 and 09:00. In pass 2 org_x recovers into window checkpoints at 08:00 (seq 0) and 09:00 (seq 1). The only late checkpoint is org_a's own late row at 08:00, so the late pass did read that window.",
      "tests": "First run at 22d4da3f6, the sealer file: Tests 36 passed (36), VERDICT command-exit 0. Ablation 1 (scripts/ablation-replace.mjs): the late rule  was replaced by , which makes every chain late below the last window. The new no-late-sweep pin went red (AssertionError: expected 5 to be 3), as did both resume pins: Tests 3 failed | 33 passed (36), VERDICT command-exit 1. Restored: blob == HEAD (5e40b5178ea3), git diff HEAD empty. Ablation 2: the window pass's accept gained , a separate per-pass budget for org_b. The window-major pin went red (AssertionError: expected 25 to be 27): Tests 1 failed | 35 passed (36), VERDICT command-exit 1. Restored: blob == HEAD (5e40b5178ea3), git diff HEAD empty. Full plugin-audit suite at 22d4da3f6: Test Files 93 passed (93), Tests 1513 passed (1513), VERDICT command-exit 0.",
      "gates": "At 22d4da3f6: plugin-audit typecheck exit 0 (check:test-typecheck OK, 0 pinned); eslint --no-inline-config on the test file exit 0; check:nul-bytes exit 0. Only a test file changed, so the derived gate families are those of the first round. CI is not waited on.",
      "api_writes": "1: this comment on #31908 through post-stamped.mjs (relay). The PR body is not edited; the lines to change are handed to the coordinator.",
      "mcp_calls": "0",
      "security_findings": "none",
      "open_questions": [],
      "out_of_scope_findings": [
        "carrier: P4b2 · review#3 S1 (an unbounded re-walk from a held floor) is untouched here, as ordered"
      ]
    }
  25. objectstack-fleet commented on Nov 14, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-elon-musk · session_016WV4cz2MxDkQYCC4U8indM

    Correction to comment 6885542514, the P4b1 follow-up report. Its tests field lost the three mutation spellings: my shell swallowed the backtick spans when it wrote the file. In full:

    • Ablation 1: in sealLate, the accept start < this.cursorOf(chainKeyOf(tenantOf(row))) was replaced by start < to. That makes every chain late below the last sealed window, the old sweep. The no-late-sweep pin went red with expected 5 to be 3, and both resume pins went red too.
    • Ablation 2: the window pass's accept (row) => start >= this.cursorOf(chainKeyOf(tenantOf(row))) gained && (tenantOf(row) !== 'org_b' || n < 2), a separate per-pass budget for org_b. The window-major pin went red with expected 25 to be 27.

    Each was restored with blob == HEAD (5e40b5178ea3) and git diff HEAD empty. Everything else in that comment stands.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    domain:serverDomain lane — seat card indexed by label:pm:seatenhancementNew feature or requestlease:A2pm:blockingDerived cache from the Blocked-by reverse index: open card with open dependents — never hand-setpm: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