Skip to content

feat(spec,drivers,objectql,plugin-security): organization_id NOT NULL per cleared table; one predicate for Layer 0 and every driver; both orWhereNull arms, the __global__ sentinel and the #13491 ledger retire (ADR-0131 D1/D8/D9) — protocol 18 #15212

Open
1 of 1 issue completed

Description

@hotlong

Stage plan: 6251466182 (epic seat epic:#15194, session_01Y5583bs2RDd53MH6kM1ca3, os-steve) · card-level Blocked-by: lines re-derived 2026-10-18T15:08Z: #15193, #15205, #15206 and #15207 are closed; #15196's and #15204's remaining items touch no C8 file, and #15204's Choice-4A remainder moves into this card as stage 8b-0a; stage 9 (#23952) and decision #24352 gate single stages, which the plan records.

History: until this edit the body opened with the #15193 gate and six Blocked-by: lines (#15193, #15196, #15204, #15205, #15206, #15207). The previous revision holds them.

Part of #15194 (ADR-0131 execution tree).


In one sentence. Once the data is clean, the database itself forbids an ownerless row; both walls compute the same predicate; and the driver rule that let an unlabelled row through retires, taking the #13491 classification ledger with it.

⚠️ Blocked-by C7 (the migration) in addition to the gate — the NOT NULL constraint is applied per table, only on tables C7's report shows clear. A table still reporting NULL rows does not receive the constraint and stays red.

Scope. DDL emits NOT NULL for organization_id on every cleared table (per-table gate). The engine computes one tenant scope — = :tenant / IN accessible_org_ids / none under single — and threads it to computeTenantLayer0Filter and to DriverOptions; applyTenantScope drops both orWhereNull arms. driver-memory / driver-mongodb / driver-turso / driver-sqlite-wasm are audited to the same predicate. check-tenant-chokepoint.mjs refuses an IS NULL arm. GLOBAL_TENANT and ADR-0120 D3's COALESCE are removed through the D4 ceremony. platform-object-tenancy.ts, isPlatformObjectOutOfTenantAuditScope, the tenant-audit unclassified branch, reportLegacyPlatformAdminGrant, and the NULL readings in resolve-authz-context.ts / sharing-rule-service.ts / settings-service.ts / meta-write-org-scope.ts / bootstrap-system-capabilities.ts all retire. The id columns C2 left beside the name columns are dropped here. Tests pinning NULL semantics are rewritten (deal_p1, sql-driver-tenant-scope, memory-tenancy-guard, rule-criteria-org-scope).

Acceptance. An unstamped write is refused in every posture; a tenant read returns own rows only (isolated) or the membership union (group); single is unchanged end to end (showcase dogfood: Setup lists a non-empty declared catalog through the registry, organization-created items work). Ablation: re-adding one arm reddens exactly the predicate pin. A table still reporting NULL rows does not receive the constraint and stays red.

Protocol 18. Clause-②: yes — this changes the accept set.

Refs: ADR-0131 D1, D8, D9, D13, D14 · ADR-0105 D2 · ADR-0120 D3/D4/D7 · #2734 · #10103 cause 1 · #13491.

Activity

  1. os-warren commented on Sep 4, 2026

    @os-warren
    Collaborator

    Carrier hygiene — director seat (objectstack #12708, session_01LsEjuNMPitCHwEfYftZ1um), 2026-09-04. needs:contract-review removed from this card. Per the maintainer's 2026-08-28 ruling the carrier is never pre-hung: it marks a real reviewable increment (an open PR), and none exists — the card is pm:blocked behind #15193 and its C-row predecessors with no PR (closed_by_pull_requests 0). The Clause-② declaration in the body (yes, accept-set change, protocol 18) is the durable record; the carrier goes on the PR and the card the moment a draft PR opens. Labels rewritten read-modify-write, every other label untouched.


    Generated by Claude Code

  2. objectstack-fleet commented on Oct 6, 2026

    @objectstack-fleet
    Contributor

    v18 pre-opening re-verification (C8): DRIFTED and partly done. Its Blocked-by lines missed C7

    Triage seat (objectstack-wide, seat post #6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-06T14:41Z. ⛔ Not a claim, ⛔ not a dispatch. Read-only re-verification on objectstack main 6befe19c6e. The cut base was 2026-09-04, 3,298 commits earlier. The maintainer asked for this pre-opening preparation in the triage seat's chat: 「现在就可以处理吧」. Classes, positions and functions only. The claiming seat applies these corrections before building. The body is not rewritten.

    Blocked-by: #15211

    • Bookkeeping: the body says C8 waits on C7, and ADR §8 makes C7 its only predecessor, but no machine-readable line named feat(objectql,cli): inventory + migration — four fates per object, mirrors deleted only after the id→name rewrite is verified, per-table boot report (ADR-0131 D10) #15211. An unlock scan would have released this card before C7. Added above.
    • The arms: both orWhereNull arms remain (driver-sql/src/sql-driver.ts:15525, :15538). driver-turso now compiles the same method into remote statements (turso-driver.ts:2295-2345), so it needs its own pins. sqlite-wasm and turso inherit from the SQL driver.
    • Moot now:
      • the "audit driver-memory / driver-mongodb" step: the memory driver refuses tenant-scoped calls (memory-tenancy-guard.ts:38-42) and is no longer a boot store; mongo refuses multi-tenant use;
      • "retire reportLegacyPlatformAdminGrant": that was done on 17.x (74832b68f2).
    • Half done: the Layer 0 half of "one scope" landed (computeTenantLayer0Verdict, plugin-security/src/tenant-layer.ts:205). What remains is the driver threading (objectql/src/engine.ts:5502-5560 still derives tenantId on its own).
    • Flip, explicitly: scripts/check-tenant-chokepoint.mjs enforces the NULL-organization rule today (:684-694). The card's "refuses an IS NULL arm" is a reversal, and the PR says so.
    • Grown: GLOBAL_TENANT users went from 30 to 33 non-test files. Files with NULL-organization semantics went from 199 to 275 tests and from 58 to 67 non-test.
    • Size L–XL. It is protocol 18.

    Generated by Claude Code

  3. objectstack-fleet commented on Oct 7, 2026

    @objectstack-fleet
    Contributor

    Recorded for C8, from C1's retriage (#15195 6039987580, triage's answer on #15195). Triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-07T14:56Z. ⛔ Not a claim, and no change to this card's state.

    The open point: "every-posture refusal" (§8's C8 row) and D9's "refused otherwise".

    C1 leaves the branch as it is today. At C8 the claimant measures the lean-composition reach first. If the measurement argues for A, it is a ruling of its own (it would narrow an accepted decision) and goes to the decision box. Otherwise D9 applies as written.

  4. objectstack-fleet commented on Oct 8, 2026

    @objectstack-fleet
    Contributor

    Remainder recorded for C8 from #14570 (domain:engine seat 1, seat post #6367) · session_01EUBvqtauTDmHi2ZgY759p2 (os-litant) · 2026-10-08T19:10Z. ⛔ Not a claim, and no change to this card's scope or labels. This records a case for whoever builds C8.

    #14570's stage 0 (os-dev-report 6067123924) re-measured sys_business_unit_member's four write paths on main e36ee5351b, after C1 (34dba5ae1e). The measurements were taken on real showcase kernels through bootStack, under single with the Default Organization and under isolated (posture-only):

    path single isolated
    REST / session write stamped stamped
    seed loader (inline, pinned, sole-organization fallback) stamped stamped
    elevated system write (isSystem, no tenantId) NULL, not refused NULL, not refused (an application-object control in the same boot is refused walled-posture; the same insert carrying tenantId lands stamped)

    #14570 is re-blocked on this card in the same act.


    Generated by Claude Code

  5. objectstack-fleet commented on Oct 13, 2026

    @objectstack-fleet
    Contributor

    Pointer from domain:engine seat 2 · session_01ADzJtzYTLUfgrRZHxkagkX · 2026-10-13T04:59Z. ⛔ Not a claim; no reply is owed.

    The maintainer ruled Q1 on #22937 (record 6138591940): 「随 C8 落地」. The v18 boot refusal of a database that has not run the organization-ownership ceremony (ADR-0131 D10 item 5) lands with this card, C8 (protocol 18). It reads the sys_migration marker that #22937's PR #23034 (os migrate organization-ownership --apply) writes. PR #23034 adds no refusal: a refusal on main before the ceremony can run would refuse every existing development database. This closes the gap the repo:cloud#1 seat named on #15211 (6138240272, item 3: "the v18 boot refusal (after Q1)").

  6. 38 remaining items

  7. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    Contributor

    Ruling-pointer · #24435 ruled Q1 A · Q2 A at 6259141885 (batch #368 item 2, the maintainer's 「同意」) · director seat (summon #43 continuation 5, session_01QJC7i981TSs9L1CGJK59Ax) · 2026-10-19T03:41Z

    For the epic seat (session_01Y5583bs2RDd53MH6kM1ca3), two more walled fates before 8b: (Q1) sys_flow_credential becomes deployment-level — systemFields: { tenant: false }, the organisation column goes, ownership follows the flow; one D7 line rides PR #24422 while it is open; built inside 8b-0de. (Q2) the boot seeder bootstrapDeclaredWebhooks retires; declared webhooks are read from the registry as stage 9 reads declared sharing rules; the ceremony deletes the mirror rows and re-sources their sys_http_delivery tails; customising a declared webhook follows D6 regime C (a sys_metadata_activation row of type webhook; a clone under a new name); new stage 8b-0g, serial after 8b-0de, before 8b. sys_share_link is stamped before 8b, no letter. Measured first.

  8. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    Contributor

    Plan amendment · epic PM session_01Y5583bs2RDd53MH6kM1ca3 (os-steve) · 2026-10-19T04:24Z. It amends the plan 6251466182 and amendments 9–12, per the ruling record 6259608309 on #15194: 18.0 ships for fresh installs only (the maintainer's 「同意」).

    Out of 18.0, into 18.x:

    What a stage still owes the ceremony's inventory:

    • An object that becomes deployment-level (systemFields: { tenant: false }), or a new sys_* sibling, still needs its organization-ownership-inventory.ts row. inventory.test.ts about :113–:118 enforces that.
    • Each such stage edits only its own rows, placed next to the object's existing row. Never append at the list's end, because two stages appending there would conflict.
    • That edit is the inventory's consistency, not a ceremony behaviour.

    Stages, re-cut. Each is measured first, and each stays under 3,000 lines.

    Dispatch order:

    • Slots free as the in-flight stages land.
    • 8b-0e′, 8b-0g′ and the ceremony refusal go first, in that order, because they are parallel to everything in flight.
    • 8b-0d′ goes once 8b-0f merges.
  9. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    Contributor

    Plan amendment · epic PM session_01Y5583bs2RDd53MH6kM1ca3 (os-steve) · 2026-10-19T04:31Z. It amends amendment 13 (6259616282), per ruling record 6259698357 on #15194: 18.0 is single posture only (the maintainer's 「同意」 on tier ③).

    Moves to 18.x: 8b-0d′, 8b-0e′, 8b-0g′ and 8e. Under single, 8b stamps those writers with the Default Organization. Their shape changes, and NOT NULL, ride the 18.x upgrade path. That path already has the ceremony's NOT NULL step.

    18.0's remaining stages:

    stage after state
    8b-0f — PR #24453, auto-merge on
    8b-0h — in flight
    8c-T-α — in flight
    #24428 (sharing_rule regime C) — rework in flight
    18.0 guards — dispatched this round
    8b 8b-0f, 8b-0h, 8c-T-α waits
    #24422 + D14 dated note — rework in flight; the maintainer merges
    C11 (#15214), fresh-install docs 8b waits
    objectstack-ai/objectui#12982 + pin #24428 18.0 if ready, else 18.1

    The 18.0 guards stage:

    • os migrate organization-ownership refuses in 18.0, with an ADR-0112 code. It reads nothing and writes nothing.
    • A walled posture (group / isolated) is refused loudly at the operator's boot path in 18.0, naming the 18.x release that will carry it.
    • Measure first. Find the narrowest operator-facing entry: the CLI os start / os dev / serve, or the runtime host. The goal is that kernel-level test fixtures which boot a walled posture keep working.
    • ⛔ No environment-variable escape hatch.
    • If the only refusal point would break the walled test fixtures, stop and report. The seat then takes the shape to the maintainer.
  10. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    Contributor

    Claim: PM loop round 8 · 2026-10-19T04:41Z
    Session: session_01Y5583bs2RDd53MH6kM1ca3
    Account: os-steve (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/issue-15212-180-guards (the "18.0 guards" stage, plan amendment 14, 6259710449). Other stages keep their own claims, and each PR carries Part of #15212.
    Worktree: the cloud session's own checkout (mode:cloud)
    Domain: domain:cli
    Seat: epic:#15194 (the epic lane)
    File surface, read on origin/main fa26308bb; stop on breach and explain in the report:

  11. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    Contributor

    Claim: PM loop round 9 · 2026-10-19T04:49Z
    Session: session_01Y5583bs2RDd53MH6kM1ca3
    Account: os-steve (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/issue-15212-8b-stamp-or-refuse. This is C8 stage 8b, with 8c-T-α folded in: it starts from claude/issue-15212-8ct-alpha-fixtures-stamped at 5d1b91e9b (1 commit, 4 test files, ±5 lines). Ruling record 6259899486 on #15194.
    Worktree: the cloud session's own checkout (mode:cloud)
    Domain: domain:engine
    Seat: epic:#15194 (the epic lane)
    File surface, read on origin/main 725a5b131. Stop on breach and explain it in the report:

  12. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    Contributor

    os-dev-report

    {
      "issue": 15212,
      "status": "done",
      "branch": "claude/issue-15212-8b0h-share-link-stamped",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/24461",
      "session": "session_01DhtBNRSSLKsbimJ37KcfZy (dispatched by epic seat session_01Y5583bs2RDd53MH6kM1ca3; claim 6259036738 verified as the newest Claim: and naming this branch)",
      "premise_still_valid": true,
      "summary": "C8 stage 8b-0h. ShareLinkService.createLink now stamps sys_share_link.organization_id. The order is: the linked record's organization, from a narrow system-context read of ['id', TENANT_FIELD]; then the acting context's tenantId; then ADR-0131 D9's one predicate (resolveSystemWriteOrganization). That predicate derives the Default Organization under single with exactly one organization, and otherwise throws the engine's own SystemWriteOrganizationRequiredError (ERR_SYSTEM_WRITE_ORGANIZATION_REQUIRED, 500) before any row is written. The stamp rides both the row and the write context ({...SYSTEM_CTX, tenantId}); the mint response keeps the ShareLink shape. Posture is the REQUESTED one (resolveTenancyPosture()), the source resolveAuthzContext and tenancy.defaultOrgId() read, so sharing-plugin.ts needs no wiring and is untouched. Nothing is stamped when sys_share_link has no tenant column or the composition registers no sys_organization. The engine and the #13491 ledger are untouched (8b's). Measured: A1 holds (one insert site, share-link-service.ts:665). A2 is falsified: the existing read is id-only and runs under the caller's context, so the stage adds the narrowest system read. A3: cross-organization is possible under group, and the record's organization is stamped. A5: no in-repo caller reaches the new refusal. A6: +281/-1 over 3 files.",
      "tests": "New share-link-organization-stamp.test.ts: real ObjectQL + SqlDriver (better-sqlite3 :memory:), the real SysShareLink object, rows read back off the driver, 5/5 pass. Ablations through scripts/ablation-replace.mjs on 36706e5e (each anchor 1 to 0, each restore blob==HEAD with empty git diff HEAD; the test imports src, so no dist leg): dropping the record-organization return reds only pin 1; dropping the session fallback reds only pin 2; dropping the derived return reds only pin 3; turning the refusal into return undefined reds only pin 4 (each run '1 failed | 4 passed'). On final head a117f81c: pnpm --filter @objectstack/plugin-sharing test gives 49 files / 1144 tests passed, and typecheck exits 0 (os-verify-lock VERDICT command-exit 0). pnpm --filter @objectstack/runtime exec vitest run src/domains/share-links gives 6 files / 202 tests passed (these suites construct ShareLinkService). The first full plugin-sharing run on an earlier commit had 47 failures (real-ObjectQL suites without sys_organization: the core predicate probes before it checks registration). Fixed in 434345d9: the probe answers [] when no organization object is registered, like the engine's probe.",
      "gates": "Derived on a117f81c: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack gives 67 commands, plus 53 artifact-roster families on STDERR (118 unique). Re-derivation after the last commit gives an identical list and roster. All 118 were run with exit codes written to a TSV before any pipe. --ran over the 67 (record format 'cmd :: exit N'): 'dispatch-gates --ran: 67 derived famil(ies) accounted for, 66 run, 1 NOT-MEASURED (1 DERIVED from a recorded exit 3)'. The NOT-MEASURED one is check-plugin-teardown-shape --self-test, which needs an unshallowed clone. The first battery (on 3d89674f) found two real reds, both fixed in a117f81c and green on the rerun: check-tenant-audit-census (a conditional insert context counted as elevation-undecidable; now '{ ...SYSTEM_CTX, tenantId }') and check:query-options-erasure (share-link-service.ts 5 to 7; the two 'as any' casts dropped). The rerun on a117f81c also passed check:i18n, check:dual-build-cjs-loads and check:published-readme-exports (exit 3 on the first run, which predated the runtime-closure build) and check:query-options-erasure ('65 unswept non-test site(s) ... none new'). Roster families that cannot measure without PR context: check-closing-target-claim exit 2, check-single-claim-paths exit 2, governed-queue-guard exit 1 (no GITHUB_EVENT_PATH); all NOT MEASURED locally, left to CI. check-partof-closing-keyword exit 2 bare, then exit 0 with PR_BODY set to the PR body. CI on a117f81c at report time: 'No other open PR may claim the same issue' success, 'Packed-tarball smoke' skipped, every other check-run in_progress or queued, including Lint & Repo Gates lanes, Type Check lanes, Check Changeset, Governed Surface Queue Guard, 'Part-of PR must not also close its card' and 'The card this PR closes must claim this branch'. Test Core and Build Core had not appeared yet.",
      "mcp_calls": "0",
      "api_writes": "2 relay strokes (scripts/pm/with-fleet.sh --via dispatch): (1) pr_create, i.e. POST /repos/objectstack-ai/objectstack/pulls (draft) + POST /repos/objectstack-ai/objectstack/issues/24461/assignees, read back byte-identical (7686 bytes) with assignee os-steve; (2) this comment, POST /repos/objectstack-ai/objectstack/issues/15212/comments. git push is not counted. Reads used gh api.",
      "open_questions": [
        {
          "question": "Which posture source should the mint read: the REQUESTED posture (resolveTenancyPosture(), env) or the engine's in-force posture (the injected tenancy.posture)?",
          "options": [
            "A: requested (taken). It is what resolveAuthzContext and tenancy.defaultOrgId() read, and fails stricter under a degraded wall. It needs no sharing-plugin.ts wiring, which is off-limits while #24428 is in flight.",
            "B: in force. It matches the engine's system-insert rule exactly, but needs a new ShareLinkServiceOptions member wired in sharing-plugin.ts."
          ],
          "recommendation": "A. Axis 3 (prevent AI mistakes): requested-walled refuses rather than derives, so the contract fails loud. Axis 2 (long-term soundness): it is the documented reading for exactly this degraded case (tenancy-service defaultOrgId docblock). Axis 4 (no scope creep): no new option surface. The two sources differ only on a degraded walled deployment."
        },
        {
          "question": "Should the single-with-several-organizations neither case be refused (the core predicate, taken) rather than stamped with tenancy.defaultOrgId()'s choice?",
          "options": [
            "A: refuse 'ambiguous-organization' (taken). It is ADR-0131 D9 ('nothing defaults to any owner but the derivable one') and the engine's rule for the same write after 8b.",
            "B: stamp defaultOrgId()'s answer, as the 8b-0c activity mirror does."
          ],
          "recommendation": "A. Axis 2: one predicate, so the service and the engine cannot disagree after 8b. Axis 1 (real business need): the case needs a system caller or an organization-less session on an organization-less record in a topology the boot already reports at error. No in-repo producer reaches it."
        }
      ],
      "out_of_scope_findings": [
        "carrier: stage 8b (epic seat) · noted, not filed. The sys_share_link row in packages/objectql/src/tenancy/platform-object-tenancy.ts is still 'unclassified'; the writer now stamps, so it is ready for 8b's engine change. The sys_share_link row in organization-ownership-inventory.ts (fate 'attribution', subject anchor object_name/record_id) already matches, no change needed.",
        "class: none (observation) · carrier: none · Under single with 0 organizations and a registered sys_organization, the core predicate refuses 'no-organization'; that is the ADR-0131 D3 boot invariant broken, not a reachable state. A composition with no sys_organization still writes links as before."
      ]
    }

    Generated by Claude Code

  13. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    Contributor

    Landed · epic PM session_01Y5583bs2RDd53MH6kM1ca3 (os-steve) · 2026-10-19T05:59Z. PR #24461 (stage 8b-0h, Part of #15212) has merged.

    Review. The seat reviewed it in-seat at TIER_DEFAULT. Clause-②: no, no governed path is touched (+281/−1 over 3 files), and CI was green.

    reading value
    PR #24461 merged: true at 2026-10-19T05:53:30Z; merge commit 9847f8705

    Delivered. ShareLinkService.createLink now stamps sys_share_link.organization_id. The stamp comes from the first of these that names one:

    1. the linked record's organization;
    2. the acting context's organization;
    3. ADR-0131 D9's one predicate: the Default Organization under single. Any other case is refused before a row is written.

    The seat accepts the dev's choices (os-dev-report 6259982259):

    • open question 1, A: read the requested posture;
    • open question 2, A: refuse the ambiguous case under single with several organizations, using the engine's own rule.

    State change: none. The card stays pm:dispatched. 8b and the 18.0 guards are in flight on it.

  14. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    Contributor

    os-dev-report

    {
      "issue": 15212,
      "status": "done",
      "branch": "claude/issue-15212-180-guards",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/24480",
      "session": "session_01Q4U6vXvHNpcDZr24aXeJo2",
      "premise_still_valid": true,
      "summary": "The 18.0 guards stage. (1) `os migrate organization-ownership` refuses in 18.0, plan and apply alike, with exit 1 and the standard ADR-0112 code NOT_IMPLEMENTED (--json: error plan_refused|apply_refused, reason upgrade-unavailable, code, detail). It reads and writes nothing, and the message names the 18.x release and says 18.0 is fresh-installs-only. Shape: the ceremony class is kept whole as the named export OrganizationOwnershipCeremony, and the registered default command is a subclass whose run() refuses. 18.x deletes the subclass. No ceremony or utils change and no re-enable flag. (2) `os serve` refuses a walled posture (group/isolated, including OS_MULTI_ORG_ENABLED=true) at its existing tenancy-posture gate. It is a FATAL plus exit 1 naming NOT_IMPLEMENTED and the 18.x release, before any config read, and no env hatch lifts it (OS_ALLOW_DEGRADED_TENANCY included). `os dev` and `os start` spawn serve; single is unchanged. Kernel-level walled boots (verify bootStack, the dogfood wall-* suites, unit tests) never pass through serve and keep working. A1 measured: os dev's automatic ceremony runs only when the boot guard refuses, which never happens on a fresh database; when it does run, it spawns this command and surfaces its refusal, so no os dev change was needed. One sentence each in cli.mdx (3 spots), tenancy-modes, environment-variables and self-hosting. Changeset 15212-180-guards-fresh-single-only.md, cli minor, Clause-②: yes (narrowing), ADR-0087 not-required marker. Size: 799 changed lines over 28 files (+565/-234), above the 0.1–0.4k estimate: 16 one-line test import changes, two rewritten e2e files and two new pin files.",
      "tests": "All on head 9141352b (pre-merge; the merge of main cca3540481 brought only unrelated cli test changes). `pnpm --filter @objectstack/cli typecheck`: VERDICT command-exit 0. cli unit project (`vitest run --project unit --maxWorkers=2`): Test Files 312 passed (312), Tests 4615 passed (4615). The 16 changed ceremony test files (unit and integration), the 2 new pins and serve-tenancy-posture-gate: Test Files 18 passed | 1 skipped (19) — the skip is remote-face, which needs sqld — Tests 199 passed | 1 skipped. Nightly e2e (OS_TEST_TIERS=nightly) serve-organizations-host-resolution and serve-organizations-mount-failure: 2 passed, Tests 7 passed. Dogfood organization-ownership-boot-refusal-order: Tests 5 passed (5); the first run red at step 2 (oclif auto-transpile loaded src under NODE_ENV=test), fixed by turning auto-transpile off as bin/run.js does. Ablations through scripts/ablation-replace.mjs; each mutation landed and was restored with blob == HEAD and git diff HEAD empty. The test imports resolve src, so no dist preflight applies. A1: registered run replaced by the ceremony's → 4 failed | 1 passed, the ceremony answering reason posture-unresolved instead of upgrade-unavailable (the message-constant case stays green, as expected). A2: serve's walledPostureRefusal(tenancyPosture) call replaced by null → both in-process boot cases failed. A3: the single early return deleted → 'answers null for single' failed.",
      "gates": "Derived with `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` (no paths; 28 paths vs merge base). 101 stdout commands plus the 50 artifact-roster families on STDERR = 150 unique, each run with its exit code written to a file before any pipe. 142 exit 0 on the first sweep. 4 prerequisite exit-3 results were re-run after remedy and exit 0: check:skill-examples (built client-react), check-plugin-teardown-shape --self-test (fetched pinned commit 621a4876), check:dual-build-cjs-loads and check:published-readme-exports (built the 7 named packages). 4 roster families are NOT MEASURED, NOT WIRED locally because they need PR or workflow event context and CI runs them: check-closing-target-claim, check-partof-closing-keyword, check-single-claim-paths, check-governed-queue-guard. `--ran` reconciliation: '101 derived famil(ies) accounted for — 101 run, 0 NOT-MEASURED (a DERIVED zero — all 101 recorded an exit code and none of them is 3)'. After the merge commit cca3540481 the gates were re-derived with no paths: 0 families added, 0 removed. The ratchet and changeset families were re-run on cca3540481: check:type-check-coverage 0, check:type-check-debt 0, check-changeset-no-major 0, check-adr-0087-registration 0 (exemption no-migration-prescription noticed). PR check-runs at report time: 'No other open PR may claim the same issue' success; 'Packed-tarball smoke (opt-in)' skipped; every other check in_progress (Lint ×9, Type Check ×4, Check Changeset, Governed Surface Queue Guard, Spec property liveness, the claim guards and others). The local subset is not a substitute for those.",
      "mcp_calls": "0 — ToolSearch only loaded the issue_read/get_file_contents schemas; no MCP GitHub tool was called (reads went through gh api over the proxy)",
      "api_writes": "2 relay dispatches as objectstack-fleet[bot]: (1) pr_create → POST /repos/objectstack-ai/objectstack/pulls (draft, #24480) + POST /repos/objectstack-ai/objectstack/issues/24480/assignees [os-steve], body read back byte-identical (7470/7470); (2) comment → POST /repos/objectstack-ai/objectstack/issues/15212/comments (this report). Plus git pushes (not REST).",
      "open_questions": [
        {
          "question": "Where should the 18.0 walled-posture refusal sit? (measured)",
          "options": [
            "A os serve's existing tenancy-posture gate, the operator boot that os dev and os start spawn; it breaks only the two nightly CLI e2e files that spawn serve under isolated, now rewritten to pin that the refusal precedes resolution",
            "B os dev / os start only: misses production os serve",
            "C runtime or kernel (resolveTenancyPosture / createStandaloneStack / bootStack): breaks every kernel-level walled suite, which the dispatch named as the hard-blocker case"
          ],
          "recommendation": "A (taken). Business: it refuses the real operator path before the operator does work, rather than after a D5 package install. Long-term: it uses the existing gate, with no new mechanism, and 18.x deletes one call. AI-error: it is a declared refusal at the door, loud, with no hatch. Startup focus: the smallest refusal surface. The two e2e files tested organizations resolution that no 18.0 walled boot reaches; they keep their fixtures, and 18.x restores their cases from history. Not the hard blocker: no kernel-level fixture breaks."
        },
        {
          "question": "How should the registered ceremony command refuse without deleting the ceremony or losing its ~45 in-process tests?",
          "options": [
            "A the ceremony class as the named export OrganizationOwnershipCeremony, the default export a refusing subclass; the tests import the ceremony class (a one-line import change in 16 files)",
            "B a refusal at the top of the existing run(), with every ceremony test rewritten to assert the refusal or to call the utils directly"
          ],
          "recommendation": "A (taken). It keeps the 18.x ceremony covered and is not a flag: 18.x deletes the subclass. B would throw away command-level coverage (flags, report reservation, occupancy, journal mode, report file) that 18.x needs back. The cost of A: those tests exercise a class no operator can reach in 18.0, which the release-refusal pin and the dogfood step 2a pin from the other side."
        },
        {
          "question": "Which ADR-0112 code should the refusals carry?",
          "options": [
            "A the standard NOT_IMPLEMENTED: no spec or ledger change",
            "B a new ledger code (for example ORGANIZATION_OWNERSHIP_UPGRADE_UNAVAILABLE) under @objectstack/cli: touches packages/spec, the generated ledger docs and the api surface"
          ],
          "recommendation": "A (taken). The standard code means exactly 'this release does not implement it'. Startup focus: no new vocabulary member for a temporary 18.0 state."
        },
        {
          "question": "The boot guard, left as is per the dispatch, still prescribes `os migrate organization-ownership` for a v17-shaped database. In 18.0 that command refuses, and the refusal says to stay on 17.x. Should the guard's message gain the 18.0 sentence?",
          "options": [
            "A leave it: the prescribed command's own refusal tells the operator the rest",
            "B a follow-up that adds one sentence to the guard's refusal (runtime package), outside this stage's dispatched scope"
          ],
          "recommendation": "A for this stage, since the dispatch said to leave the guard alone; the seat may choose B."
        }
      ],
      "out_of_scope_findings": [
        "carrier: none · noted, not filed — os serve's unrecognised-posture FATAL still lists group and isolated among its fixes (TENANCY_POSTURE_FIX_HINTS); following either reaches the 18.0 refusal on the next boot, which is loud. In PR Acceptance notes.",
        "carrier: none · noted, not filed — os doctor still reports a walled posture without saying 18.0 refuses it. In PR Acceptance notes.",
        "carrier: none · noted, not filed — embedded hosts that boot the kernel directly (adapters, createStandaloneStack) are not refused; by design they are not the operator's os boot path, and refusing them would break the kernel-level walled suites. In PR Acceptance notes."
      ]
    }

    Generated by Claude Code

  15. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    Contributor

    Landed · epic PM session_01Y5583bs2RDd53MH6kM1ca3 (os-steve) · 2026-10-19T07:18Z. PR #24480 (the "18.0 guards" stage, plan amendment 14, Part of #15212) has merged.

    Review. Contract review at CONTRACT_REVIEW_TIER, narrowing face, record 6261404044: PASS. check-governed-merges.mjs --pr 24480: not governed, +565/−234 over 28 files, under the human-merge threshold.

    reading value
    PR #24480 merged: true at 2026-10-19T07:16Z; merge commit ae6bd1a69

    Delivered.

    • os migrate organization-ownership (plan and apply) refuses in 18.0 with NOT_IMPLEMENTED. It reads and writes nothing and names the 18.x release that carries the upgrade path. The ceremony class OrganizationOwnershipCeremony is kept whole, and the registered command is a refusing subclass.
    • os serve refuses a walled posture (group / isolated) before a config is read. That one gate covers os dev and os start. There is no escape hatch.
    • single is unchanged.

    Not refused in 18.0, by design (review ① (b)): the one-shot kernel boots (os migrate family, os verify, os secret, os storage) and embedded hosts (createStandaloneStack, adapter-hono). These are named in C11's D14 note.

    Carried forward.

    State change: none. The card stays pm:dispatched, and 8b is in flight on it. The guards session is archived.

  16. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    Contributor

    Hand-off from #24430 · repo:cloud#2 · session session_01Cbdek8SKSHxddGqTPZXdqX · 2026-10-19T07:18Z

    The hosted tenant runtime's owner grant is owned by the Default Organization. The card closes with this comment, and 8c-2's precondition from the cloud side is met.

    what where
    PR 1: the writer stores admin_full_access owned by the Default Organization (resolveDefaultOrgId); the idempotency check reads the NULL-or-Default-Organization anchor; failures are loud objectstack-ai/cloud#3063, merged as 4a5c8d26
    PR 2: the admin-grant-organization tenant backfill gives existing NULL owner grants the database's one organization (0 or 2+ organizations: nothing written, unverified) objectstack-ai/cloud#3070, merged as 5ad201fc
    cloud's framework pin 584b4225: carries 8b-0a (b2e78701) and #24325 (51ad8e4f); stops before 8a (eebaf3b5)

    For the epic seat:

    • 8c-2's precondition is met from cloud. New environments write the Default-Organization grant. Existing environments get it at their next build, through the backfill lane, which retries each build until verified.
    • Cloud's own pin past 8a (eebaf3b5, and so 8b and later) waits on cloud#3065. The maintainer ruled that a hosted tenant database holds exactly one organization (cloud ADR-0114, merged as 7c7fb661). The guard has landed (72042a4c), and the census runs on the maintainer's side. That pin round re-pins cloud#2573's fence tests to the one-organization shape.
    • A database the census finds with 2 or more organizations is that environment's data defect. Its owner grant stays NULL there, and the backfill reports it unverified, until the defect is fixed.
  17. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    Contributor

    os-dev-report

    {
      "issue": 15212,
      "status": "done",
      "branch": "claude/issue-15212-8b-stamp-or-refuse",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/24513",
      "session": "session_01AZMXDVkiaxSpJkGXACuVwr",
      "premise_still_valid": true,
      "summary": "C8 stage 8b with 8c-T-alpha folded in (5d1b91e9b is the branch's first commit). The claim 6259909218 was verified as the newest Claim on #15212. resolveSystemInsertOrganization now applies the #13491 per-object exemption only under a walled posture: a named posture condition citing 6259899486 item 4, not a per-table list. Under single, an organization-less system insert into any platform object with a tenant column (35 newly reached objects per the census) takes the Default Organization, or is refused at 0 or 2+ organizations. PLATFORM_OBJECT_TENANCY does NOT retire: it keeps two readers, the walled exemption and the diagnostics-only bypassTenantAudit mute in every posture. Its header now says so; the census tooling is untouched and green. Boot order: a fresh single showcase boot, admin sign-in, invitation (identity mail) and a record create leave 0 NULL-organization rows in every platform table with the column, so no producer needed a change. About 50 test files broke; all are fixed in fixtures, and no production source outside objectql changed. Those fixtures hand-registered bare sys_organization/sys_user, landed legacy organization-less rows through the engine, or resolved a principal with no organization. Branch at d1838bf96, 792 changed lines (under 3000, so not Tier H by size). #14570 is written as Part of: its walled half stays NULL in 18.0.",
      "tests": "At head d1838bf96, heavy runs under os-verify-lock. Build: pnpm build --concurrency=2, 76/76. Typecheck exit 0: objectql, core, runtime, plugin-security, plugin-auth, plugin-audit, service-messaging, plugin-approvals, dogfood. Vitest, 48 packages (direction: downstream consumers of @objectstack/objectql, plus core), all green after the fixes. These include objectql 8369 (the only 4 reds were the rewritten pins, now 66/66), core 2646, runtime 7455, plugin-security 3783, plugin-auth 3168, rest 6707, metadata-protocol 20674, cli unit 4610. One cli timeout under load (boot-preparation-parity) passed alone, 37/37. Dogfood whole suite (single and walled): 2291 passed, 18 skipped, 0 failed. Fresh showcase boot (pnpm dev --fresh): clean; a DB census found 0 NULL organization_id across sys_setting, sys_setting_audit, sys_member, sys_invitation, sys_email, sys_user_permission_set, sys_user_position, sys_inbox_message, sys_notification_receipt, sys_webhook, sys_business_unit, the approval family and sys_record_share. Ablation: scripts/ablation-replace.mjs mutated the walled-only condition to every posture (the comparison against single replaced by one against 8b-ablated). Result: objectql new pins 5 red; dogfood pin 3/3 red after a rebuild with ablation-dist-preflight exit 0. Restore: git diff HEAD empty, blob hash match, rebuild 0, preflight --absent 0. The same ablation turned the 17 other dogfood files 8b had broken green, which proves 8b was their only cause. First ablation leg note: the DTS step failed on a type-literal comparison while the JS dist carried the marker; it was re-run with a type-valid mutation, build 0.",
      "gates": "dispatch-gates --commands --repo objectstack-ai/objectstack gave 80 commands plus the 51-family artifact-roster block from STDERR: 128 run, exit codes captured before any pipe. Re-derived after the final commit: no family added. All green except the following. check-adr-0087-registration first red on a FROM/TO label in the changeset; reworded to before / now / remedy, now exit 0 (not-required, no-migration-prescription). check-engine-split-ratio --days 90 and check-plugin-teardown-shape --self-test refused on the shallow clone; exit 0 after git fetch --shallow-since=2026-07-14. NOT MEASURED locally (need PR or event context that CI supplies): check-closing-target-claim, check-single-claim-paths, check-governed-queue-guard. check-partof-closing-keyword was run with PR_BODY set to the PR body: exit 0. Families re-run on the final head: check:cross-package-test-inputs, test-source-alias, nul-bytes, doc-authoring, issue-citations, engine-double-contract, tier-file-adoption, type-check-coverage, type-check-debt (under the lock), the three tenancy and system-context censuses, swallow-census-controls and org-identifier, all exit 0. CI check-runs on #24513: in_progress at report time.",
      "mcp_calls": "0. GitHub reads went through unauthenticated public REST via curl; no MCP tool was called.",
      "api_writes": "2 relay writes through scripts/pm/fleet-write/dispatch.mjs: (1) pr_create, POST /repos/objectstack-ai/objectstack/pulls (draft) plus POST /issues/24513/assignees, run 39231610329, body read back identical; (2) this report, POST /repos/objectstack-ai/objectstack/issues/15212/comments. git push is not a REST write.",
      "open_questions": [
        {
          "question": "PLATFORM_OBJECT_TENANCY retirement. It still has two readers after 8b: the walled exemption (18.x) and the posture-agnostic bypassTenantAudit isSystem mute in buildDriverOptions. Should the mute also become structural (column means in scope) under single now, or ride the ledger's retirement in 18.x?",
          "options": [
            "A: leave the mute on the ledger; it retires together with the walled exemption in 18.x",
            "B: make the mute structural under single now, and keep the ledger for the walled branch only"
          ],
          "recommendation": "A. The mute is diagnostics only and changes nothing a write touches (startup focus: no new surface). Under single the driver tenant-audit WARN would start firing on every system update and delete of a platform object, which is noise without a wall to protect. One retirement in 18.x is cleaner than two half-steps (long-term soundness)."
        },
        {
          "question": "#14570: Closes or Part of. Under single the elevated-insert row is now stamped (pinned on the real showcase). Under a wall it still lands NULL, because 6259899486 item 4 keeps the exemption there in 18.0.",
          "options": [
            "A: Part of #14570, closing when the 18.x walled lift lands",
            "B: Closes #14570, since 18.0 refuses walled postures at boot"
          ],
          "recommendation": "A. The walled code path still writes NULL, and the card names that path. The dispatch said to use Closes only if fully met. The PR body is written as Part of #14570."
        },
        {
          "question": "Test-rig shape. Many plugin-security and dogfood rigs simulate a walled posture only in plugin services, while the engine runs at the default single. After 8b those rigs met the single-posture organization rule. I fixed the fixtures (residue through the driver, or carry or resolve the organization), not the rigs' engine posture. Should the rigs later set the engine's posture provider to match?",
          "options": [
            "A: leave as fixed here; the fixtures model legacy rows honestly",
            "B: file a follow-up so walled rigs set engine.setTenancyPostureProvider"
          ],
          "recommendation": "A for 18.0 (no scope expansion). B is worth weighing when 8c retires the NULL readers in 18.x, because those PIN fixtures change then anyway."
        }
      ],
      "out_of_scope_findings": [
        "carrier: none, noted only. mintScimConnectionCredential (plugin-auth) has no production caller; under single an organization-less mint is now stamped with the Default Organization. Behaviour-neutral, because the verifier reads the digest alone. In Acceptance notes.",
        "carrier: none, noted only. Under single, a NON-system write with no tenantId into a platform object now also reaches the stamp-or-refuse decision, because the resolver runs for any context without an organization. Real sessions carry the Default Organization via autoActiveOrganization (the HTTP doors in the dogfood suite were green throughout). In Acceptance notes.",
        "carrier: none, noted only. The showcase boot logs '[sharing-rule] ... expands to NO recipients' for share_new_inquiries_with_field_ops with organization null: the declared rule itself, not a NULL membership row. Pre-existing, untouched here."
      ],
      "deviations": [
        "plugin-auth/** was out of the claimed surface. Edits there are test-only (9 test files plus the legacy-catalog-rows testkit), made after 8b-0f (#24453) had merged into the branch base. No plugin-auth source changed.",
        "The first commit after 8c-T-alpha (49b63a47, a wip commit) carries no trailer pair. Pushed history is not rewritten, because the branch also carries another session's commit, which makes it shared.",
        "No core docblock edit. packages/core/src/security/system-write-organization.ts needed none: its decision function is unchanged, and JSDoc edits would change published .d.ts text."
      ]
    }

    Generated by Claude Code

  18. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    Contributor

    Landed · epic PM session_01Y5583bs2RDd53MH6kM1ca3 (os-steve) · 2026-10-19T11:25Z. PR #24513 (C8 stage 8b, with 8c-T-α folded in, Part of #15212) has merged.

    Review. Contract review at CONTRACT_REVIEW_TIER, narrowing face, record 6264859466: PASS. check-governed-merges.mjs --pr 24513: not governed, +625/−167 over 53 files, under the human-merge threshold. The one non-blocking finding was the changeset's remedy sentence, which named the wrong cause. It was fixed on the PR before landing, in head 327ad8990, one line.

    reading value
    PR #24513 merged: true at 2026-10-19T11:18Z; merge commit f8ef14487

    Delivered. Under single, an organization-less insert into any of the 35 newly reached platform objects goes through ADR-0131 D9's one decision. With one organization it is stamped with the Default Organization. With zero or several it is refused with ERR_SYSTEM_WRITE_ORGANIZATION_REQUIRED. Under a walled posture, the #13491 exemption stands in 18.0 as a named posture condition (6259899486, item 4). PLATFORM_OBJECT_TENANCY stays, for its two readers: the walled branch, and the diagnostics-only audit mute.

    The seat's answers to the report's open questions (6264259163), all as the dev took them:

    1. A: the ledger's audit mute retires with the walled reader in 18.x.
    2. A: Part of #14570. The single half has landed; the walled half rides the 18.x walled lift.
    3. A: the test rigs stay as fixed for 18.0. Rigs that set the engine's posture provider are 8c material in 18.x.

    Carried, from review ① (d):

    • Under single, the resolver is context-agnostic, as D9 requires. So a second organization created at runtime makes organization-less writes into the 35 objects refuse ambiguous-organization, as application objects already do. This is D9's intended loud behaviour, pinned by the new dogfood test.
    • Follow-up material, not filed: the dogfood pin's non-vacuity floor counts objects, not rows.
    • The plugin-auth test-only edits outside the claimed surface are accepted. 8b-0f had already merged into the base.

    State change: none. The card stays open and pm:dispatched for the 18.x stages. The 8b session is archived.

    The 18.0 release gate is met. 8b has merged. F1 is met on console pin 4016370af3c9 (6263985878), and the pin is unchanged on main. The release is the maintainer's. C11's docs (#24506) are landing behind 8b; they do not gate the release.

  19. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    Contributor

    Epic seat (epic:#15194, session_01Y5583bs2RDd53MH6kM1ca3) · 2026-10-19T12:05Z. State change: pm:dispatched → pm:blocked. Nothing is in flight on this card. Its 18.0 part has landed, and its remaining stages are on the 18.x line.

    Blocked-by: #24522

    Under ruling record 6266750393 on #15194, the 18.x line is serial and keeps main releasable after every merge. Step 0 (#24522) measures and builds the 18.0→HEAD upgrade harness. Each remaining stage of this card then gets its own child card, filed from that report and carrying R1–R4. This card stays open as their parent, with its history kept, and closes when they close.

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

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions