Repository navigation
fix(cli): the organization-ownership ceremony reads its dialect once, through metadata-core's shared resolver, and refuses an unresolved one before any statement - #23647
Conversation
…esolver Claude-Session: https://claude.ai/code/session_01Q9PZ5Pzqcus4woVvTZwWNU Co-authored-by: Claude <noreply@anthropic.com>
…ce, through metadata-core's dialectOfDriver, and refuses an unresolved one before any statement Claude-Session: https://claude.ai/code/session_01Q9PZ5Pzqcus4woVvTZwWNU Co-authored-by: Claude <noreply@anthropic.com>
…emony change as measured The sweep is os migrate plan's, not the ceremony's, and the schema boot never plans SqliteWasmDriver, so the change had no reach there. Claude-Session: https://claude.ai/code/session_01Q9PZ5Pzqcus4woVvTZwWNU Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q9PZ5Pzqcus4woVvTZwWNU Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 2 package(s): 16 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 8 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 31 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 1178dce5aa16eb839f7ffacc8bb8213a9b91816d && git checkout 1178dce5aa16eb839f7ffacc8bb8213a9b91816d
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin a647783d46a69775a97ea57c2d1a54dae1b7333e 107a7a923cd473f50016cee23ea47111bdcaa5ec && git checkout -B drift-repro a647783d46a69775a97ea57c2d1a54dae1b7333e && git merge --no-ff 107a7a923cd473f50016cee23ea47111bdcaa5ec
node scripts/docs-audit/affected-docs.mjs --json a647783d46a69775a97ea57c2d1a54dae1b7333e
|
Contract reviewServed-tier: The review is owed by the ① Derived judgments
② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
席位核对✅ ACCEPT: PR #23647 at
Checklist, read against GitHub and
Accepted:
Filed from this report: #23648 ( Landing: Generated by Claude Code |
…107a7a9) into claude/issue-23648-sqlite-wasm-sql-driver This branch builds on PR #23647's dialectOfDriver, which resolves the wasm driver to sqlite once the schema boot finds it. The branch is merged as it stands, not rewritten. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FLwgbTSmGMrqsXkBDsiwfG
…river PR #23647 has landed on main (c2bb302, from the same head 107a7a9 this branch merged), so after this merge the branch's diff against main is this card's alone. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FLwgbTSmGMrqsXkBDsiwfG
Fixes #23558
Clause-②: yes (widening)
The grade is
yes (widening)because this PR exports one resolver from@objectstack/metadata-core,dialectOfDriver, so the ceremony can share it. The claim graded the cardnounless that export was added, and it was added. No existing export answered a driver's dialect.What changed
os migrate organization-ownership(the plan and--apply) now reads its SQL dialect once, from the stack's driver, before either arm runs a statement. It uses the resolver that metadata-core's legacy-row reader uses: the driver'sdialectNamefirst, then a recognisedconfig.clientstring.Anything else is refused with
driver-unsupportedand exit 1, and no statement runs. That covers a client class with no recogniseddialectName, an unrecognised client string,dialectName: 'unknown', and a driver with no dialect at all. The refusal names the driver: its name, itsdialectName, and its client string or client class name.packages/metadata-core/src/legacy-organization-row.ts:dialectOfDriveris exported. Its behaviour is unchanged, and its doc comment says who shares it.packages/cli/src/utils/organization-ownership-plan.ts:dialectOf, which read the client string only, is replaced byceremonyDialectOf(driver). It calls the shared resolver and throws the refusal.PlanReadercarries the resolveddialect.clientstays as the plan file's record, and only when it is a string.packages/cli/src/utils/organization-ownership-apply.ts: thedialect ?? 'sqlite'default is gone. The apply takes the executor's resolved dialect.packages/cli/src/commands/migrate/organization-ownership.ts:ceremonyDialectOf(stack.driver)right after the raw-seam check, before any statement. In--applythis is also before the ledger check.dialectOf(executor.client) ?? 'sqlite'is gone.@objectstack/clipatch,@objectstack/metadata-coreminor.Premise check against
main(f7cd580bff)main. The card said "the ceremony would then build SQLite-dialect raw SQL against a PostgreSQL or MySQL database". It would not. Both arms run the plan step first, and the plan'sdialectOf(String(client))returned null for a client class (stringified) and for an unrecognised string. So the plan refused withdriver-unsupportedbefore any statement.?? 'sqlite'and the template lookup's?? 'sqlite'were reached only after the plan had resolved the same client to a dialect, so neither ever applied.resolvePlannedDriverExecreads noconfig.client; it only picksexecuteorraw. In that module the string-only read isreadPhysicalTableNames, which belongs toos migrate plan's unmanaged-table sweep. The ceremony never calls it. It is left unchanged (see Acceptance notes).dialectOfDriver/dialectOfNamewere not exported, and no exported entry in the repository answers a driver's dialect this way. That is why there is one export.No-regression guard: every in-repo driver, measured
Each driver was constructed through the shared datasource factory (
createDefaultDatasourceDriverFactory) or the runtime's Turso factory, from the build of0fc3ae2575. No driver package is touched by this PR. Themaincolumn is the command's own expression,dialectOf(String(config.client)).namedialectNameconfig.clientmaincom.objectstack.driver.sqlsqlite'better-sqlite3'com.objectstack.driver.sqlite-wasmsqliteClient_WasmSqlite2com.objectstack.driver.sqlpostgres'pg'com.objectstack.driver.sqlmysql'mysql2'libsql://com.objectstack.driver.tursosqlite'better-sqlite3'file:com.objectstack.driver.tursosqlite'better-sqlite3'Every driver that ran the ceremony before still runs it.
SqliteWasmDriver never reaches the resolver through the command today. It was measured through the CLI at
91084b5d5e:os migrate organization-ownership --database-url wasm-sqlite://FILE --jsonanswers{"error":"plan_refused","reason":"driver-unsupported","detail":"no SQL driver with a raw-SQL seam is active"}. That is the same answermaingives. The schema boot's SQL-driver lookup does not include the service name that driver registers under, sostack.driveris null. This is reported as a finding and not fixed here.The SQLite path was also run through the CLI at
f9f69beda5:--database-url FILE(better-sqlite3) writes the plan, records"client": "better-sqlite3", and exits 0.NOT MEASURED: a live PostgreSQL or MySQL run of the ceremony. Both shapes were measured on constructed drivers, and the command pins assert that each one's first statement is its own dialect's catalog read.
Tests
packages/cli/src/commands/migrate/organization-ownership.dialect.test.ts(new,unittier):dialectName; a client class asSqlDriveranswers it (dialectName: 'unknown'); an unrecognised client string (oracledb);dialectName: 'unknown'besidepg; nodialectNameand no client.plan_refused/apply_refused, reasondriver-unsupported, a detail that names the driver, exit 1, zero statements, no plan file, and the apply step never called.--applyhands the executordialect, andclientonly when it is a string.packages/metadata-core/src/legacy-organization-row.test.ts: the exporteddialectOfDriver, nine rows.dialectNamefirst; a client string when there is nodialectName;'unresolved'for a client class, an unknown string, anddialectName: 'unknown'besidepg;'none'for no dialect and no driver.organization-ownership-plan.integration.test.ts: theoracledbrefusal now pins that the dialect is refused before the plan issues any statement.organization-ownership-apply.test.ts:60,...served-catalog.integration.test.ts,...host-supplement.integration.test.ts) gaindialect: 'sqlite'.Runs on
107a7a923c, the final head, after mergingmainatedac2183c0, which brings in PR #23624's hunks in the same files. The merge was clean, and was followed bypnpm install --frozen-lockfileand a rebuild of the cli closure.@objectstack/cliunit project, by path (organization-ownership.dialect,unmanaged-tables,organization-ownership.apply-flags,organization-ownership-inventory): 4 files, 90 tests passed.@objectstack/cliintegration project, by path (the plan, apply, served-catalog, host-supplement, column-drops, unmanaged-tables and meta-stored-legacy-organization suites, and the command's fate-supplement, occupancy and journal-mode suites): 10 files, 148 tests passed. This layer was run locally because the diff edits integration-tier files.pnpm --filter @objectstack/cli typecheck(tsc --noEmitandcheck:test-typecheck): exit 0.pnpm --filter @objectstack/metadata-core typecheck: exit 0.pnpm --filter @objectstack/metadata-core test: 18 files, 728 tests passed.pnpm --filter @objectstack/cli exec vitest run --project unit(the whole unit layer): 290 of 291 files and 4341 of 4342 tests passed. The one red wasboot-preparation-parity.test.ts > every SERVE_ONLY entry is still used by serve.ts, a 5000 ms timeout (5160 ms) on a shared box while the gate battery ran beside it. The diff touches neither that file norserve.ts. Re-run alone on the same head: 37 of 37 passed.Ablation (one-off proofs, through
scripts/ablation-replace.mjswith anEXIT INT TERMrestore trap)Both mutated subjects are
packages/cli/src, resolved relatively, and metadata-core is aliased tosrcin the cli vitest config, so nodist/is involved.The SQLite fallback restored in
ceremonyDialectOf, on107a7a923c. The anchorif (… ) return dialect;was replaced withreturn … ? dialect : 'sqlite';. On disk: injected 1, anchor 0; blob25cf4a26b487became476f0e239e7b.oracledbpin.25cf4a26b487) andgit diff HEADis empty. The same result held on91084b5d5e.Gates
Local, on
107a7a923c:node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderived 67 commands from the merge baseedac2183c, with no stale-tree warning. All 67 were run, and each exit code was captured before any pipe: 67 of 67 exit 0.--ranreconciliation: "67 derived, 67 run, 0 NOT-MEASURED, 0 UNRUN", exit 0.check:dual-build-cjs-loads,check:i18n,check:i18n-coverageandcheck:i18n-walk-parityneed build output beyond the cli closure. They were measured after building their named prerequisite closures (the CLI, the examples, and the plugins and services they extract). On the pre-merge head they first answered exit 3 (PREREQUISITE NOT MET) and were re-run green.check:type-check-coverageandcheck:type-check-debt, exit 0.pnpm lintnarrowed to the 10 changed.tsfiles:eslint --no-inline-configon them, exit 0, no output. The full repo scan is CI'sLint & Repo Gates.Acceptance notes
os migratefinds no SQL driver on awasm-sqlite://or*.wasm.dbdatabase. The schema boot looks a SQL driver up bySQL_DRIVER_SERVICESinpackages/cli/src/utils/schema-migrate.ts(driver.com.objectstack.driver.sql,driver.com.objectstack.driver.turso,driver.sql). SqliteWasmDriver registers under none of them. So the ceremony refuses a SQLite database with "No SQL driver with a raw-SQL seam is active … The ceremony supports SQLite, PostgreSQL and MySQL." This was measured through the CLI (above). It is reported to the seat as a finding and is not fixed here, becauseschema-migrate.tsis outside this claim.os migrate plan's unmanaged-table sweep still readsconfig.clientas a string only (readPhysicalTableNames,packages/cli/src/utils/unmanaged-tables.ts). It is the only remaining string-only reader in the cli. It has no reach today: its drivers come from the same lookup, which never plans SqliteWasmDriver. Carrier: none.packages/runtime/src/organization-ownership-boot-guard.tskeeps a third copy of the resolver. In that copy, an unrecogniseddialectNamefalls through to the client string. No in-repo driver reaches the difference:SqlDriveranswers'unknown'only for a client that the same three spelling sets do not recognise. Carrier: none.organization-ownership-plan.ts:181,organization-ownership-grants.ts, and the apply test's:1979, which belong to PR fix(plugin-security,spec): the grant remedy texts name the reviewed-plan ceremony; C7b wording follow-ups in cli, plugin-auth and the durability vocabularies (#23513) #23624; andpackages/drivers/**.Generated by Claude Code