Uh oh!
There was an error while loading. Please reload this page.
fix(metadata-protocol): propagate the metadata-store 503 out of getMetaDiagnostics (#8855) - #8923
Conversation
…taDiagnostics (#8855) The per-type read in `getMetaDiagnostics` was wrapped in an untyped `catch` that `continue`d. The comment named a benign reason ("type not listable in this kernel scope") and that reason is real — but the catch took everything else with it, including the 503 `getMetaItems` raises for a `sys_metadata` read that failed for any reason other than "not provisioned yet" (#5532). Swallowing it put the emptiness back one layer up and published it as a number: `stats[t]` was never written (the type ABSENT from the response, not zero — the Studio directory tile simply loses it) and `total`, which counts entries that failed validation, read 0 because an unreadable store contributes none. A diagnostics endpoint reported "0 problems" precisely when it could read nothing. The catch is narrowed, not deleted: a 503 is rethrown unchanged, every other failure still skips that one type. No response field is added. The envelope is propagated rather than re-classified, so the driver error stays on `cause`. ADR-0110 D3 — a miss and an outage are different facts with opposite dispositions, and a consumer must never read one as the other. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XeQRiAa7vYRVX5Fog7Zby8
…gnostics-store-outage-503
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
📓 Docs Drift CheckThis PR changes 1 package(s): 3 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
⛔ 1 release-owned page(s) also reference the affected code. These are read-only:
|
Uh oh!
There was an error while loading. Please reload this page.
Fixes#8855
Implements triage ruling
5302846257as written: classify by error type through the existing discrimination and propagate the 503. Not "delete the catch"; no payload field added.The defect
getMetaDiagnosticssweeps every metadata type and publishes four numeric facts about the corpus. Its per-type read was wrapped in an untypedcatchthatcontinued. The comment above it named a benign reason — "type not listable in this kernel scope" — and that reason is genuinely real, which is why this PR narrows the catch instead of removing it. The catch took everything else with it, including the one error the callee exists to raise.getMetaItemsclassifies a failedsys_metadataread by error type throughrethrowUnlessMetadataStoreUnprovisioned: it returns normally for the one benign driver reason (isMissingTableError— the table is not provisioned yet, soitems: []IS the truth) and throws a 503 for every other read failure. That is the discrimination #5532 introduced so an outage would stop looking like emptiness.getMetaDiagnosticscaught the 503 back into emptiness one layer up, then published the emptiness as a number.Measured before writing anything
The card's repro was written but never run, and triage asked for prediction-then-measurement. Prediction was recorded first; the run matched it exactly. Protocol-level, on
origin/main@8664a2c99, with an engine whose every read rejects:The benign run is what makes the harm legible — it is the same payload minus the
stats. Two user-visible harms out of onecatch:stats[t]is never written, so an unreadable type is absent from the response rather than zero. The Studio directory tile the field's own doc names loses the type, byte-shaped like an environment that declares none of it.totalcounts entries that failed validation, and a store nobody can read contributes none — so the endpoint whose whole job is reporting problems answeredtotal: 0at the exact moment it could read nothing. Green was the failure mode.scannedTypesreported the full 26 in both runs: it is computed from the intent (targetTypes.length, fixed before the loop) and never decremented oncontinue.The fix
One narrowed
catchinside one method. A 503 arriving from the read is rethrown unchanged; every other failure still skips that one type, so a kernel scope that cannot enumerate one type does not fail the whole governance sweep. ADR-0110 D3: a miss and an outage are different facts with opposite dispositions, and a consumer must never read one as the other.The envelope is propagated, not rebuilt. Re-running
isMissingTableError/rethrowUnlessMetadataStoreUnprovisionedon what arrives here would re-wrap an already-shaped 503 in a second one and displace the driver error riding ascause— the objectlogWithheldServerFaultprints for the operator (#5437).isMissingTableErrorwalks the cause chain, so that re-wrap is not hypothetical. The pin assertscauseby identity, which is what proves propagation rather than reclassification.The test is
status === 503rather than the narrower code check deliberately, in the directionrethrowUnlessMetadataStoreUnprovisioned's own doc argues: a false "benign" silently mis-answers a question nobody can re-ask, while a false "real" costs one 503 the caller can retry.Scope held
rethrowUnlessMetadataStoreUnprovisionedhas seven other call sites and reaches two live sibling dispatches; this PR calls the discrimination's result, it does not modify the producer. The diff sits entirely insidegetMetaDiagnostics.packages/rest. Re-measured rather than trusted: the card's UNVERIFIED note put the handler atrest-server.ts:4759, but refactor(rest): extract the ADR-0112 error/fault-classification prologue out of rest-server.ts #8887 moved it to:3478. It already wraps the call intry/catchand routes throughhandleRouteError, which preservesstatus— so the 503 reaches the wire with no change in that lane.organizationIdleft unmeasured, per the ruling — it belongs to the [finding] The REST metadata write door stamps every audit row env-wide, so #8747's org scope cannot separate tenants on the REST-authored path #8805 tenant-scoping thread.Verification
New pin
packages/metadata-protocol/src/protocol.diagnostics-store-outage.test.tscarries the discriminating control in the same file, because a refusal pin with no positive control cannot show the discrimination is selective rather than blanket:causeidentitysys_metadatacount: 0"0 problems" is the right answer in the benign cell, and it is exactly the answer a blanket change would have kept producing in the wrong one.
Ablation — direction predicted before running: ordinary red, on the outage half only. Restoring the bare
} catch { continue; }gives 3 failed / 3 passed, and the three reds fail by resolving with the defect's payload verbatim:The three benign/selective/healthy controls stay green through the ablation — that separation is what shows the change is the outage split and not a blanket throw.
Suites (all at the head quoted below):
@objectstack/metadata-protocol— 103 files / 1493 tests passed...@objectstack/metadata-protocol): the three that touch this surface —@objectstack/objectql210 files / 3675 tests,@objectstack/rest118 files / 1948 tests,@objectstack/client23 files / 301 tests.objectqlcarriesdiagnostics-clean-baseline.test.tsand is the consumer that went red on fix(metadata-protocol): fold publish Phase 2's object-table lookup at the producer #8867 after a green edited-package suite.Gates — derived from the actual changed paths via
scripts/pm/dispatch-gates.mjs, then re-run at the post-merge head:check:changeset-gate-self-tests,check:cross-package-test-inputs,check:durability-log-level,check:filter-alias-parity,check:objectui-changeset,check-adr-0087-registration,check-changeset-no-major,check-empty-changeset, plus the convention-triggeredcheck:query-options-erasure,check:type-check-coverageandcheck:type-check-debt --re-measure, andcheck:nul-bytes/check:engine-double-contract/check:error-code-casing/check:adr-anchorsadded beyond the derived list. All pass.TEST_DEBT measured directly on a built workspace, since
@objectstack/metadata-protocolsits in the DEBT ledger andpnpm typecheckcarries zero information about it: full workspace build, then--re-measurere-ran tsc per ledger entry —none above its recorded number, the package's recorded 63 unchanged, so the new test file contributes 0 errors. No ceiling raised.Verified at
b1de760d6(post-merge head; the union above was run at this sha).Generated by Claude Code
Generated by Claude Code