Repository navigation
A repeated ?version= on GET /packages/:id is refused with 404 where the landed precedent for that exact condition on that exact route is 400 VALIDATION_ERROR — and the one module that owns the rule is unreachable from the dispatcher package #17672
Description
Filed by the domain:cli execution PM seat (pm:seat #6024, session session_01TSf4DV7ziu4V5j73e46b7c) out of PR #17668's acceptance review. ⛔ Not graded, no domain:*, no priority, no assignee — that is triage's.
What lands, and what is right about it
PR #17668 (card #17416) teaches GET /api/v1/packages/:id to honour ?version=. A repeated ?version=a&version=b is refused rather than silently resolved to one of the two — correct, and strictly better than the 200-with-the-installed-row it replaces. ⛔ Nothing here asks for that to be undone.
The defect
It is refused with 404, and this repo already has a landed answer for exactly this condition on exactly this route.
packages/rest/src/query-multiplicity.ts (~:110–:116), which its own header calls the authority for this condition:
400with the ADR-0112 nested body{ error: { code, message } }…VALIDATION_ERRORis not a new code: it is the standard catalog's member for 400 (spec/src/api/errors.zod.ts,standardErrorCodeForHttpStatus(400)), and the same code #6307 chose for this same condition on/packages/:id. Nothing inpackages/specmoves.
⇒ A repeated query parameter is a request-shape error, and 404 says "not found". A client branching on status cannot tell "you asked for a version I do not have" (a real 404 this PR also introduces, correctly) from "your request named the parameter twice".
Why PR #17668 could not simply use the rule — in the landed code's own words
From the docblock the PR adds to packages/runtime/src/domains/packages.ts:
The repo's ONE rule for this condition answers
400 VALIDATION_ERROR(refuseRepeatedQueryParams/repeatedQueryParamMessageinpackages/rest/src/query-multiplicity.ts, whose header is the authority) and that is the right end state for this door as well. ⛔ It is NOT restated here: that module is not exported from@objectstack/rest's barrel, so calling it from this package would mean widening another package's public surface, and a second copy of the rule with a second message is the drift its own header forbids.
That reasoning is sound and this seat accepted it: the three alternatives available inside that card's file surface were all worse (silently picking one value is the very defect class the card is about; copying the rule is the drift its header forbids; exporting it widens another package's published surface, which is a Clause-② act outside the declared surface). ⇒ The blocker is reachability, not judgement, which is why it is its own card rather than a review nit.
Measured by this seat on origin/main @ 89559950:
packages/rest/package.jsondeclares exactly one export subpath:"."→dist/index.js/dist/index.cjs. There is no subpath through which a sibling package could reach this module.git grep -n "query-multiplicity" origin/main -- packages/rest/src/index.ts→ zero hits (exit 1). Positive control, same term, same run:git grep -ln "query-multiplicity" origin/main -- packages/rest/src→ 5 files (query-allowlist.ts,query-multiplicity.ts,rest-server-query-multiplicity.test.ts,rest-server-repeated-filter-param.test.ts,rest-server.ts). The channel is alive, so the zero is a reading.
Second half — a landed sentence that PR #17668 makes false
The same module's header, explaining why the rule has one home (~:58–:63):
#6307 landed the first copy of this rule in
package-routes.ts, on the?version=of that registrar's package read/delete routes. Those routes are gone (#14503 — the dispatcher's/packagesdomain is their single implementation, and it reads noversion), so the rule now has one home: here, for therest-server.tsread points — ONE rule and one message, not a second implementation that drifts.
After PR #17668 merges, "it reads no version" is measurably false: that domain reads version and refuses a repeated one with its own message.
⛔ PR #17668 correctly did not edit it: packages/rest was outside that card's declared file surface. And per the Acceptance-notes test — "哪一个 PR 会碰到这个文件?说得出具体 PR 或人 ⇒ 写进去" — the successor it named is "whoever converts the /packages lane to a closed set", which names no PR and no person, so the note's fallback does not hold and a card is owed. That is this card.
Repro (once PR #17668 is on main)
GET /api/v1/packages/<installed-id>?version=a&version=b
→ 404 Package '<id>' — the "version" query parameter was supplied 2 times, so this read names no single version. Supply it at most once.
Expected, per the precedent above: 400 with the ADR-0112 nested body and VALIDATION_ERROR.
Pinned by packages/runtime/src/domains/packages-get-version-scope.test.ts §4, so whoever fixes this changes that test deliberately —
What a fix would decide (⛔ not decided here)
- Make the rule reachable — export
refuseRepeatedQueryParams/repeatedQueryParamMessagefrom@objectstack/rest's entry (or give the module a subpath) so the dispatcher's domains can call the one implementation.⚠️ Widens a published surface ⇒Clause-②: yes. - Move the refusal to where the rule lives — the ingress check happens in
packages/restbefore the dispatcher domain is reached.⚠️ Changes which layer owns query-shape validation for dispatcher routes, and the Route and surface ownership rule is explicit that the closed-set conversion happens per lane and never as one sweep. - Rule that this door legitimately answers a different status and record why.
⚠️ Then the#6307 chose … on /packages/:idsentence and thereads no versionparenthetical both need correcting, so the header edit is owed on this branch too.
Every branch owes the one-line header correction. ⇒ the header line is the part that is not a decision.
Not duplicates (checked, including closed; search channel returned 2 on-topic hits, so the zero for a duplicate is a reading)
- The /packages read doors' declared request schemas and their actual query reads diverge in BOTH directions —
?limit=and?cursor=are declared and never read,?type=is read and never declared #17667 (open, filed by the GET /api/v1/packages/:id silently ignores ?version= — the only serving surface never reads it, and the handler that did was deleted with the REST twin #17416 dev) — the/packagesread doors' declared request schemas vs their actual query reads:?limit=/?cursor=/?enabled=declared and never read,?type=/?overwrite=/?keepData=read and never declared. A different artefact (the zod request schemas inpackages/spec) and a different fix.⚠️ Its option 3 touches the same closed-set question; this card is about the status and message for a repeated occurrence and the reachability of the module that owns it. Fold them if triage judges the fix is one edit. - [rest] Unknown query parameters are silently dropped on every REST route except
/approvals/requests— decide whether the closed-parameter-set rule becomes ingress policy #7606 (closed) — unknown query parameters silently dropped, as an ingress-policy decision. This is repeated parameters, which already have a landed rule. - GET /api/v1/packages/:id silently ignores ?version= — the only serving surface never reads it, and the handler that did was deleted with the REST twin #17416 / PR fix(runtime): GET /api/v1/packages/:id honours ?version= instead of silently ignoring it #17668 — the
?version=honouring itself, deliberately scoped to that one parameter on that one door. GET/DELETE /packages/:id把重复的?version=查询参数(string[])原样交给 PackageService #6307 (the precedent) — the original 400 for this condition, on routes that have since been removed by packages/rest package read routes claim to SHADOW their dispatcher twins, but the dispatcher answers /packages and /packages/:id on a stock showcase boot #14503.
Activity
claude commentedon Sep 12, 2026
Claim: session_01TSf4DV7ziu4V5j73e46b7c
Branch: claude/issue-17672-repeated-version-400-reachability
Clause-②: yes
Dispatch —
domain:cliexecution seat (#6024), round 22, 2026-09-12T06:39Z. The assignee and thisClaim:line are written by the seat on the dev's behalf: the dev inherits both, ⛔ posts no second claim and ⛔ writes no assignee.needs:contract-reviewis hung on the card in the same write as the claim, because this seat declaresClause-②: yes— see §4.serve.ts+dev.ts(open #17725), and #17536 was sent back to triage this round (5644235398) over a direction question its own filer flagged 「before dispatch」 and neither triage pass answered. ⛔ Neither is a grading of this card.1. The ruling — direction is settled; two of three routes are explicitly refused
Triage ruled at
5643017415. Quoted, and these are the fence:⇒ Your first act is to falsify premise ②, not to write the export. If the two symbols are not enough, or pulling them up drags the module's internals onto the published surface, the fork fires and you stop and report. ⛔ A copy of the rule inside
packages/runtimeis never the answer, under any pressure — it manufactures a second instance of the very defect this card removes.2. ⭐ The one line that is owed on EVERY branch, including the stop
packages/rest/src/query-multiplicity.ts:61still reads, onorigin/maintoday:That parenthetical is measurably false since #17668 (
1a25f4a8d) landed: the dispatcher's/packagesdomain does readversionnow and refuses a repeated one with its own message. The card judged this precisely and triage quoted it:⇒ ⛔ Even if the fork fires and you deliver no export, this line is corrected in the same PR. 「⛔ 不接受"方向没定所以整卡不动"。」
3. Premise check — three faces, read at dispatch on
origin/main, 2026-09-12T06:38Zgit log --oneline -3on the three landing paths:query-multiplicity.tstip is73709893f(#16628),rest/src/index.tstip isab56ea3a1(#17420),domains/packages.tstip is1a25f4a8d(#17668) ⇒ nothing has moved since the card was writtengit grep -c 'query-multiplicity' origin/main -- packages/rest/src/index.ts→ 0 hits, exit 1. Control on the same run:git grep -ln … -- packages/rest/src→ 5 files ⇒ the channel is alive, so the zero is a reading:61, quoted above404packages/runtime/src/domains/packages.ts:601carries the repeated-version message; the door answers404completed; #17667 isneeds-user-decisionondomain:spec⇒ triage's ⛔ no-fold ruling stands and is now structural — it is another lane's decision card4.
Clause-②: yes— declared by this seat, and whyRoute 1 widens
@objectstack/rest's published surface: two symbols that are today reachable from nowhere outside the package become part of its entry. That is a public-surface change on a released package ⇒yes, graded the conservative way, ⛔ never the other direction.needs:contract-reviewis on the card and the PR does not enqueue until an at-tier contract review of record exists. PutClause-②: yesin your PR body as a bare column-0 line — no bullet, no bold, no blockquote, no backticks.no— the two carriers must agree, and a disagreement is a finding to bring back to this seat, not a formatting choice.5. Acceptance
404first against the door as it stands, then show the same request answering400with the ADR-0112 nested body{ error: { code, message } }andcode: VALIDATION_ERROR. A status-code fix whose "before" was never driven is a guess that went green.400and its message come fromrefuseRepeatedQueryParams/repeatedQueryParamMessage— ⛔ not from a string that happens to match. A control that can fail: change the shared message and watch the door's answer follow.404s must survive. PR fix(runtime): GET /api/v1/packages/:id honours ?version= instead of silently ignoring it #17668 correctly introduced a genuine404("you asked for a version I do not have") and the door already had one for an unknown id. Pin both, so a client can now distinguish request-shape error from not-found — which is the whole point of the card.packages-get-version-scope.test.ts§4 pins the interim404. It is a pin on the interim answer, not the end state — change it deliberately, say in the PR body that you are changing a pin and why, and ⛔ do not route around it or treat it as existing contract.:61— corrected, on every branch including the stop..changeset/entry required. Draft PR.6. Mechanics
One worktree, branch exactly
claude/issue-17672-repeated-version-400-reachability. ⛔ Never edit the shared checkout, ⛔ nevergit stash. ⛔packages/specis read-only here — the card is explicit that nothing in it moves (VALIDATION_ERRORis already the catalog's member for 400). Run the repo's fast checks, report exit codes. Return theos-dev-reportJSON block; ⛔ report absences as absences.Generated by Claude Code
fix(runtime): a repeated ?version= on GET /packages/:id answers 400 V…
docs: name the two published pages this door's pins protect (#17672)
claude commentedon Sep 12, 2026
os-dev-report
{ "issue": 17672, "status": "done", "branch": "claude/issue-17672-repeated-version-400-reachability", "pr": "https://github.com/objectstack-ai/objectstack/pull/17815", "premise_still_valid": true, "summary": "Route 1 as ruled. `@objectstack/rest`'s barrel now publishes `repeatedQueryParamMessage` and `refuseRepeatedQueryParams`; the dispatcher's `/packages` domain imports the message and answers a repeated `?version=` with `deps.error(msg, 400)`, whose envelope derives `VALIDATION_ERROR` from `standardErrorCodeForHttpStatus(400)`. Its local `repeatedVersionMessage` is deleted. Nothing in `packages/spec` moved and NO copy of the rule was made in `packages/runtime`. The owed header line is corrected on the same branch: `query-multiplicity.ts` said the dispatcher's `/packages` domain 'reads no `version`' — false since #17668 — and now states that the rule's one home serves two doors, with a clause naming which half is portable across a package boundary. PREMISE 2, measured, bends in one half and the fork does NOT fire: nothing MORE than the two named symbols is needed (so the trigger condition is absent), and exporting them adds exactly two primitive-signature declarations to the built `.d.ts` with none of the module's internals (half two holds) — but only ONE of the two is callable at this door. `refuseRepeatedQueryParams` writes its own response onto a `res`; a dispatcher domain returns `{ handled, response }`, and driven with a capturing `res` the gate's body fails the dispatcher surface's `BaseResponseSchema` with `envelopeViolations` = ['success is missing, must be a boolean']. It is exported anyway because the ruling names both symbols and narrowing a ruling is a report, not a dev's decision — the question is handed to contract review in the PR body and in open_questions. Clause-2 was NOT downgraded: the published surface widens either way. A SECOND premise, stated by both the card and the dispatch, IS falsified: `packages-get-version-scope.test.ts` §4 does not pin the interim `404` — its own docblock says the status was left out on purpose so the pin would survive this fix, and it would have stayed green. It was changed deliberately anyway (the PR body says so and why): §4 now pins 400 + VALIDATION_ERROR + the nested body + the message by derivation, and a new §5 pins the card's actual criterion, that the three refusals are mutually distinguishable. Both genuine 404s survive and are pinned on `error.code` as well as status. Not a breaking change, measured: the 404 being replaced came from #17668 (1a25f4a8d), which is not an ancestor of the `@objectstack/runtime@17.4.0` tag (exit 1) with two control commits from that tag's history answering exit 0 in a non-shallow checkout — it has never shipped. Coordinator docs-drift advisory folded in and re-measured on the branch, ⛔ not taken on trust: `content/docs/api/client-sdk.mdx`'s error table ALREADY gives `VALIDATION_ERROR` / 400 for 'a repeated query parameter', so this door contradicted a published page for as long as it answered 404 — a stronger justification than the landed precedent alone, and ⛔ nothing to edit there. `content/docs/kernel/contracts/metadata-service.mdx` states that a missing id on this route answers `404 RESOURCE_NOT_FOUND` with message `Package 'ID' not found`; both halves are asserted with `toBe` in §1 and the message again in the new ordering pin, and the test comments now name both pages so each pin says what it protects. ⛔ No docs file is edited and no release-owned page is touched. ⚠️ Carried forward rather than papered over: the advisory reports `packages/rest/src/index.ts` yielded NO anchor, so pages documenting the new exports are invisible to that run — its silence there is not coverage.", "tests": "All on the final commit 2969466d0 — the branch is 4f530786e (a merge of origin/main 43df8db3a, which touches only CI workflow/script paths, no overlap with this diff) plus one comment-only commit naming the two documentation pages. The whole gate sweep, the runtime suite and repo-wide lint were RE-RUN on 2969466d0 rather than carried over: gate families re-derived (set identical, 61), 61 run, ALL exit 0 this time including check:type-check-debt and check:dual-build-cjs-loads, reconciliation 61 derived / 61 run / 0 NOT-MEASURED / 0 UNRUN; pnpm --filter @objectstack/runtime test 260 files / 3623 passed, VERDICT command-exit 0; pnpm lint exit 0; the pin suite 13 passed. Heavy runs via scripts/pm/os-verify-lock.sh, slot issue-17672-dev, every verdict read from the printed VERDICT line. REPRO FIRST, before any edit, on branch point 310760d22, real HttpDispatcher + real SchemaRegistry, one host: repeated ?version=a&version=b -> 404 RESOURCE_NOT_FOUND; ?version=99.0.0 -> 404 RESOURCE_NOT_FOUND; unknown id -> 404 RESOURCE_NOT_FOUND; unscoped -> 200. All three refusals identical on status AND code. AFTER: repeated -> 400 VALIDATION_ERROR with body { success: false, error: { code, message, httpStatus: 400 } } and message 'The \"version\" query parameter was supplied 2 times. Supply it at most once — this endpoint will not choose between conflicting values.'; both 404s and the 200 unchanged. SUITES: pnpm --filter @objectstack/rest test -> 190 files, 3181 passed, 1 skipped, VERDICT command-exit 0. pnpm --filter @objectstack/runtime test -> 260 files, 3623 passed, VERDICT command-exit 0. pnpm --filter @objectstack/runtime test:repo -> 2 files, 69 passed, exit 0. typecheck for both packages (incl. check:test-typecheck) exit 0. Builds: runtime dependency closure exit 0; both changed packages exit 0; full workspace turbo build 72/72 successful. pnpm lint (repo-wide, eslint . --no-inline-config) exit 0 — no narrowing claimed. GATES: scripts/pm/dispatch-gates.mjs --commands derived 61 families on the merged tree with --repo asserted; all 61 run, --ran reconciliation carrying a per-family exit code reports 61 derived / 61 run / 0 NOT-MEASURED / 0 UNRUN, a DERIVED zero. Two first answered exit 3 = PREREQUISITE NOT MET (NOT a pass, NOT a finding): check:dual-build-cjs-loads needed a whole-workspace dist and went to 0 after the full build; check:type-check-debt OOM'd under a NODE_OPTIONS heap cap tighter than the one it declares for itself and went to 0 without it (5 ledger entries re-measured, 55 raw tsc errors, none above its record). Also run because the derivation flags their rosters as sitting under a changed path: check:error-status-conformance, check:error-code-casing, check:route-ledger-census, check:authz-resolver, check:published-readme-exports — all exit 0. check:api-surface is not a script at this root (exit 254, ERR_PNPM_RECURSIVE_EXEC_FIRST_FAIL) — recorded as a mis-spelled command, NOT as a measurement. PUBLISHED-SURFACE READING: built packages/rest/dist/index.d.ts gains exactly two declarations, `repeatedQueryParamMessage(name: string, count: number): string` and `refuseRepeatedQueryParams(req: any, res: any, names: readonly string[]): boolean`; SingleQueryRead, readSingleQueryValue, FILTER_SLOT_QUERY_PARAMS, assertFilterParamSuppliedOnce and repeatedFilterParamMessage are all absent from it. ABLATION (the control acceptance item 2 asks for), run from the COMMITTED state, trap 'restore' EXIT INT TERM with absolute paths: replaced the shared sentence tail in packages/rest/src/query-multiplicity.ts. On-disk proof, not the editor's exit code — grep -c removed text 1 to 0, injected text 0 to 1; git hash-object e6887a55 to 1482e095 against the HEAD blob hash e6887a55. Leg A, packages/rest's own literal pin rest-server-query-multiplicity.test.ts: 15 failed | 17 passed, exit 1. Leg B, the dispatcher door re-driven with NO rebuild: wire message became '... Supply it at most once — ABLATION-17672 the shared sentence moved.' and a temporary literal assertion on the door went red. Leg C, the committed derivation pin: still 13 passed, exit 0, which is what it is written for. Restore verified by an empty git diff HEAD for that path. ⚠️ No rebuild was needed because packages/runtime/vitest.config.ts aliases @objectstack/rest to ../rest/src/index.ts — the suite resolves the specifier to SOURCE, so scripts/ablation-dist-preflight.mjs (the dist-resolved preflight) does not apply; the ablation reaching the door with no build IS the proof that source is the resolved path. NOT MEASURED: CI's own path-scheduled jobs (Test Core shards, Temporal Conformance, Dogfood Regression, Dogfood Verify CLI, Build Core) and the 49 artifact-roster / 10 wide-population families the derivation prints outside the runnable 54-61 — reason: they are CI's runs, and the five job families have no local invocation. PLATFORM MEASUREMENT taken while delivering, recorded because it cost a repair: on PR #17815 the session-URL footer sent with the CREATE was stored byte-identical, one footer (read-back diff: only a stripped trailing newline). The first body EDIT (REST PATCH) then APPENDED the bare-footer block on top of it, leaving TWO. Repaired by re-sending the body with NO footer of my own, after which the read-back shows exactly one (the platform's bare form). ⇒ on this surface create and edit disagree, matching AGENTS.md's 'never re-send a body that already carries an appended footer'. The PR body now carries one bare footer; durable session attribution lives in the commit trailers and in this comment.", "mcp_calls": "4 — issue_read(get) on 17672, search_issues (one targeted dedup query, taken only after the REST search endpoint answered 403 'sessions are bound to their configured repositories'; channel switch declared), issue_write(create) for 17813, and this report comment. Card body and BOTH binding comments (5643017415, 5644242643) were read zero-quota off the /issues/17672 page payload, whose frontTimelineItems reported edges 12 = totalCount 12 with hasNextPage false. Branch pushes, draft-PR create, two body PATCHes with read-backs, the PR label write + read-back, and the tag/ancestry readings all went through git or the repo-scoped REST channel, which probed 200.", "open_questions": [ { "question": "`refuseRepeatedQueryParams` is now published but has no cross-package consumer and cannot acquire one at a dispatcher domain — should it stay on `@objectstack/rest`'s barrel?", "options": [ "A — keep both, as the ruling's route-1 wording names: the pair is coherent (a gate plus its message) and any sibling mounting `(req, res)` handlers can use it, which is precedent this barrel already sets with `prepareImportRequest` / `runImport` for plugin-auth; the export site carries a clause telling a dispatcher-domain author not to reach for it", "B — publish only `repeatedQueryParamMessage`: minimal widening, and it removes a footgun whose only plausible cross-package caller is exactly the author who must not use it" ], "recommendation": "A for now, and B is a one-line change on this branch that touches nothing else. Reason: the measurement falsifies an assumption embedded in the ruling's phrasing, not the ruling's direction, and `needs:contract-review` is already hung on both carriers — an at-tier review is the seat that should choose, not this one. ⛔ Clause-2 is unaffected either way and was NOT downgraded." }, { "question": "The card and the dispatch both state that `packages-get-version-scope.test.ts` §4 pins the interim `404`. Measured, it does not — its docblock says the status was deliberately omitted so the pin would survive this fix. Does the seat want that correction recorded anywhere beyond this report and the PR body?", "options": [ "A — no further action: the PR body states it and §4 was upgraded deliberately anyway", "B — the seat notes it on the card, since the same sentence was carried forward from the card into the triage ruling and into the dispatch without anyone re-reading the file" ], "recommendation": "A. The pin is now stronger than either description assumed, and nothing downstream depends on the old characterisation. Raised only because it is a premise that three documents repeated and none measured." } ], "out_of_scope_findings": [ "filed as #17813: `metadata-protocol`'s `repeatedQueryParamError` docblock claims its wording is `packages/rest`'s 'verbatim so a caller ... is told the same thing twice', and driven, the two sentences differ at the first quoted character (The \"top\" vs The 'top') plus an appended clause; not fixable here because @objectstack/metadata-protocol does not depend on @objectstack/rest and the arrow points the other way, so it is a layering decision", "noted, not filed: the multiplicity check sits AFTER the id lookup, so an unknown id with a repeated ?version= still answers 404 not-found rather than 400. That is #17416's deliberate ordering and this card moved the status of a refusal, not the order of two refusals; now pinned explicitly instead of incidental. Successor: the contract review on this PR, or whoever converts the /packages lane to a closed parameter set (#17667's territory, currently needs-user-decision)", "noted, not filed: `readRequestedVersion` in the domain still implements the same count-not-shape predicate as `readSingleQueryValue` in packages/rest. Importing it too would mean a third exported symbol plus its result type — exactly the widening the fork instruction guards — and the door's copy also carries the `latest` sentinel, which is domain semantics. The drift surface the module's header actually names is the MESSAGE, and that is now imported. Successor: none — this is a note for the reviewer of this diff and nothing else is queued against these lines", "noted, not filed: the PR-body footer behaviour measured above — create stores the session-URL footer verbatim, the first edit appends a bare one on top. Already stated as a rule in AGENTS.md's 'GitHub mutates body BYTES' block and in pm-dispatch references/platform-readings.md, so this is a confirmation of a recorded reading rather than a new one. Successor: none — no card is owed for a behaviour the instruction set already describes and I followed the prescribed repair" ] }Generated by Claude Code
claude commentedon Sep 12, 2026
Landed —
2b08a7299cbd53edfaeaa9d949da71cc1c8471b1Release: session
session_01TSf4DV7ziu4V5j73e46b7c· cause: work landed onmain· destination: closedcompleted(by the PR's ownFixes), assignee cleared andpm:dispatchedstripped in the same write.domain:cliexecution seat (#6024), 2026-09-12T09:04Z.PR #17815 merged 2026-09-12T09:02:04Z. Acceptance verdict
5644630505; contract review of record5644634770.Landing verified — two readings plus controls
git rev-list --parents -n 1 2b08a729git merge-base --is-ancestor 2b08a729 origin/main2969466d0c→ exit 1 ⇒ the test can return falseReading 2, on
origin/main: the barrel'sexport { refuseRepeatedQueryParams, repeatedQueryParamMessage }→ 1 ·packages/runtimeimports the message → 1 · a local copy of the message inpackages/runtime→ 0 · the false header sentencereads no \version`→ **0** · control on that same file,one home` → 1 ⇒ the matcher fires, so both zeros are readings.⇒ the rule has one implementation, the dispatcher calls it, and the sentence that was measurably false since #17668 is gone.
The PR was dequeued and re-enqueued at 08:34 because
Governed Surface Guardrefused the merge group: the contract-review PASS was on record, this seat strippedneeds:contract-reviewfrom this card, and stopped there — while the second carrier on the pull request stayed on. The guard reads the PR's label. Full record at5644774782; on the re-enqueued group that guard reads success.⭐ Two things carried forward from it, both now in force:
needs:contract-reviewis a TWO-carrier gate. Clearing one is not clearing the gate. ⛔ Strip both, read both back, in the same act.convert_to_draftdoes (measured:removed_from_merge_queuefollowed the draft conversion, not the disarm).What the card delivered
Route 1 as ruled: the one shared rule is reachable, the door answers
400 VALIDATION_ERRORfrom it, and the owed header correction rode along on the same branch. ⛔ No copy of the rule inpackages/runtime— the one thing no pressure made acceptable — and the file now carries explicit ⛔ comments at:80and:601saying theres-shaped gate is not what a dispatcher domain uses.⭐ Premise ② bent and the fork correctly did not fire. Only one of the two ruled symbols is callable at this door (
refuseRepeatedQueryParamswrites onto ares; a dispatcher domain returns{ handled, response }and its body failsBaseResponseSchema). The fork's trigger is more than two symbols needed; here fewer are usable. The dev exported both anyway because the ruling names both, and narrowing a ruling is a report rather than a dev's decision — and handed the question up. The contract review ruled A on jurisdictional grounds, ⛔ not aesthetic ones; B (publish only the message) stays available at one line.packages-get-version-scope.test.ts§4 was said to pin the interim404. It does not — its own docblock says the status was deliberately omitted so the pin would survive this fix. The dev read the file instead of the three documents and upgraded §4 deliberately anyway.Not a breaking change, measured: the
404being replaced came from #17668, which is not an ancestor of the@objectstack/runtime@17.4.0tag (exit 1, two control commits from that tag's history at exit 0, non-shallow) ⇒ it never shipped.Filed by the dev out of this round rather than folded in: #17813 (
metadata-protocol'srepeatedQueryParamErrordocblock claims verbatim parity withpackages/restand the two sentences differ — a layering decision, since the dependency arrow points the other way).Generated by Claude Code
fix(pm): H19 judges a `Blocked-by:` block against the body's target s…
test(rest): cover the appended tenancy positional and stop hand-typin…
fix(runtime): a repeated ?version= on GET /packages/:id answers 400 V…
docs(metadata-protocol): the repeated-parameter docblock stops callin…
fix(runtime): GET /api/v1/packages honours the declared `enabled` que…
fix(runtime,rest): the dispatcher /meta item reads ask the per-caller…