Repository navigation
feat(driver-sql,objectql)!: a tenant-scoped read no longer answers a row with no organization (18.x step 7, part 7a-1) - #24825
Conversation
…r retired; webhook activation door Claude-Session: https://claude.ai/code/session_01PDSxBZE561TFe1N2iEjKXZ Co-authored-by: Claude <noreply@anthropic.com>
…he 18.0 dual read; door pins for webhook Claude-Session: https://claude.ai/code/session_01PDSxBZE561TFe1N2iEjKXZ Co-authored-by: Claude <noreply@anthropic.com>
…asserts no row and a dispatch Claude-Session: https://claude.ai/code/session_01PDSxBZE561TFe1N2iEjKXZ Co-authored-by: Claude <noreply@anthropic.com>
…the checklist, the webhooks guide and the showcase; changeset Claude-Session: https://claude.ai/code/session_01PDSxBZE561TFe1N2iEjKXZ Co-authored-by: Claude <noreply@anthropic.com>
…tcher's switch read Claude-Session: https://claude.ai/code/session_01PDSxBZE561TFe1N2iEjKXZ Co-authored-by: Claude <noreply@anthropic.com>
…posture builds no wall, as its 18.0 seeded copy stamped with the Default Organization was Claude-Session: https://claude.ai/code/session_01PDSxBZE561TFe1N2iEjKXZ Co-authored-by: Claude <noreply@anthropic.com>
…red writes; the declared-webhook double is drivable by the limit gate Claude-Session: https://claude.ai/code/session_01PDSxBZE561TFe1N2iEjKXZ Co-authored-by: Claude <noreply@anthropic.com>
…does not implement Claude-Session: https://claude.ai/code/session_01PDSxBZE561TFe1N2iEjKXZ Co-authored-by: Claude <noreply@anthropic.com>
…authors; checklist and admitsOrganization docblock say a declared webhook is deployment-wide under single Claude-Session: https://claude.ai/code/session_01PDSxBZE561TFe1N2iEjKXZ Co-authored-by: Claude <noreply@anthropic.com>
…0g-webhooks-registry
…the fixture's customized declared-webhook mirror Claude-Session: https://claude.ai/code/session_01PDSxBZE561TFe1N2iEjKXZ Co-authored-by: Claude <noreply@anthropic.com>
…n fail on a double delivery Claude-Session: https://claude.ai/code/session_01PDSxBZE561TFe1N2iEjKXZ Co-authored-by: Claude <noreply@anthropic.com>
…0g-webhooks-registry # Conflicts: # content/docs/permissions/tenant-audit-census.mdx # docs/audits/2026-08-tenant-audit-write-call-sites.counts.md
…0g-webhooks-registry
…0g-webhooks-registry # Conflicts: # content/docs/permissions/tenant-audit-census.mdx # docs/audits/2026-08-tenant-audit-write-call-sites.counts.md
…-registry' into claude/issue-24581-ceremony-open
…NOT NULL door end to end
…he ceremony, then boot; boot-refusal-order step 2 back on the binary
…he checklist; changeset
…ap, docs and strictness-ledger artifacts
…el — the cipher names no organization Claude-Session: https://claude.ai/code/session_014UATXWqAXqkaZkbG92ox6V Co-authored-by: Claude <noreply@anthropic.com>
…sys_flow_credential leave reach) Claude-Session: https://claude.ai/code/session_014UATXWqAXqkaZkbG92ox6V Co-authored-by: Claude <noreply@anthropic.com>
…he 18.x rule plugin-sharing (rule sweep, record-share stamp, share-link stamp), service-storage (the upload doors' scoped writes) and driver-sql's read-back: an organization-less row is no tenant's, so no scoped read or write reaches it. Claude-Session: https://claude.ai/code/session_01DDkA1TNK1gGKSujgXiLLZQ Co-authored-by: Claude <noreply@anthropic.com>
…ssue-24583-7a1-driver-null-arm
…ssue-24583-7a1-driver-null-arm
…ssue-24583-7a1-driver-null-arm
📓 Docs Drift CheckThis PR changes 3 package(s): 13 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 7 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 26 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 46e8fb2d681b86eb8ba0ef482e810adee61650b3 && git checkout 46e8fb2d681b86eb8ba0ef482e810adee61650b3
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin c3a8e07b6aca321c0114f49944b0564baf0d2c0f 5124ae06c29791dff42e5517d9838f2a096fd1b7 && git checkout -B drift-repro c3a8e07b6aca321c0114f49944b0564baf0d2c0f && git merge --no-ff 5124ae06c29791dff42e5517d9838f2a096fd1b7
node scripts/docs-audit/affected-docs.mjs --json c3a8e07b6aca321c0114f49944b0564baf0d2c0f
|
Contract reviewServed-tier: Method: the diff ① Derived judgments1. The arms — correct. 2. The premise — holds by code reading. (a) 3. The kept NULL disjunct — right call; it must not retire. 4. The 5. Tests — every rewritten pin asserts the new rule; nothing weakened to green. 6. Clause-② and semver — 7. Merge hygiene — this part alone. ② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS |
…ssue-24583-7a1-driver-null-arm
Part of #24583 (18.x step 7, part 7a-1: the driver's NULL-organization arm). The card stays open for the other step-7 parts (7b-1, 7b-2, 7b-3).
Clause-②: no (narrowing)
Landing order. This branch started from
mainmerged withclaude/issue-24581-ceremony-open(#24681, steps 5+6), because it retires readers that only certification makes dead. #24681 has since merged (c6f775cbd), and this branch has mergedmainagain (307ce4c4b). The diff againstmainis this part alone: 19 files, +332 / −241, no governed path (check-governed-merges --branch: 0 of 19 on the register, under the 3,000-line line).What changes
SqlDriver.applyTenantScope(packages/drivers/driver-sql/src/sql-driver.ts). BothorWhereNullarms are gone. A tenant scope compiles toorganization_id = :tenant, ororganization_id IN (…)undergroup. A row with no organization is no tenant's (ADR-0131 D1, D14). With no tenant on the call, or no tenant column, the read stays unscoped, as before. The Turso remote face compiles the same chokepoint, so it follows by construction; only its two docblocks changed.readBackInsertedRows(same file). The docblock is restated. One consequence of the retired arm is handled in code: the rows' own tenant union scopes the read-back only when every written row carries a tenant. A batch with no tenant on the call that mixes stamped and unstamped rows would otherwise miss its own unstamped row after it landed, and the refusal would fire after the write. The ids are this call's own primary keys, so the read cannot reach a row this call did not write.LifecycleServiceglobal retention / archive pass (packages/objectql/src/lifecycle/lifecycle-service.ts). On anorganization_idpartition the{ [column]: null }disjunct is gone and the bare$ninstays.sys_audit_log.tenant_id, ADR-0131 D7). There, NULL is a live, deployment-level value and not a pre-ceremony one: on the ceremonied 18.0 fixture,sys_audit_log.tenant_idholds 14 NULL rows of 172.tenantWindowsFormakes the decision once, asTenantPartition.nullIsLive, so the passes name no column of their own (the federated-reader ledger's existingtenantWindowsForrow stays true).OR … IS NULL(buildDriverOptions's federated note, the reference-probe and master-header notes).Premise check: is every retired reader dead?
singleinstall (pnpm dev:crm -- --fresh), after seeded-admin sign-in and a REST createorganization_id; 0 NULL rows. The session carries an active organization.ceremoniedFixtureover the 18.0 fixture)organization_id; 0 NULL rows.sys_audit_log.tenant_id: 14 / 172 NULL (the kept attribution arm).isolatedos serve, real@objectstack/organizations)singleposture readsno-organization-object)applyTenantScopeexits before the arm. Shipped compositions:examples/embed-objectql(memory driver, which refuses tenant scope); everyos servecomposition withAuthregisterssys_organization(plugin-auth).Proof (head
2ee8e8d87, after the lastmainmerge)pnpm dev:crm -- --fresh -p <random>from a full build: health 200, seeded-admin sign-in 200,crm_accountcreate 201, list 200, 0 error lines.upgrade-18x-harness.dogfood.test.ts+organization-ownership-boot-refusal-order.dogfood.test.ts: 12 / 12.bin/run-dev.js serve),OS_TENANCY_POSTURE=isolatedwith the walled prerequisites, on a throwaway app that declares the real@objectstack/organizations. It reached the banner,Tenancy: isolated,Organizationsmounted, discovery 200, health 200, 0 refusals; the database it created holds 0 NULLorganization_idrows in 30 tables. Step 4's seven walled e2e cases (OS_TEST_TIERS=nightly): 7 / 7.typecheckexit 0 for objectql, driver-sql and driver-turso. Downstream: the 92 test files in 13 packages that reach an organization-less row through the real SQL driver, all green (1636 tests). The 5 among them that pinned the retired reach are rewritten here.scripts/ablation-replace.mjsat9987cf807, the same driver code as the final head; mutation landed and restored toblob == HEAD,git diff HEADempty). Re-addingorWhereNullon the equality arm turns 4 pins red insql-driver-tenant-scope.test.ts. Reverting the read-back guard turns the new mixed-batch pin red on the read-back face.node scripts/pm/dispatch-gates.mjs --commandson the final head: 78 commands. Each was run with its exit code captured before any pipe, and all 78 exited 0.--ranreconciliation: 78 derived, 78 run, 0 NOT-MEASURED (a derived zero: every line carries its exit code).Acceptance notes
readBackInsertedRows's scope guard (the claim named its docblock); it is the read-back consequence of the retired arm, and it is in the same function.packages/drivers/driver-turso/src/{turso-driver,remote-transport}.tschanged in docblocks only.engine.ts's dangling-reference audit docblock ("a NULL-organization row … probes unscoped") is unchanged. It describes that audit's own code, which this part does not touch.packages/runtime/src/domains/packages.tsstill says env-widesys_metadatareads areorganization_id IS NULL.sys_metadatahas carried no organization column since18.sys-metadata-organization-column-retired(systemFields: { tenant: false }), so the driver arm never applied there. Text drift, left for its owner.legacyrow was seeded withtenantId: 'org_A', soinjectTenantOnInsertstamped it and the old "stays reachable" pin was vacuous. It is now seeded with no tenant and asserted unreached.__global__(7b-3),docs/adr/**.Generated by Claude Code