Uh oh!
There was an error while loading. Please reload this page.
fix(metadata,objectql,metadata-protocol): require a missing-table error to name the table that was read - #13437
Conversation
…e fixtures phrase per-object faults
📓 Docs Drift CheckThis PR changes 3 package(s): 32 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 6 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 24 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 0c1e5a2a184b3e48c81ea522bb59e242c30cb430 && git checkout 0c1e5a2a184b3e48c81ea522bb59e242c30cb430
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 3d10755f02070fa693bd7004c645255ad1fef544 6316ca068f28f377be1c9e0af38409af2df07331 && git checkout -B drift-repro 3d10755f02070fa693bd7004c645255ad1fef544 && git merge --no-ff 6316ca068f28f377be1c9e0af38409af2df07331
node scripts/docs-audit/affected-docs.mjs --json 3d10755f02070fa693bd7004c645255ad1fef544
|
Uh oh!
There was an error while loading. Please reload this page.
Fixes#13324
isMissingTableErroranswers the one question that licenses a fail-soft caller totreat an empty result as the truth: "did this READ fail because the table has not
been provisioned yet?". It matched the shape of the dialect phrase and never
asked which table the phrase names.
The defect, measured
Driven against a real libsql file database on this branch:
sys_metadataexists and may be backed by rows. The phrase matches the regex, sothe read was answered benign — a false "benign", the direction the module's own
docblock calls far more expensive than a false "real".
Why the phrase alone cannot decide it
The suggested cheaper route was to exclude the measured "names something else"
shapes, as
excludesalready does for the Postgres column phrasings. Measured, thatroute cannot work: a view over a missing base table and a genuine missing table the
caller happened to qualify produce byte-identical messages.
no such table: main.absent_baseno such table: main.ordersNothing in the string separates them. The only discriminator is whether the name in
the phrase is the name that was asked for, so the read's name is a parameter
rather than another regex. A shape-only exclusion would have had to give up one of
those two verdicts, and giving up the second is a lost true positive.
The change
A third front-exclusion channel on
MISSING_TABLE, alongside the#6347sub-object phrase it is the sibling of. That one recognises a failure about
something inside a relation; this one recognises a failure about a different
relation. Both end the question with
false, and both can only ever subtractbenign verdicts, never add one:
The comparison folds away schema/database qualifiers, the legacy
namespace__shortprefixresolveTableNamestrips, and case, so a tolerant matchnever manufactures a loud verdict for a genuine missing table.
The parameter is optional.
@objectstack/metadatais a published package and./errorsa published subpath, so a required parameter would be a breaking APIchange and a major bump. Omitting it reproduces the previous behaviour exactly; all
17 in-repo call sites pass it. The cost of that choice is that the narrowing is
opt-in per call site, and it is stated in the JSDoc.
Call sites
Seventeen, across six files in three packages —
packages/metadata,packages/objectql,packages/metadata-protocol. Five of the thirteen files inthe dispatched surface turned out to be prose only (
packages/restx2,packages/runtime,packages/types, andpackages/drivers/driver-sql), so thislane's hot serial file is not written by this PR at all.
One shared helper needed more than a name.
rethrowUnlessMetadataStoreUnprovisionedserves callers reading four different tables (
sys_metadata,sys_metadata_audit,sys_metadata_commit,sys_metadata_history); the firstdraft hardcoded
sys_metadataand turneddiffMetaItem's genuinely unprovisionedsys_metadata_historyinto a loud failure. Its parameter is required — it isprivate, so the compiler can enforce it — and each caller now names what it read.
Two test fixtures were unfaithful, and are corrected rather than re-baselined
Neither expectation moved. Both fixtures threw one fixed error for every read
regardless of object, so a
sys_organizationprobe received a fault phrased formy_app_widget, and a test titled "an unprovisioned sys_metadata_commit" injecteda fault phrased for
sys_metadata. No driver does that. The injectors now phrasethe fault for the table actually read; the assertions are untouched.
Evidence
Ablation — removing the new channel turns exactly the 3 defect pins red and
leaves all 72 other cases green, so the true-positive pins do not ride on it.
Mutation and restore were each confirmed on disk by blob hash against HEAD.
The card's own repro, A/B on one tree (ablated dist vs fixed dist,
os migrate plan,NODE_ENV=production):sys_metadataview reads reported at ERRORsys_metadata_activationgenuine-miss WARN (true positive)Three — the count the card measured, and the phrase named
mainexactly as itrecorded.
Fence: no true positive regressed. Every shape is pinned with the read named
— sqlite
no such table: Xand its schema-qualified form, Postgresrelation "x" does not exist, MySQLtable "x" doesn't exist,unknown table,the SQLSTATE, symbolic-code and errno limbs, and the driver wrapper that carries
the original as
cause.Tests and gates, all at
6316ca068f:@objectstack/metadata698,@objectstack/objectql4287,
@objectstack/metadata-protocol2017 (10 skipped), plus 51 runtime real-driverfail-soft probes. Full-repo
pnpm lintandpnpm build(71/71) green. 38 of the 40derived gates green;
check-test-completenessandpm/check-half-statesare exit 3NOT MEASURED (they need a CI turbo log and a GitHub credential respectively), which
both gates declare is not a finding.
#13273 is not addressed here and remains open — it only adds one more reader of this
predicate, a consequence of the gap rather than its cause.
Generated by Claude Code
Generated by Claude Code