Repository navigation
feat(driver-sql,driver-turso,driver-memory,objectql,cli,metadata-protocol): plain organization key part in new unique indexes; the differ reads COALESCE(tenant) as tenant on a certified database (18.x step 7b-1 + 7b-2) - #24893
objectstack-fleet[bot] wants to merge 96 commits into
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>
…n organization key part (ADR-0131 D1) Claude-Session: https://claude.ai/code/session_01NnXVu3vF2TKFLMi5Me4CUd Co-authored-by: Claude <noreply@anthropic.com>
…the safe drift that survives the plain organization key part Claude-Session: https://claude.ai/code/session_01NnXVu3vF2TKFLMi5Me4CUd Co-authored-by: Claude <noreply@anthropic.com>
…t-null-ddl' into claude/issue-24583-7b12-index-plain-form
…12-index-plain-form # Conflicts: # packages/plugins/plugin-auth/src/sso-register-platform-admin-gate.test.ts # packages/qa/dogfood/test/parent-derived-write-refusal-not-visible.dogfood.test.ts # packages/qa/dogfood/test/predicate-write-unreadable-not-matched.dogfood.test.ts # packages/qa/dogfood/test/sharing-rule-org-less-caller.dogfood.test.ts # packages/qa/dogfood/test/write-door-unreadable-is-not-found.dogfood.test.ts # packages/runtime/src/default-organization-statements.golden.test.ts # packages/services/service-automation/src/sys-user-default-row-scope.integration.test.ts
Contract review (delta re-review after FAIL at de01287)Served-tier: Reviewed: the dev's four own commits since de01287 (9ef44b5, 3bb7ad6, c1f0005, d4e977b; 9 files, the delta in ① Derived judgments1. B1 — the changeset is now a true declaration. CLOSED.
2. B2 — the "not certified" prose now says what the code does. CLOSED.
3. B3 — the
4. The two re-pins — neither weakens an assertion.
5. The 18.0.0-born dogfood leg (9ef44b5) models the real store. B4 CLOSED as to the leg.
② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS |
…n stamp alone — the 7b-0 × main textual merge had kept both keys Claude-Session: https://claude.ai/code/session_01NnXVu3vF2TKFLMi5Me4CUd Co-authored-by: Claude <noreply@anthropic.com>
… into 7b-0 Conflicts in seven fixture files take #24893's resolution (4dadd7e): where 7a-2 now writes the grant with an organization the pre-18 relax goes; where main still lands an organization-less row the relax stays. sharing-rule-criteria-required takes main's version verbatim (both sides had added the organization key). plugin-webhooks' new real-SqlDriver case gets the lean engine's Default Organization. Claude-Session: https://claude.ai/code/session_018EszgtA3V2KXBX5tf59Hi4 Co-authored-by: Claude <noreply@anthropic.com>
Part of #24583
Clause-②: yes (widening). New DDL changes form.
sys_settingrow-identity index, which the metadata protocol rebuilds on every boot, keys the plainorganization_idfrom the next boot on. This is probe-first and a pure relaxation, so it cannot fail on existing rows.Clause-② text corrected by the epic seat on 2026-10-20 (about 16:15Z) after the contract review's B1 (record 6293077099). It mirrors
.changeset/24583-plain-organization-key-part.mdat3bb7ad6b1.18.x step 7, parts 7b-1 + 7b-2 (claim 6289404450, governing record 6285122667). ⛔ This lands only after 7b-0 (
claude/issue-24583-7b0-tenant-not-null-ddl) has merged and this branch has mergedmainagain. Until then the diff carries 7b-0's commits too.What changes
7b-2: new DDL keys the plain organization column (ADR-0131 D1: the tenant column is NOT NULL).
uniqueIndexesFromFields,normalizeDeclaredIndex(unique: 'organization') andlegacyUniqueReplacementsno longer mark the tenant key part NULL-safe. A field-level organization unique isUNIQUE (organization_id, f).buildIndexNameis unchanged, so no index is renamed.memory-unique-constraint.ts, where a NULL organization now exempts the row like any other NULL key part;os generate migration, which now emits the organization composite it used to list as NOT EMITTED (GLOBAL_TENANT_KEYremoved);sys_settingrow-identity migration:organization_idis plain andCOALESCE(user_id, '')stays. Its duplicate query lists only rows the index refuses.probeNullSafeUniqueDuplicates, local and Turso remote) leaves out rows with a NULL plain key part. The index never refuses those rows.__global__as the autonumber counter key is untouched (7b-3's ground).7b-1: the differ reads
COALESCE(tenant)≡tenant, in both directions, where the tenant column holds no NULL.diffManagedIndexestakestenantKeyCertified. When it is set, the tenant key part's form drops out of the comparison identity. The column set, uniqueness and other key parts still have to match.SqlDriver.detectTableIndexDriftsets it from two witnesses:ALTERproduce it too.ObjectQL.isOrganizationOwnershipCertified(): a verifiedsys_migrationownership row at the currentshapeGenerationor above. This is the same reading the boot guard boots on, and it covers a birth marker or the ceremony's marker.fileColumnsMovedsupply-seam precedent, so the driver never learns the ledger's vocabulary and there is no layering break.enforcedIndexKey,isSyncReproducibleIndex,isRuntimeManagedIndexand the shadow-key reader are unchanged. An input carryingnullSafeColumnsis still honoured verbatim.Measured
ceremoniedFixture):sys_email_templatestays nullable (withsys_sso_providerand one rotation shard) and keeps its 18.0 COALESCE index, so on the fixture as-is the certification witness is what carries it.ALTERand a Turso remote store not at all, so there every 18.0 index survives the ceremony. A SQLite store certified by an earlier 18.x ceremony keeps them too. The pin therefore also restores all 13 COALESCE indexes exactly as 18.0 wrote them.recreate_indexand 0 destructive drift. The 12 declared composites survive the boot byte for byte.sys_setting's index is rebuilt by its own runtime migration, which rebuilds it every boot anyway.packages/qa/dogfood/test/upgrade-18x-index-equivalence.dogfood.test.ts: 7/7.scripts/ablation-replace.mjsonschema-drift.ts, comparing with the equivalence switched off:ablation-dist-preflightshowed the marker in 2 built files.recreate_indexlines in the output.unhex(sha2(concat(organization_id,0x1f,v),256)), which is plain.recreate_index/destructive when uncertified and gives no drift when certified.stale); the column is derived, so no data moves.Proof
R1, fresh
pnpm dev -- --freshon a random port: health 200, seeded-admin sign-in 200, 0 error lines, no schema-drift line.R2 + R3:
upgrade-18x-harnesswith the new equivalence file gave 15/15;organization-ownership-boot-refusal-ordergave 4/4.18.0.0-born: measured live on the published 18.0.0 (
os migrate plan: 0 changes over 73 tables), and pinned as the third leg, 10/10. On the merged tree with test(dogfood): fixture-18.0.0 — the upgrade harness boots a database the published 18.0.0 created #24911's fixture-18.0.0 leg, the dogfood files give 26/26.R4: built CLI
run-dev.js serveunderOS_TENANCY_POSTURE=isolated, on a throwaway app declaring the real@objectstack/organizations(invite-only membership).Tenancy: isolated, Organizations mounted.organization_id.Step 4's 7 walled e2e cases (
OS_TEST_TIERS=nightly): 7/7.Suites:
--project unitThe integration files touched were run alone.
typecheckis clean on all six packages.driver-sql's 3 failures are not this change. Each one inserts an organization-less row into a fresh table, which 7b-0's NOT NULL now refuses:
sql-driver-21227-create-answers-stored-row(2) and 7b-3'ssql-driver-organizationless-counter-refusal(1). Measured on the base merge of main and 7b-0 without this change, the first two already fail; the third arrived with 7b-3. They belong to 7b-0's next merge ofmain.node scripts/pm/dispatch-gates.mjs --commands: 105 families, each exit captured before any pipe. The final-head results are in the report.check-engine-split-ratioandcheck-plugin-teardown-shape --self-testrefused on the shallow clone, then passed after deepening.check:durability-log-levelandcheck:query-options-erasurewent red on an earlier head and were fixed here.check:generated: exit 0.check-governed-merges --branch: Tier H by size.Size
The governed-merges check reads 5,532 changed lines while 7b-0's commits ride along, so this is Tier H by size and it is not split. This part's own commits come to 2,465 lines: 478 production, 1,968 tests, 19 changeset. There are 0 fixture lines; no fixture was regenerated.
维护者速读(草稿)
organization_id列,不再用COALESCE(organization_id, '__global__')。旧库里已有的 COALESCE 索引,只要库已认证(出生标记或仪式标记)或租户列已 NOT NULL,差异检测就视为同一个索引,不重建、不改名。唯一例外是sys_setting的行标识索引:元数据协议每次启动都会重建它,下次启动起改用普通列(先探测再替换,只放宽、不会因已有数据失败)。__global__桶已无意义;不加等价判断的话,每个升级上来的库每次启动都会被提示「删掉重建」13 个唯一索引。sys_setting行标识索引外,已有索引一律不动,回滚即还原本 PR,不涉及数据迁移;MySQL 实测无需重建列。Acceptance notes
recreate_indexof ADR-0120 D4 (bare composite → NULL-safe) is no longer produced by any generator. Its apply path and pre-flight remain for an op that already carriesnullSafeColumns, and the Turso suites pin them that way.recreate_indextoward the plain key.os migrate organization-ownershipas the remedy, neveros migrate apply --allow-destructive(B3, pinned insql-driver-tenant-key-equivalence.test.ts).os serveuntil the ceremony certifies it.os generate migrationemits the tenant column nullable, while the driver's fresh table is NOT NULL (7b-0). The generated composite is now the platform's own constraint, which is stronger than the nothing it emitted before, but the column's nullability still differs from the driver's. That is 7b-0's ground and is not changed here.🤖 Generated with Claude Code
https://claude.ai/code/session_01NnXVu3vF2TKFLMi5Me4CUd
Generated by Claude Code