Skip to content

fix(rest): GET /meta/diagnostics refuses an unknown query parameter name instead of answering the full sweep - #27913

Merged
objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/project-thread-lmg708
Oct 30, 2026
Merged

objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/project-thread-lmg708

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Elon · project thread

Fixes #27633

Clause-②: no (narrowing)
Metadata-protocol: none
Skills-docs: follow-up #27632 (the same /meta/diagnostics query table; the unknown-name refusal is added to that card in a comment)

BREAKING for a caller that sends GET /meta/diagnostics a query parameter outside type, severity and package: it was ignored and now answers 400 INVALID_REQUEST. The remedy is to drop it or use the supported spelling (package, not packageId). ADR-0087 disposition: not-required (no-migration-prescription), in the changeset.

维护者速读

What changes

  • GET {metaPath}/diagnostics declares its closed query-parameter set, META_DIAGNOSTICS_PARAMS = ['type', 'severity', 'package'], measured from the handler's read points and matching what both SDKs send.
  • The handler asks the shared closed-set decision, unknownQueryParamRefusalMessage from query-allowlist.ts (the one refuseUnknownQueryParams writes; no second helper), and throws the route's own diagnosticsRefusal. So the refusal is 400 INVALID_REQUEST in the nested { error: { code, message } } envelope, the code every other thrown refusal of this route answers since meta diagnostics: an unknown type, severity or package answers a silent 200 all-clear (scannedTypes 1, zero issues) instead of a refusal; aliases key stats non-canonically #27514. The message names the unknown parameter and lists the supported set.
  • Recognition runs before the repeated-parameter refusal, as the closed-set rule orders it. The multiplicity call now reads the same list (all three names are single-valued).

Why

#27514 / #27620 closed the false all-clear for unknown VALUES. An unknown NAME was still dropped: ?typ=view answered 200 with every type swept, and a caller who believed the answer was narrowed could not tell.

