Repository navigation
fix(cli): os migrate plan/apply examine the datasource a datasourceMapping default routes objects to, as os dev does - #24199
Conversation
…rces os serve connects A datasourceMapping rule routing objects to a declared datasource (the declared default included) left them bound to no driver on the migrate boot: no datasource-connection service was published, so AppPlugin kept every declared datasource metadata-only. Publish one (managed datasources only) and plan each declared datasource the deployment's objects resolve to. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QyoWKK9RwUNxBxmevy9Neb
…eMapping-default datasource Also read each planned database once in the unmanaged-table sweep: the primary resolved from a declared default and the declared connection are one file, and were counted twice. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QyoWKK9RwUNxBxmevy9Neb
…er-per-database sweep Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QyoWKK9RwUNxBxmevy9Neb
…ed datasources Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QyoWKK9RwUNxBxmevy9Neb
…port Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QyoWKK9RwUNxBxmevy9Neb
…tasources
It filters the plan's own findings; on a datasourceMapping-default project
its refusal ("not in the set os migrate plan diffs: bound to no driver")
became false once the plan composed the declared datasource. SchemaStack
names each planned driver's database (databaseOf).
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QyoWKK9RwUNxBxmevy9Neb
…e that cannot connect Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QyoWKK9RwUNxBxmevy9Neb
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QyoWKK9RwUNxBxmevy9Neb
📓 Docs Drift CheckThis PR changes 1 package(s): 12 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 6 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 29 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 d86bedc91b799a0cf9e332de3eae6005e9d76a6a && git checkout d86bedc91b799a0cf9e332de3eae6005e9d76a6a
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 7d6ec91d2f89e834fc5b82183a95cb3c5427a882 0b5ed65b4e090d9f073ca805d4e83f10a2fe3c7e && git checkout -B drift-repro 7d6ec91d2f89e834fc5b82183a95cb3c5427a882 && git merge --no-ff 0b5ed65b4e090d9f073ca805d4e83f10a2fe3c7e
node scripts/docs-audit/affected-docs.mjs --json 7d6ec91d2f89e834fc5b82183a95cb3c5427a882
|
席位核对 — ACCEPT · PR #24199 at
|
Fixes #24120
Clause-②: no
What was wrong
Take a project whose primary store is a declared datasource that
datasourceMappingroutes as the default:datasources: [{ name: 'qa_main', driver: 'sqlite', … }]withdatasourceMapping: [{ default: true, datasource: 'qa_main' }]. On it,os migrate planresolvedqa_main's database as its primary ("the project config's default datasource qa_main"), but it registered no driver namedqa_main. Every object the rule routes there, platform objects included, was bound to no driver. The plan printedExamined 0 managed table(s)and a PARTIAL plan, and logged[ObjectQL] Datasource 'qa_main' mapped for object 'sys_metadata' is not registered.os devon the same config reports drift in that database and namesos migrateas the remedy.Mechanism, measured (dispatch hypotheses H1–H3)
cb8756b00on a minimal scratch stack (qa_mainsqlite plus the default mapping, one app object, provisioned by a boot, then staged). The plan reportedExamined 0 managed table(s)and coverage0 of 71 declared object(s) … (71 bound to no driver).os devon the same database printed[schema-drift] os24120_thing.extra: column exists in the database but not in metadata (orphaned).schema-migrate.ts's driver lookup.AppPluginconnects declared datasources only through thedatasource-connectionservice (honourDatasourceMappingBeforeSchemaSyncandstart(), thenconnectDeclared, the ADR-0062 D1 path).os dev/os servepublish that service fromDatasourceAdminServicePlugin(packages/cli/src/commands/serve.ts, the Runtime Datasource Admin block). The migrate boot (bootSchemaStack,packages/cli/src/utils/schema-migrate.ts) published nothing, soAppPluginleftqa_mainmetadata-only. Even with a driver, the planned set was the primary and the telemetry sibling only.AppPluginconnects the declared datasource from the artifact or config it holds.packages/runtime(resolve-project-database.ts) is untouched.The fix (
packages/clionly)bootSchemaStackgetscomposeDeclaredDatasources. It is set byos migrate plan, byapply, and byunmapped-columns(see below). It composesDeclaredDatasourceConnectionPlugin, which publishesdatasource-connectionas aDatasourceConnectionServiceover the shared driver factory. That is the same pathos devconnectsqa_mainby, under its declared name. It is not composed when the composition already brings a publisher.external/validate-onlydatasource holds no table a migration plans, and its objects are federated.data/directory behind.available, and a registered non-federated object resolves to it (the engine's routing decides).SchemaStack.databaseOfnames each planned driver's database. A composition note names each declared datasource and its database, in both faces.distinctDatabaseDrivers: the unmanaged-table sweep reads each database once. The primary resolved from a declared default and the declared connection are two connections to one file, and without thisphysicalTablesdoubled (measured 144 against 72).os migrate unmapped-columns(outside the claim's file surface). It filters the plan's own findings. Once the plan composedqa_main, its refusal on this config,sys_user is not in the set "os migrate plan" diffs: it is bound to no driver, became false. It now composes the same datasources and names the database throughdatabaseOf. Edited under the standing rule that a published defect this change makes false is fixed in the same change. The claim's file surface needs that path added.Scope. The mechanism connects every managed declared datasource
os serveconnects: one a mapping routes objects to, one an object binds, or one withautoConnect. It is not limited to the default. Limiting it would have needed a filter that keeps the same defect on a non-default mapping rule. Behaviour change: a routed managed datasource that cannot connect now failsplan/apply/unmapped-columnswith the connect erroros servegives. Measured with an unreachable mapped postgres: exit 1 anddatasource 'qa_pg': connect failed — connect ECONNREFUSED … fail-fast per ADR-0062 D5. UnderOS_ALLOW_DRIVER_CONNECT_FAILURE=1the plan exits 0 and is PARTIAL (1 bound to no driver).Evidence
The pin is
packages/cli/src/commands/migrate/plan.declared-default-datasource.integration.test.ts(integration tier,// test-budget: 80). It provisions withos dev --compileandOS_MIGRATE_AND_EXIT=1, then stages two drifts inqa_main's file: a column the app object stopped declaring, and a column onsys_user. Then it asserts four things:[schema-drift]setos devprints, and holds both staged drifts;unexaminedObjects: 0, anddatabaseSourceisconfig-datasourceqa_main;physicalTablesequals the file's table count;os migrate unmapped-columns --object sys_userreadsfx24120_legacy.0b5ed65b4schema-migrate.ts,plan.ts,apply.tsrestored tocb8756b00)be919df4aAssertionError: expected [] to deeply equal [ 'fx24120_thing.extra', …(1) ]unmapped-columns.tsrestored tocb8756b00)0b5ed65b4os migrate unmapped-columns --object sys_user failed(exit 1,Datasource 'qa_main' … is not registered)schemaModecheck replaced bytrue,scripts/ablation-replace.mjs)3df5f9f1fexternalandvalidate-onlycases ofschema-migrate.declared-datasources.test.tsEach before or ablation leg restored with
git checkout HEAD --, withgit diff HEADempty and blob hashes equal to HEAD.Manual readings on the scratch stack, after the fix: the plan examined 71 tables and listed
os24120_thing.extra [drop_column].apply --allow-destructive --yesapplied it, and the next plan showed 0 changes and 0 unexamined.--database-url file:pointing at the same file gaveExamined 71, the same drift, andphysicalTables: 72.Local checks at
0b5ed65b4:pnpm --filter @objectstack/cli typecheck && vitest run --project unit: exit 0, 310 files / 4599 tests.vitest run --project integrationover the pin andunmapped-columns.integration.test.ts: 2 files / 11 tests passed.pnpm exec eslint --no-inline-configover the 6 changed.tsfiles: exit 0.node scripts/pm/dispatch-gates.mjs --commands: 65 families derived; all 65 run, each exit 0.--ran:65 derived, 65 run, 0 NOT-MEASURED, 0 UNRUN.CI owns the rest: the farm,
Test Core, the type-check lanes, dogfood.Acceptance notes
DatasourceAdminServicePlugin.start()onos serve, which this boot does not run. Objects bound to them stay "bound to no driver", and the plan says so (PARTIAL).os migrate organization-ownership(held off on this seat) andsecurity-catalog-overlaysare not opted in. On adatasourceMapping-default deployment the former's drops "through that set's drift" still examine none of the routed tables. Not measured here.docs/qa/platform-checklist/areas/cli.json, itemcli.dev-automigrate-policy: step 2's note thatos migrate plan"composes no driver for it and prints a PARTIAL plan" is stale once this lands, and the plan can serve as that step's oracle again.sys_packageslisted as unmanaged) is not touched: the sweep listedsys_packagesalone, once, before and after.idx_sys_user_created_at) is acreate_indexdrift in the plan, whileos dev's boot creates it silently. The two agree on the remedy. This predates this change; the pin stages no such drift.origin/mainmoved 5 commits sincecb8756b00(to2bcaefba9). None touches a path here, and the branch was not merged. CI's merge ref and the queue's rebuilt generation carry the joint check.Generated by Claude Code