Repository navigation
fix(rest): GET /meta/diagnostics refuses an unknown type, severity or package, and keys an alias by its canonical type (#27514) - #27620
Conversation
…kage and keys aliases canonically An unknown ?type=, ?severity= or ?package= used to be answered 200 with zero issues, and ?type=views keyed its stats `views`. The route now refuses all three with 400 INVALID_REQUEST in its one nested envelope: severity in the route, type by the list door's own verdict after the sweep, and package in the sweep (not installed and no item bound to it). The sweep keys a named type by its canonical spelling. Claude-Session: https://claude.ai/code/session_01L6bhgTiSuMwZbnqidCwkCM Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L6bhgTiSuMwZbnqidCwkCM Co-authored-by: Claude <noreply@anthropic.com>
…FindOnePredicate, pinned in the engine-double ledger Claude-Session: https://claude.ai/code/session_01L6bhgTiSuMwZbnqidCwkCM Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 2 package(s): 21 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 — 26 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 e2aeeee07e6ce653b3ba10327c09b1e96654ec90 && git checkout e2aeeee07e6ce653b3ba10327c09b1e96654ec90
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 77e993ea29f1a96126f46a5e3748847ce5eb5dd3 59ef3dbe1e68f9765be5d7f503107bb7b8d91b60 && git checkout -B drift-repro 77e993ea29f1a96126f46a5e3748847ce5eb5dd3 && git merge --no-ff 59ef3dbe1e68f9765be5d7f503107bb7b8d91b60
node scripts/docs-audit/affected-docs.mjs --json 77e993ea29f1a96126f46a5e3748847ce5eb5dd3
|
|
domain:server#2 · os-sales · session_01L6bhgUCizoYWQVJWjhdR6F Contract-review-claim: session_01L6bhgUCizoYWQVJWjhdR6F · head 59ef3db · 2026-10-29T14:59Z |
|
domain:server#2 · os-sales · session_01L6bhgUCizoYWQVJWjhdR6F Contract reviewServed-tier: Read-only: the net diff Review act: 2026-10-29T15:21Z ① Derived judgmentsAccept-set and public-surface changes the diff implies, each judged against the cited code:
② Semver level
③ Boundary flagsThe os-dev-report (6500171775) declares
Two flags of my own, neither a blocker:
④ Design judgmentCheck-runs on the head, read at 2026-10-29T15:18Z: 48 runs, 34 success, 14 skipped, none failed, none in progress. The ten required contexts:
The narrowing against the published spec on Implemented-by: VERDICT: PASS Generated by Claude Code |
|
domain:server#2 · os-sales · session_01L6bhgUCizoYWQVJWjhdR6F 维护者速读(席位终稿)
席位意见: ACCEPT。三类拒收都复用既有判决(列表门的未知类型判决、包绑定读),无新错误码。 席位核对ACCEPT · head
|
domain:server#2 · os-sales · session_01L6bhgUCizoYWQVJWjhdR6F
Requested by Sales · project thread
Fixes #27514
Clause-②: no (narrowing — owes one contract-review-tier review before the queue)
Metadata-protocol: none
Skills-docs: follow-up #27632
维护者速读
GET /meta/diagnostics遇到不认识的类型、严重级别或包,不再回 200「全部正常」,而是回 400 并说明原因;用别名(如views)查询时统计按标准类型名(view)给出;该接口所有拒绝统一成一种错误信封。packages/rest/src/meta-collection-routes.ts(诊断路由:severity 校验、类型拒绝、统一信封);packages/metadata-protocol/src/protocol-meta-diagnostics.ts(未知包拒绝、标准类型键);packages/rest/src/rest-meta-diagnostics-unknown-params.test.ts。pnpm --filter @objectstack/rest exec vitest run src/rest-meta-diagnostics-unknown-params.test.ts,预期 11 个用例全部通过。What changes
Measured on
main@556f3230through the real route (a realObjectStackProtocolImplementationover a realSchemaRegistry), every premise on the card held:?type=zzz_unknownscannedTypes: 1,stats.zzz_unknownzerosINVALID_REQUEST?severity=bogusINVALID_REQUEST?package=com.nope.missingINVALID_REQUEST?type=viewsstatsandentrieskeyedviewsview, byte-equal to?type=view?type=viewes(#8924){ error: '…', code }{ error: { code, message } }, same message?type=view&type=object(#6877)VALIDATION_ERRORerrororwarning; an empty value keeps the default, as an emptytypekeeps the full sweep.refuseUnknownMetaListType(the static spelling contract, then the live type set fromgetMetaTypes), soGET /meta/:typeand the diagnostics door agree on which names are types, and a plugin type the deployment carries live still answers. Asked after the sweep so the [finding] getMetaDiagnosticsscannedTypesstill overstates the swept set when a type is benignly skipped — no payload field lets a caller detect partial coverage #8924 near-miss refusal, which names the spellings the caller reached for, keeps answering first.getMetaItemsof typepackage, the registry's records) does not carry it. An installed package with no items still answers its zeros, and rows bound to an uninstalled package are still swept. Fails open when the listing cannot be read.canonicalMetaUrlType(type), the foldcanonicalMetaTypeapplies at every/metaentry point. A spelling the fold does not map (viewes,fieldz) is unchanged, so the [finding] getMetaDiagnosticsscannedTypesstill overstates the swept set when a type is benignly skipped — no payload field lets a caller detect partial coverage #8924 refusal still sees what the caller wrote.400 INVALID_REQUESTthe route throws or receives now leaves in the nested{ error: { code, message } }its repeated-parameter refusal already used (message bounded bytruncateClientMessage). Everything else keepshandleRouteError. Every code is an existing ledgered one (INVALID_REQUEST,VALIDATION_ERROR); no new code, nothing inpackages/spec.metadata-protocol/src/protocol.tsis untouched: the package check usesgetMetaItems, whichMetaDiagnosticsHostalready carries.Tests
packages/rest/src/rest-meta-diagnostics-unknown-params.test.ts(11 cases, through the HTTP route): the three refusals, both envelope cases, the alias case, and six preservation cases (severitywarning/error/empty/absent, an installed package,package=all, an installed package with no items, items bound to an uninstalled package, a live plugin type). Onmain: 5 failed, 6 passed. On this branch: 11 passed.protocol.diagnostics-store-outage.test.ts(stats.fieldstostats.field) andrest-server-meta-read-org-scope.test.ts(stats.viewstostats.view, plusstats.viewsabsent).pnpm --filter @objectstack/metadata-protocol test: 279 files passed, 21781 tests passed.pnpm --filter @objectstack/rest test: 335 files passed, 6589 tests passed.typecheckon both packages: exit 0.scripts/ablation-replace.mjs(anchor hit 1 to 0, restore blob == HEAD,git diff HEADempty): severity check, type verdict, envelope writer (all three inrestsrc, read by the test from source), and the package refusal and canonical key (inmetadata-protocol, which the test reads fromdist/: rebuilt,ablation-dist-preflightmarker present exit 0, then rebuilt again and--absentexit 0). Each leg turned exactly its own pins red: severity 1, type 1, envelope 4, package 1, alias 1.Acceptance notes
scripts/engine-double-contract.pinned.jsongains the new rig'sfindOnedouble (written bycheck-engine-double-contract.mjs --write); the rig opensfindOnewithassertEngineFindOnePredicate. That ledger is outside the claimed file surface and is the gate's own bookkeeping for the new test.?type=X&package=P, the package refusal reads the swept types only, so an uninstalledPwhose orphan rows are all of other types is refused with that type filter. Without the filter the full sweep finds them.claude/project-thread-nx5qkr, notclaude/issue-27514-…; the claim records it.Verification
All readings on head
59ef3dbe.node scripts/pm/dispatch-gates.mjs --commands: 69 derived families, each run with its exit code captured before any pipe, all exit 0.--ran: 69 derived, 69 run, 0 NOT-MEASURED, 0 UNRUN.pnpm lint(repo-wide,eslint . --no-inline-config): exit 0. eslint onpackages/restandpackages/metadata-protocol: exit 0.pnpm --filter @objectstack/dogfood testunderos-verify-lock.sh, no shard, no path, closure built): the lock'sVERDICT command-exit 0; Test Files 350 passed, 3 skipped (353); Tests 2817 passed, 20 skipped (2837); no failure.scripts/engine-double-contract.pinned.json;git merge-tree --write-treeof this head with each answers exit 0 (no conflict).Generated by Claude Code
🤖 Generated with Claude Code
https://claude.ai/code/session_01L6bhgUCizoYWQVJWjhdR6F