Repository navigation
fix(spec,plugin-sharing,scripts): retire the organization-less text step 7 left behind; check-tenant-chokepoint accepts where(tenant, 'is not', null) - #25140
Conversation
…he sharing-rule organization help Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Pjh5f11tyUbYMQmSZaJZFS
… null), as its header says Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Pjh5f11tyUbYMQmSZaJZFS
Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Pjh5f11tyUbYMQmSZaJZFS
Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Pjh5f11tyUbYMQmSZaJZFS
📓 Docs Drift CheckThis PR changes 2 package(s): 2 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 2 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 144 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 00f8f272c71b4bda89d211e9db6757712b2ff3ab && git checkout 00f8f272c71b4bda89d211e9db6757712b2ff3ab
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 1b3b65e643bf31d2cb4764dbfc18a04234194bbf 02067f8ceb0ebc2c75ec4f576055b4948a8c0a17 && git checkout -B drift-repro 1b3b65e643bf31d2cb4764dbfc18a04234194bbf && git merge --no-ff 02067f8ceb0ebc2c75ec4f576055b4948a8c0a17
node scripts/docs-audit/affected-docs.mjs --json 1b3b65e643bf31d2cb4764dbfc18a04234194bbf
|
|
Seat ACCEPT · epic PM Seat check
|
Fixes #24902
Clause-②: no (description and help text, plus a CI gate's rule: no response, status, outcome, boot verdict or export moves)
Metadata-protocol: none (a schema description and a field's help text; nothing a metadata author writes changes)
What changed
1. Text that contradicted behaviour after 18.x step 7
packages/spec/src/api/events.zod.ts: theDataEvent.organizationIdTSDoc and.describe(). They said every event on asingledeployment carries no organization because "nothing stamps the column". Under ADR-0131 D9, every tenant row on a single-organization deployment is stamped with the Default Organization the engine derives (resolveSystemInsertOrganization,packages/objectql/src/engine.ts), so its events carry it. The text now matchescontent/docs/protocol/objectql/schema.mdx. Absence is described as two cases: a row of an object outside the wall, and every row of a composition that registers no organization object. The schema itself is unchanged.content/docs/references/api/events.mdxwas regenerated (check:generated --fix, then a re-check: 14/14 up to date).packages/plugins/plugin-sharing/src/objects/sys-sharing-rule.object.ts: the Organization help said "null = global". It now reads "The organization this rule belongs to.", which follows this object's admin-facing rule (no tables, columns or ADRs in adescription). A code comment above it points to the typeddeploymentWideprojection (sharing-rule-service.ts). Theenbundle was regenerated withnode scripts/check-i18n-bundles.mjs --write. The zh-CN, ja-JP and es-ES values are hand-written and were updated to match, because merge mode keeps translated values and they would otherwise still say "null = global".2. The gate nit:
scripts/check-tenant-chokepoint.mjsIS NOT NULLis never refused. The rule refused every three-argumentwhere(tenant, …, null), includingwhere(tenant, 'is not', null). Knex compiles that call toIS NOT NULL:knex/lib/query/querybuilder.js:456-457, operator lower-cased and trimmed.'is not'operator, matched the way Knex matches it. An operator the gate cannot read is still refused. The header names all threeIS NOT NULLspellings.'is not'and' IS NOT ') and one new refused case (a non-literal operator). TheD8 null-arm rulebattery floor goes from 22 to 24.true, and the self-test went red (exit 1) on the new silent case. The file was restored withgit checkout HEAD --, andgit diff HEADwas empty afterwards.3. The dormant paths: both are KEPT, with reasons. Neither is retired.
organizationOutOfWriteReach(packages/services/service-storage/src/metadata-store.ts:800) is alive:storage-routes.ts:780, insiderequireStartingOrganization. Three upload doors use it: the presigned commit door, chunked completion, and the progress door when its expiry stamp is due. It is the guard behind the409an uploader gets after switching organization. It has a door-level pin inerror-envelope.conformance.test.ts:226-245(the commit door after an organization switch) and unit pins inmetadata-store.test.ts:368-405. Probe:vitest run src/error-envelope.conformance.test.ts src/metadata-store.test.ts -t 'switching active organization|organizationOutOfWriteReach'gives 2 files, 7 passed. service-storage is not in this diff, so CI does not run it.metadata-store.ts:809) was written against the retired NULL arm. That branch can still run. The v18 boot guard is composed byos servealone (packages/runtime/src/organization-ownership-boot-guard.ts, "Where it runs"). An embedding host that does not compose it can boot a database that has not been through the ceremony and still holds a v17 organization-lesssys_file/sys_upload_sessionrow.The
sys_record_sharebackfill apply pass (packages/plugins/plugin-sharing/src/backfill-sys-record-share-organizations.ts) is dead in every shipped build, but has a published reach:backfill-sys-record-share-organizations.test.tsandrecord-share-organization-stamp.test.ts:315.src/index.tsalone,filesisdist, and the index does not export it.sys_record_sharetable created by the 18.x driver with no relax, storing an organization-less grant is refused (NOT NULL).planSysRecordShareOrganizationBackfillanswers{scanned: 0, planned: 0}, and the apply pass writes 0. The existing real-driver pin can only build its legacy rows because it first relaxes the column (record-share-organization-stamp.test.ts:180).content/docs/releases/v17/17-3.mdx:44-48, 284-289) tell operators to runplanSysRecordShareOrganizationBackfill/runSysRecordShareOrganizationBackfill(engine, { dryRun: false }). The CLI ceremony inventory cites the file as its derivation (packages/cli/src/utils/organization-ownership-inventory.ts:538). Retiring it also moves two ledgers outside this claim's file surface:scripts/engine-double-contract.pinned.json:3670and the tenant-audit census counts. Per the dispatch, this is the seat's call.Acceptance notes
organizationOutOfWriteReachanswers "in reach". Since D8, the driver's scoped write cannot reach such a row, so on a host without the boot guard the door writes, misses, and answers the500outage text. Option A: keep it as it is; that state only exists on a database that has not been through the ceremony. Option B: make the door refuse such a row with its own answer. That needs a new refusal text, and it touchesstorage-routes.ts, which is outside this claim. I recommend A, and noting it for docs(adr): ADR-0131 dated notes for the 18.x line — release scope, the D9 lean-embedding exception, D1 at createTable, D2 deployment-wide declared rules (Tier H) #24897's ADR text.sys_record_sharebackfill or keep it. Option A: keep it until the 17.x upgrade window is formally closed. Option B: retire the module, its two tests and the two ledger rows now, and add an erratum PR for the 17.3 notes. I recommend A.packages/platform-objects/src/**/sys-sharing-rule.object.ts. The file is actually atpackages/plugins/plugin-sharing/src/objects/. I edited it there.storage-routes.ts:761) "a row stamped with no organization stays in reach" has the same staleness as the branch above. It is outside the file surface, so I left it.Verification
pnpm --filter @objectstack/spec test: 706 files, 21465 tests passed.pnpm --filter @objectstack/plugin-sharing test: 53 files, 1200 passed.pnpm --filter @objectstack/plugin-sharing typecheck: exit 0.pnpm --filter @objectstack/spec check:generated: exit 0.node scripts/check-tenant-chokepoint.mjs --self-test: exit 0, 12 refused and 7 silent. The scan itself exits 0.node scripts/pm/dispatch-gates.mjs --commands: 130 commands. 130 commands on head02067f8c, each exit code captured before any pipe: 127 exit 0 on the first pass. Three exited 3 (PREREQUISITE NOT MET, nothing measured).check-plugin-teardown-shape --self-testneeded a pinned commit the shallow clone lacked; after fetching it, exit 0.spec check:skill-examplesneededclient-reactbuilt; after building it, exit 0.check:dual-build-cjs-loadsneeds ten packages this diff does not touch (studio, client-react, connector-slack and others) built, so it is NOT MEASURED locally and declared to CI.--ranreconciliation: 130 derived, 130 run, 0 unrun.node scripts/pm/check-governed-merges.mjs --branch origin/claude/issue-24902-tenant-text-dormant-paths: NOT governed, 95 changed lines.🤖 Generated with Claude Code
https://claude.ai/code/session_01Pjh5f11tyUbYMQmSZaJZFS
Generated by Claude Code