Repository navigation
fix(objectql): the boot hydration ERROR names the storage remedies when no driver serves sys_metadata - #23698
Conversation
…en no driver serves sys_metadata When sys_metadata cannot be read at boot and the engine holds no datasource and no driver, the ERROR now says no storage is configured and names the three storage fixes the CLI's no-storage line names, instead of sending the operator to "check the datasource behind sys_metadata". The datasource remedy stays for every case where a datasource exists. The verdict is read from the engine's routing, never from the error text; level and consequence are unchanged. Claude-Session: https://claude.ai/code/session_01ADzJtzYTLUfgrRZHxkagkX Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): 18 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 — 17 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 f07d7e1beaceaef0ef36d091f5d023268a5f2bd3 && git checkout f07d7e1beaceaef0ef36d091f5d023268a5f2bd3
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 3bc407d3072565e620711d8ad2a715c8922e2610 f273dab8e3133a92f52949b8aefa68ef24db2fd3 && git checkout -B drift-repro 3bc407d3072565e620711d8ad2a715c8922e2610 && git merge --no-ff f273dab8e3133a92f52949b8aefa68ef24db2fd3
node scripts/docs-audit/affected-docs.mjs --json 3bc407d3072565e620711d8ad2a715c8922e2610
|
Fixes #23636
Clause-②: no
What this changes
When
sys_metadatacannot be read at boot,ObjectQLPlugin.restoreMetadataFromDblogs oneerrorline: what was not restored, and the fix. The fix half was always "check the datasource behind sys_metadata — connection, credentials, and that the table exists". On a deployment with no datasource declared and no driver registered, such as a productionos servewith noOS_DATABASE_URL, there is no datasource to check and no credential to fix. The operator was sent to a store nobody configured, with the real remedies absent from the line (they sit on the CLI's⚠ No storage:line only, which prints earlier).Now the line says which situation it is in. When nothing serves
sys_metadatait says "no storage is configured" and names the three storage remedies, worded exactly as the CLI's⚠ No storage:line names them. When a datasource exists and its read failed, the line is byte for byte what it was. Level (error) and the consequence sentence are the same in both arms.Before / after (the logged text)
Before, in every case:
After, when no datasource is declared and no driver serves
sys_metadata:After, when a datasource exists and its read failed: unchanged from "Before".
How the arm is chosen
New private
sysMetadataHasNoStorage(), asked only once the read has already failed (a healthy boot pays nothing). It reads the engine's own routing, never the text of the error the read raised. It answers "no storage" only when all three are empty:getDriverForObject('sys_metadata'): a driver serves it, so the read itself failed: datasource problem.resolveEffectiveDatasource('sys_metadata'): it is bound (explicit, mapping, lifecycle or package) to a named datasource whose driver is missing: that datasource is declared, datasource problem.listUnavailableDatasources(): the connection layer recorded a declared datasource it could not use (policy-refused, or a connect failure the operator waved through): datasource problem. This one matters because such a boot also has no registered driver, so drivers alone cannot tell it from "no storage".The three remedies live in one
private static readonly NO_STORAGE_REMEDIESbeside the method, with a note that the CLI line is their authority.File surface:
restoreMetadataFromDbregion ofpackages/objectql/src/plugin.ts, the pins, one changeset. Nothing inpackages/cli, the start-order region,onMetadataMutationorfollowRuntimeObjectCopy.Pins and ablation
New
packages/objectql/src/plugin-restore-metadata-no-storage.test.ts(6 cases), over a realObjectQLengine in every case:error.ObjectStackProtocolImplementationover a driverless engine: the boot read really fails (DB hydration skipped: … No driver available for object 'sys_metadata') and the line is the no-storage one.sys_metadatabound to a named datasource with no driver; a declared datasource marked unavailable by the connection layer.The existing
plugin-restore-metadata-outage.test.tsusedql: {}as its engine on the premise "the engine is untouched by this path". That premise no longer holds, so itsmakePlugin()now builds a real engine with one registered driver (the "a datasource exists and its read failed" shape those cases always meant). Its nine assertions are unchanged.Ablation, direction predicted before running and observed (mutation applied with
scripts/ablation-replace.mjs: anchor hit once, blob changed, restored to the HEAD blob withgit diff HEADempty; the subject issrc/plugin.tsimported directly, so no rebuild applies):false(branch condition ablated)truegetDriverForObjectproberesolveEffectiveDatasourceprobelistUnavailableDatasourcesprobeSo each probe is held by its own control, and no (a) pin passes by accident.
Verification (every reading at head f273dab)
pnpm --filter @objectstack/objectql exec vitest run --maxWorkers=2 src/plugin-restore-metadata-no-storage.test.ts src/plugin-restore-metadata-outage.test.ts: 15 passed.pnpm --filter @objectstack/objectql test: 424 files, 8129 tests passed.pnpm --filter @objectstack/objectql typecheck(after the last test edit): exit 0;tsc --noEmitclean andcheck:test-typecheckOK with the ledger unchanged (39 files, 233 errors held). The new test file is intsconfig.test.json's program (1 hit) and not intsconfig.json's (0), which is the layer that compiles tests.eslint --no-inline-configon the three changed source and test files: exit 0.node scripts/pm/dispatch-gates.mjs --commands: 64 commands, plus the 53 artifact-roster and 11 wide-population families it prints; 127 distinct commands, all run: 123 exit 0. Two exit-3 prerequisites (check:dual-build-cjs-loads,check:published-readme-exports) were rerun after a fullpnpm buildand are exit 0;check:type-check-debthit my 280 s runner window on the first pass and is exit 0 on its rerun.dispatch-gates --ranreconciles 64 derived, 64 run, 0 unrun.check-partof-closing-keywordneeds a pull request body to judge, so it first answered exit 2 (not wired) and was rerun with this body asPR_BODY: exit 0.check-closing-target-claimandcheck-single-claim-paths(exit 2: they read the pull request and the card through the API, and a local run has no pull request yet; CI supplies it) andpm/check-governed-queue-guard(exit 1: it reads a workflow event payload; this diff touches no governed surface).Acceptance notes
⚠ No storage:line andNO_STORAGE_REMEDIEShere. Nothing pins them equal, and sharing one constant would cross intopackages/cli, outside this card's surface. Noted, not filed (drift not yet exercised).OS_DATABASE_URL,--dev). A host that boots a kernel by hand with no driver reads the same three; "mount a storage driver" is the one that applies to it.start()later does not rewrite what this process already restored.Generated by Claude Code