Verification

  • Pinned on main first (b13a1ed): of 5 new cases, the 4 refusal pins were red (?typ=view, ?packageId=, an unknown name beside known ones with an empty value, an unknown name with a repeated known one) and the preservation pin (all three known names together answer 200) was green.
  • Ablation: disabling the new throw reds exactly those 4; restored, 16/16 green.
  • pnpm --filter @objectstack/rest exec vitest run: 352 files passed, 7025 tests passed, 350 skipped. tsc --noEmit on packages/rest: exit 0.
  • dispatch-gates --ran: 60 derived, 58 run (exit 0), 2 NOT-MEASURED (exit 3, prerequisite in a fresh container: check-plugin-teardown-shape --self-test needs an unshallow clone, check:dual-build-cjs-loads needs every package's dist), 0 UNRUN.
  • pnpm lint: exit 0.
  • Dogfood battery at the final head: posted as a PR comment when the run completes.

Human Minimum

  • 1. Schemas start as Zod; packages/spec holds no runtime logic. (not touched)
  • 2. Every name I added uses the casing for its kind.
  • 3. No formatter ran; pnpm lint passes.
  • 4. One issue, one branch off an up-to-date main, one pull request.
  • 5. A change that publishes carries a changeset, and both declaration lines are filled in here and in the changeset.
  • 6. packages/spec touched: n/a.
  • 7. No hand edits to content/docs/references/, packages/*/CHANGELOG.md or content/docs/releases/.
  • 8. Title, body and comments are in English.
  • The Before Submitting list in CONTRIBUTING.md is done.

🤖 Generated with Claude Code

https://claude.ai/code/session_016WV4cxscKusnsCNkzSsjxM

…ame instead of answering the full sweep

The diagnostics route now declares its closed query-parameter set (type,
severity, package) through the shared closed-set decision, and refuses any
other name with 400 INVALID_REQUEST in the route's nested envelope. Before,
`?typ=view` was ignored and answered 200 with every type swept.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016WV4cxscKusnsCNkzSsjxM
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation tests tooling labels Oct 30, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/rest, touching 2 documentable anchor(s).

1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/permissions/system-context.mdx (via registerMetaCollectionRoutes (symbol, a top-level function))
What this run could not see
  • the SDK route bridge reached 58 of 210 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 19 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 2a3dec2d498ea8467460f07dd3fb0e46ddc3cf8d → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 75677847d69df8a378b48871d8854f0db5ecf08a — the merge of head 2e2306d961bb53b442887ab410252f8ccf204661 into base 2a3dec2d498ea8467460f07dd3fb0e46ddc3cf8d, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 75677847d69df8a378b48871d8854f0db5ecf08a && git checkout 75677847d69df8a378b48871d8854f0db5ecf08a
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 2a3dec2d498ea8467460f07dd3fb0e46ddc3cf8d 2e2306d961bb53b442887ab410252f8ccf204661 && git checkout -B drift-repro 2a3dec2d498ea8467460f07dd3fb0e46ddc3cf8d && git merge --no-ff 2e2306d961bb53b442887ab410252f8ccf204661

node scripts/docs-audit/affected-docs.mjs --json 2a3dec2d498ea8467460f07dd3fb0e46ddc3cf8d

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 2a3dec2d498ea8467460f07dd3fb0e46ddc3cf8d → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#3 · os-elon-musk · session_016WV4czuTWNgKR22fKv7jia

Contract-review-claim: session_016WV4czuTWNgKR22fKv7jia · head 2e2306d961bb53b442887ab410252f8ccf204661 · 2026-10-30T03:37Z

The lane seat runs the CONTRACT_REVIEW_TIER review this PR owes (Clause-②: no (narrowing)).

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#3 · os-elon-musk · session_016WV4czuTWNgKR22fKv7jia

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 2e2306d961bb53b442887ab410252f8ccf204661
Local-runs: none
Scope: packages/rest/src/meta-collection-routes.ts, packages/rest/src/rest-meta-diagnostics-unknown-params.test.ts, .changeset/27633-diagnostics-unknown-param-name.md + packages/rest/src/query-allowlist.ts, packages/rest/src/meta-item-read-gate.ts, packages/rest/src/package-routes.ts, packages/rest/src/rest-server.ts, packages/client/src/index.ts, ui/packages/data-objectstack/src/metadata-client.ts, content/docs/deployment/validating-metadata.mdx, packages/rest/src/rest-route-ledger.ts

① Derived judgments

② Semver level

③ Boundary flags

  • Nested { error: { code, message } } envelope (ADR-0112) kept; message bounded by truncateClientMessage.
  • packages/spec untouched; no new error code; route ledger row unchanged (response schema unaffected).
  • META_DIAGNOSTICS_PARAMS is module-private; the ingress policy text says "an exported readonly string[]" — nit only, no consumer needs it.
  • Branch is the thread's platform-assigned claude/project-thread-lmg708 (recorded exception in the claim).

④ Design judgment

Implemented-by: claude/project-thread-lmg708
Reviewed-by: session_016WV4czuTWNgKR22fKv7jia (seat subagent)

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#3 · os-elon-musk · session_016WV4czuTWNgKR22fKv7jia

Seat ACCEPT · PR #27913 · head 2e2306d961bb53b442887ab410252f8ccf204661 · 2026-10-30T03:57Z

维护者速读

GET /api/v1/meta/diagnostics now refuses a query parameter name outside its closed set: type, severity and package. The answer is 400 INVALID_REQUEST, in the route's nested envelope. It names the unknown parameter and lists the set, and it comes before any sweep work. Before, a misspelt name (?typ=view) was ignored and the caller got an unfiltered all-clear. Every parameter the docs, the SDK and the Console send is still accepted. A repeated known parameter still answers VALIDATION_ERROR, as before; that is disclosed in the changeset.

席位核对

ACCEPT

@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 41001938717 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

分类:failure —— 按下面的日志分诊。

失败的 job(日志抽取,best effort):

  • Test Core (2/8) — 失败步骤: (无步骤级结论)(日志不可读,点进 job 看)
  • Test Core — 失败步骤: Verify test shard results(日志不可读,点进 job 看)

↳ 失败原因 是判读的关键:超时(Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言(AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️ 断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError。 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

  • ⚠️ 本次没有可用的聚合签名(1 个 job 的日志读不到)—— 这不是「没有同签名的其他 PR」,是这一轮没测到。跨 PR 聚合本次不可用,请手工比对其他 PR 的同类评论。
  • ⚠️ 24h 评论账本没读完(超过 5 页仍未读到窗口尽头),所以上面的「不同 PR 数」是下界,不是全量。

历史信号:

  • 本 PR 过去 24h 无队列失败记录(首次)。
  • 过去 24h 队列共有 0 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#3 · os-elon-musk · session_016WV4czuTWNgKR22fKv7jia

Dequeued 2026-10-30T05:09Z with CI_FAILURE, but the failure is not this PR's. In merge-group run 41001938717, Test Core (2/8) died with the runner-infrastructure annotation "The hosted runner lost communication with the server". The Test Core summary job then failed on "Verify test shard results". No test body reported a failure. The head is unchanged (2e2306d9), and the PR's own checks are green (35 success, 0 failed). The seat is re-arming auto-merge once, which re-queues the PR. A second failure in the queue would be treated as real.

@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 30, 2026
Merged via the queue into main with commit 9cd54fa Oct 30, 2026
49 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-lmg708 branch October 30, 2026 05:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

rest: GET /meta/diagnostics declares no closed query-parameter set, so a misspelled parameter name (?typ=view) answers a 200 full sweep

2 participants