Repository navigation
feat(cli,driver-sql)!: a tenant table's organization-less row no longer reaches the __global__ counter, and a production server boot with no auth secret is refused (18.x step 7, part 7b-3) - #24845
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>
📓 Docs Drift CheckThis PR changes 7 package(s): 28 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 9 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 154 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 c084de89036dc7b5a8dd7333a615617fc76bb745 && git checkout c084de89036dc7b5a8dd7333a615617fc76bb745
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 41646a5788f5362a66527a287200f9832fb00602 82c14dd8f6cf6871d77b99091016678a8cab17ea && git checkout -B drift-repro 41646a5788f5362a66527a287200f9832fb00602 && git merge --no-ff 82c14dd8f6cf6871d77b99091016678a8cab17ea
node scripts/docs-audit/affected-docs.mjs --json 41646a5788f5362a66527a287200f9832fb00602
|
Contract reviewServed-tier: Merge-base ① Derived judgments1. The serve refusal — holds.
2. The counter refusal — holds, with the per-driver edge named below.
3.
4. Texts — hold. The 5. Tests — hold. 6. Semver and Clause-② — the line holds, one level does not. CI at review time. 25 success, 7 skipped, 0 failed; 8 still running: Test Core 1/6–6/6, Type Check · workspace, Type Check · consumer gates. Required-check status is therefore not yet final; nothing red. ② Semver level
③ Boundary flagsB1 — ADR-0087 marker category (changeset). Replace the marker with the seventh category, naming the retired export: B2 — Residuals (not blocking).
Implemented-by: VERDICT: FAIL |
…me-export-retired category and grades driver-turso minor; the refusal pin unsets the legacy secret spellings Claude-Session: https://claude.ai/code/session_01H7Dv7HSDeT8d2fbDq3myx5 Co-authored-by: Claude <noreply@anthropic.com>
|
Correction to the PR body, answering contract review 6288633042 (R4). The body says " The same commit also carries:
Generated by Claude Code |
Contract review (re-review after FAIL at 112ea87)Served-tier: Delta over the FAIL record at The two-file claim — verified. ① Derived judgmentsB1 — resolved. The marker now reads
B2 — resolved. R2 — handled. The pin's R4 — handled. The changeset's first bullet is retitled to The merge — nothing changes a judgment. 7a-1 ( Nit (non-blocking, as instructed). The body cites "ADR-0131 D9, stage 8d" for the launch-window CI at review time (10:36Z). 41 check-runs: 30 success, 7 skipped, 0 failed; 4 still running: Test Core (2/6), (3/6), (4/6), (6/6). Of the five required contexts, ② Semver level
③ Boundary flagsNo blocking item remains. B1 and B2 are closed as above. Residuals (not blocking).
Implemented-by: VERDICT: PASS |
Part of #24583 — step 7, part 7b-3 (claim 6284514874, amended 6284744613). The card stays open until every step-7 part has landed.
Clause-②: yes (narrowing) — measured: two accept sets narrow (the driver's autonumber counter and
os serve's production server boot), and four exports are added and one is removed.#24681 (steps 5+6) has merged, and this branch merged
mainafter it, at112ea871overmain307ce4c4. The diff againstmainis this part alone: 24 files, +637 / −88.check-governed-merges --branchputs 0 of the 24 paths on the register, and the size is under the human-merge line.Why
Step 7 rests on the premise that the readers it retires are dead after certification. The first measurement on this part found one still live. A production
os servewith theauthtier on and noOS_AUTH_SECRETskipped AuthPlugin with a warning and served anyway. With no AuthPlugin,sys_organizationis never registered, so the engine answersno-organization-object, and the inline seed wrote rows withorganization_idNULL and drew their autonumbers from the__global__counter.Measured on
examples/app-showcase, on a fresh SQLite database, on the merged tree before this change:showcase_field_zoothen held 2 rows, both with NULLorganization_id;_objectstack_sequencesheld one row withtenant_id__global__.The seat ruled option A (amendment 6284744613, under #3963's rule A1). Close the door at the boot, then retire the counter path where the organization object exists.
What changed
os serverefuses a production server boot whose auth did not compose for a missing secret.packages/cli/src/commands/serve.ts.OS_AUTH_SECRETand--no-server.--no-server(kernel only) still boots with the warning,--devstill uses its fallback secret, and thehost-kernelreason (the cloud runtime) still only warns.os startspawnsserve, so it gets the same refusal.The counter refuses an organization-less row on a tenant table, wherever the driver registered the organization object.
SqlDriver.resolveSequenceTenantIdis the one rule both faces call;TursoDriver's remote face passes the table and field through.OrganizationlessCounterRefusal(packages/drivers/driver-sql/src/organizationless-counter-refusal.ts), with codeERR_SYSTEM_WRITE_ORGANIZATION_REQUIREDand status 500. That is the engine twin's code, registered for@objectstack/driver-sqlin the error-code ledger.__global__stays the counter key for an object with no tenant column. It also stays for a composition with no organization object, i.e. a lean@objectstack/objectql/coreembedding.organizationObjectRegistered, set incomputeAndRecordTenantField, the one funnel every registration door passes through.Texts:
GLOBAL_TENANTno longer exported from@objectstack/objectql;app-plugin.tsseed-handoff docblock;UniqueScopeSchemaTSDoc infield.zod.ts;unique/legacy-organization-composite;duplicates.ts's live-condition docblocks.Each now says that a NULL-organization row is residue an earlier release wrote, not something a new write produces.
Pins:
sql-driver-organizationless-counter-refusal.test.ts(driver-sql): refusal on create and bulkCreate, carried organization, no tenant column, lean embedding.serve-no-auth-secret-refusal.integration.test.ts(cli): the refused boot's database file never exists; the control boot with the secret creates it and reaches ready.system-write-organization.test.tsand runtimesystem-write-tenancy-autonumber-split.integration.test.tsassertADR-0131 D9andorganization_id = NULL, and assert that__global__is absent. The runtime one also asserts it is the engine's refusal, not the driver's.data-model-rules.unique-verdicts.test.ts.seed-tenancy-autonumber-split.integration.test.tsnow lands its 17.x residue through the same driver call with the refusal's composition fact withheld (asA17xDriver), so the residue is the driver's own 17.x path.E2E_AUTH_SECRETintest/helpers/serve-process.ts).serve-ready-degraded-boot.e2e.test.tsreaches its degraded banner kernel-only (--no-server).What reads the orphaned
__global__counter rows a 17.x install left on tenant tables, after this change: the counter does not.os migrate duplicatesstill reports them inliveConditions, now documented as residue. The historic seed-tenancy backfill (kernel:ready, and the handoff on organization create) still merges them into the organization's counter. Its migration entry is not touched here.Measured
On HEAD
112ea871(aftermainwas merged), with a freshpnpm installandpnpm build(exit 0):os serveof app-showcase with no secret: refused, with "no auth secret is set" andOS_AUTH_SECRETnamed, and the database file never created.organization_id, with 0 NULL rows, and the counter is keyed by the Default Organization.examples/embed-objectql: 2/2 tests passed.pnpm dev -- --freshon a random port: health 200, seeded-admin sign-in 200, 0 error lines.upgrade-18x-harness.dogfood.test.ts(over the ceremonied fixture) andorganization-ownership-boot-refusal-order.dogfood.test.ts: 2 files, 12 passed.isolatedproductionos servewith the real@objectstack/organizationsruntime composed (Organizationsin the plugin list): health 200, 33 tables carryorganization_idwith 0 NULL rows, and 0 failure lines. The walled dogfood cases (the sixwall-*files and the three*-walled*files): 9 files, 133 passed. Step 4's walled e2e cases (serve-organizations-host-resolution,serve-organizations-mount-failure) ran in the CLI nightly tier below, green.OS_TEST_TIERS=nightly, every*.e2e.test.ts): 83 files, 879 tests;turbo run typecheckover the seven touched packages and their closure: 68 of 68 tasks.b2fec9a9(before themainmerge; the ablated line is unchanged by it).node scripts/ablation-replace.mjsreplaced the refusal line inresolveSequenceTenantId. The landing was proven on disk: anchor count 1 to 0, blob0af77d32fad3to0828ff0cfadb. Thensql-driver-organizationless-counter-refusal.test.tswent 2 failed, 3 passed: both refusal cases red, the three__global__/carried cases still green. Restored: blob equals HEAD andgit diff HEADis empty.node scripts/pm/dispatch-gates.mjs --commandson the final head derives 124 commands over the 24 paths. All 124 ran, each exit code captured to a file before any pipe.--ranreconciles 124 of 124.check:adr-0087-registration,check-changeset-no-major,check:error-code-provenance,check:cli-test-child-env,check:type-check-debtand the speccheck:generated(14 artifacts up to date).check-engine-split-ratio --days 90(exit 2) andcheck-plugin-teardown-shape --self-test(exit 3) refuse in this shallow clone: they need history older than the clone. NOT MEASURED here; this is checker health, not this diff.Acceptance notes
Engine.resolveSystemInsertOrganization'sno-organization-objectexit still writes NULL. After this PR no served composition reaches that exit (os serverefuses it); a lean embedding still does, by design.liveConditionsinos migrate duplicates. The JSON shape is unchanged; only its meaning narrowed from forecast to residue.content/docs/deployment/cli.mdxstill calls it "about to mint more duplicates". That page is outside this part's file surface, so the correction is left to the seat..describe()onDeclaredIndexSchema.uniqueand theobject.zod.tscomment describe theCOALESCEDDL emitters, which are 7b-2's (held for a ruling). They are untouched.resolveSequenceTenantId. No new remote-face test was added; the driver-turso suite is green.scripts/check-cli-test-child-env.mjs: the new refusal pin joins its pinned census of files that spawnbin/run.js(13 to 14). The gate and its self-test pass.packages/cli/test/helpers/serve-process.ts: gainsE2E_AUTH_SECRET.uncreatable-directory-doors.integration.test.tsandserve-ready-degraded-boot.e2e.test.ts: production server rigs whose property is not auth. The first now hands in the secret; the second boots--no-server. No assertion was weakened.docs/adr/**,content/docs/releases/, theCOALESCE(…,'__global__')DDL emitters, the hash shadow and the index differ (7b-2), and historic migration entries.🤖 Generated with Claude Code
https://claude.ai/code/session_01H7Dv7HSDeT8d2fbDq3myx5
Generated by Claude Code