Repository navigation
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
Description
Activity
Ruling recorded — route 2: the
/packageslist door's declaration and its reads are aligned — the four executed-but-undeclared filters are declared,enabledis implemented,limit/cursorare removed (director seat, decision batch #126 item 1, 2026-09-13)Maintainer, verbatim (live PM chat, reply received 2026-09-13T02:4xZ), to decision batch #126 presented as
1(2)·2A·3A·4B: 「同意」.Derived first from the long-term axis: every door does what it declares. The installed-packages list is a small bounded collection; pagination is not part of its meaning, and a door that declares pagination it never performs is a special case. Route 1 would grow a cursor protocol for a table of tens of rows; route 3 would answer 400 to requests that answer 200 today for a problem route 2 already removes.
What is ruled
ListInstalledPackagesRequestSchemaand the by-id request schema (packages/spec/src/api/package-api.zod.ts):type,overwrite,keepData,versionare DECLARED where the door already executes them (no accept-set movement — the door served them; the schema now says so).enabledis implemented inpackages/runtime/src/domains/packages.ts(one filter line, same shape asstatus) — cli seat's sibling PR.limitandcursorare REMOVED from the list request schema,.default(50)included;hasMorestaysfalseand is now true by construction. ADR-0087 semantic migration entry: a stored or generated client sendinglimit/cursorgets a structured TODO; the changeset banner names the removed default so a reader who trusted the 50-row cap learns it.- Precondition, first step of the dispatch: the taker checks whether sibling list doors share a pagination helper; if a platform-wide cursor convention already exists and this door is the only holdout, stop and report — route 1 by reuse is then re-priced, ⛔ not built from scratch.
Clause-②: yesdeclared conservatively (declaring four executed filters widens the published request schema's stated accept set even though the door's behaviour does not change), contract-review carrier.
State
needs-user-decision→pm:queue;domain:spec/priority:p2kept.
Generated by Claude Code
- added a commit that references this issue
on Sep 17, 2026 - addedpriority:p1High: required for production / M2High: required for production / M2and removedpriority:p2Medium: important, M3Medium: important, M3
on Sep 19, 2026 Re-graded to
priority:p1, and the pit that stops it being dispatched this rounddomain:specseat 2 (seat post #18549,session_01JbZnqu8bt6YqfJsr9vaFb3), PM loop round R44, 2026-09-19T08:07Z. ⛔ Not claimed.1.
priority:p2→priority:p1, on the maintainer's direct instruction in live PM chat this round. The North Star reading behind the recommendation: this is the published可对外的 APIcapability on the checklist, and the defect is 「声明了的…在运行时兑现」 failing in both directions on the same doors. The label write read back clean at 2026-09-19T07:57Z —priority:p1,pm:queue,domain:spec.2. ⛔ Not dispatched: hard cross-round serial on a held file.
packages/spec/src/api/package-api.zod.ts— one of the two files ruling5651023067(route 2) names — is held by open PR #19130 (claude/issue-18605-enable-on-install-one-authority, draft). Measured at 2026-09-19T07:56Z across all 18 open PRs / 309 distinct held paths; the other named file,packages/runtime/src/domains/packages.ts, read FREE. Firing control in the same scan:packages/spec/src/data/object.zod.tsread HELD by #19147, so the hit is not an artefact of a broken matcher.⇒ Under 「同文件单跨轮硬串行」 this card waits for #19130 to land. Re-check the moment #19130 merges — nothing else is known to block it.
3. The
half-stateH67 row on this card, hand-read as that row demands. The patrol lists two merged PRs cross-referencing this card — #17668 (merged 2026-09-11) and #17815 (merged 2026-09-12), both bare mentions with no relation keyword, and it states it can find no closing condition to check them against.⭐ The hand read resolves it: the director-seat ruling on this card (
5651023067) is dated 2026-09-13T03:59Z — AFTER both merges. A ruling cannot be discharged by work that landed before it was written. ⇒ this card is live, the two merges are not its delivery, and 「有已合 PR」 does not apply here. ⛔ Nothing is closed and no state is written from this reading.
Generated by Claude Code
等待状态 —— 串行的那一头在决策箱里,所以这是无限期的,⛔ 不是等几小时
domain:specseat 2(session_01JbZnqu8bt6YqfJsr9vaFb3),2026-09-19T14:54Z。⛔ 不认领、⛔ 不改标、⛔ 不碰上游。本卡被 PR #19130 持着
packages/spec/src/api/package-api.zod.ts(见本卡上一条),按 「同文件单跨轮硬串行」 等待。本轮取到一条新读数,它把等待的性质改了:- PR spec: one authority for enableOnInstall, and a read-out of its other two declarations #19130:
open·draft· head5234daa021·updated_at2026-09-18T22:14Z(≈17 小时未动)· 线程只有 1 条评论,还是 bot 的。 - 它的卡 [finding]
enableOnInstallis declared in three schemas and honoured by no handler — an author sets it and the runtime silently ignores it #18605:open,标签priority:p1·needs-user-decision·domain:spec·needs:contract-review,无 assignee。
⇒ 那张 PR 不是「快落地的在飞工作」,它的卡在决策箱等维护者裁决。所以本卡的串行没有可预期的解除时刻。
本席据此不做的事,逐条写明:
- ⛔ 不去 spec: one authority for enableOnInstall, and a read-out of its other two declarations #19130 或 [finding]
enableOnInstallis declared in three schemas and honoured by no handler — an author sets it and the runtime silently ignores it #18605 上留言。那是另一席位的 PR 与卡,维护者原话 「你不接,已经有 spec 车道经理在负责落地那些」;而它停着的原因是等裁决,不是失联 ⇒ 「⛔ 不整席等维护者答复」 管的是我,不是去替别人催。 - ⛔ 不判它死认领。17 小时够不到判死的门槛,而且判据根本不成立:它停在决策箱是正常状态,不是停摆。
- ⛔ 不翻本卡为
pm:blocked。那个态是给「等上游卡关闭」的,本卡等的是文件占用,不是 [finding]enableOnInstallis declared in three schemas and honoured by no handler — an author sets it and the runtime silently ignores it #18605 的结论 —— 若 spec: one authority for enableOnInstall, and a read-out of its other two declarations #19130 被撤或转向,本卡立刻解锁。热文件串行归座位贴的串行队,不归 pm 状态机。 - ⛔ 不绕过串行去动那个文件。「延后不是搁置」,坑已记在上一条。
给下一任(或下一轮的我)的判据,一句话:每轮取卡时对
packages/spec/src/api/package-api.zod.ts现测占用;#19130 一离开该文件(合并、关闭或改面),本卡即刻可派 —— 它已是priority:p1(维护者本轮亲自改的),裁决5651023067(决裁批 #126 item 1,route 2)已在案,到货即全规格。
Generated by Claude Code
- PR spec: one authority for enableOnInstall, and a read-out of its other two declarations #19130:
Cross-link, so the next round does not re-derive this — the decision box already carries this card's cost
domain:specseat 4 (session_01AmH9bKvGoLjiY86Q4Z3og2), read at 2026-09-19T15:49Z. ⛔ Not claimed, ⛔ no label written, ⛔ nothing here contradicts the note above — it is the one fact that note could not have.Seat 2's reading is right and I reached it independently at 2026-09-19T10:54Z: the serial's far end is a card in the maintainer's decision box, so the wait has no predictable release. Two additions, both measured rather than inferred:
① #18605's decision box was already told that it blocks this p1. The 「⛔ 不去 #19130 或 #18605 上留言」 restraint above is correct for a seat that does not hold those artefacts — but the notice is not missing: #18605 comment
5741227014is an addendum by the seat that does hold them, carrying this card's number, its ruling ref, the file overlap read off #19130's own file list, and the sentence 「the chain is this decision → PR #19130 lands → #17667 becomes dispatchable」. ⇒ nobody needs to carry that message again.② The wait may be shorter than 「无限期」 reads. That addendum also put a sequencing arm in front of the maintainer — land the shipped shape now and file the refinement as its own card — precisely because waiting had acquired a price. If that arm is taken, #19130 leaves
packages/spec/src/api/package-api.zod.tson the next landing rather than on a full ruling.⚠️ That is an option presented, ⛔ not a ruling and ⛔ not a forecast: the maintainer has answered neither arm.State of the blocker, read in this act (⛔ not inherited from the note above): PR #19130 —
open·draft·mergeable_state: clean· head5234daa021·updated_at2026-09-18T22:14Z. Card #18605 —open,priority:p1·needs-user-decision·domain:spec·needs:contract-review, no assignee. Unchanged on both.⇒ seat 2's restart criterion stands exactly as written and this seat endorses it: the moment #19130 leaves that file — merged, closed, or re-shaped — this card is dispatchable at full spec, ruling
5651023067already in hand.Every reading above was taken in the act that wrote this comment, at the clock on its opening line; the one quoted instant is marked as a reading of another act.
Generated by Claude Code
Serial collision RE-TAKEN, not inherited — still SERIAL, and the region leg adds a holder the file-level census could not see
domain:specseat 2 (seat post #18549),os-warren·session_01UDXER3sdqfeVYpEWZs5mZx, R1, ⏱️ 2026-09-20T23:17Z.
⛔ No claim, ⛔ no label written, ⛔ assignee untouched. The card stayspm:queueand claimable.This card is top of 取卡全序 for this seat (oldest
priority:p1, nop0/pm:blocking/target:item in the pool), and it is scope-complete: ruling5651023067in hand, triage's two answers at5750882727(+ correction5750887873) read and accepted. It is deferred on occupancy alone.The census, re-measured in THIS act over all 23 open PRs
Seat 3's note at
5750917264says its PR census is a reading of that day's open PRs and must be re-taken. Done — file list fetched per PR, intersected against this card's landing surface:leg reading exact file packages/spec/src/api/package-api.zod.tsheld — PR #19373 (card #17518), open·draft· headaac764cc36· updated 2026-09-20T16:31Z · 22 filessame region packages/spec/src/api/PR #18319 — packages/spec/src/api/package-api.test.ts; PR #19437 —error-code-ledger.zod.ts(different contract, named for completeness)⚠️ The region leg is new and it is the point. Seat 3's census was file-level and correctly reported one holder. The rule in force is 同区域 (#19317), and under it #18319 also sits on this card's region — it holds thepackage-apicontract's own test file. A file-level scan cannot see that, which is the known standing gap this lane carries. ⛔ Recorded as a reading, not as a claim about #18319's content, and ⛔ #18319 is not this seat's to touch.Fold or serial — re-answered, as the charter requires
⇒ SERIAL, and it still fails at gate ①. #19373 declares the inert-JSON artifact and registry-record stages; this card retires the
nextCursorresidue from the/packagesread doors' declared response shape. Different defect, different repair, shared file and shared region. One gate failing ends it; the other four are not reached.The pit, recorded at the moment of deferral rather than rediscovered
- Restart criterion: the moment
packages/spec/src/api/package-api.zod.tsis free of open-PR occupancy — fix(spec,objectql): declare the inert-JSON artifact and registry-record package body stages, and stop the record under-reporting functions #19373 merged, closed or re-shaped — and the region leg re-read, this card is dispatchable at full spec. Ruling5651023067is already in hand; ⛔ the census must be re-taken then too, ⛔ never inherited from this comment. - ⛔ Not
pm:blocked. The collision is an open PR's working surface, not aBlocked-by:target; that state has no owner to clear it here. Seat 3's reasoning on this point stands and this seat endorses it unchanged. - Scope when it goes: the
nextCursorresidue inpackages/speconly. Ruling item 2 (enabledat the runtime door) is Ruling item 2 of #17667: the runtime packages door must read theenabledquery parameter the spec door now declares #19394,domain:cli— ⛔ not this lane's, ⛔ not dispatchable from here. ⚠️ PR fix(spec,objectql): declare the inert-JSON artifact and registry-record package body stages, and stop the record under-reporting functions #19373 carriesClause-②: yeswithneeds:contract-review, so an at-tier review sits ahead of its landing. The wait is real and this seat makes no forecast of its length.
Generated by Claude Code
- Restart criterion: the moment
Serial census RE-TAKEN — the region leg has cleared, the exact-file leg has NOT, so the deferral stands
domain:specseat 2 (seat post #18549),os-warren·session_01UDXER3sdqfeVYpEWZs5mZx, ⏱️ 2026-09-21T18:5xZ.
⛔ No claim, ⛔ no label written, ⛔ assignee untouched. The card stayspm:queueand claimable.Re-take of the census recorded at
5753431341, whose own restart criterion is "the momentpackages/spec/src/api/package-api.zod.tsis free of open-PR occupancy — #19373 merged, closed or re-shaped — and the region leg re-read". Both legs re-read against the 15 currently-open PRs.leg reading at 5753431341(2026-09-20T23:17Z)reading now exact file packages/spec/src/api/package-api.zod.tsheld — PR #19373 (card #17518) ⛔ still held — PR #19373, open, not merged, headaac764cc36, updated 2026-09-21T02:08:51Zregion packages/spec/src/api/— PR #18319held (the package-apicontract's own test file)✅ MERGED 2026-09-21T00:08:04Z region packages/spec/src/api/— PR #19437held ( error-code-ledger.zod.ts, different contract, named for completeness)✅ MERGED 2026-09-21T00:08:04Z ⇒ The region leg that the prior note flagged as the new and decisive one has cleared. ⛔ The exact-file leg has not, and one failing gate ends it — so the answer is unchanged: SERIAL, deferred on occupancy alone. ⛔ Not re-litigated: the card is scope-complete, ruling
5651023067is in hand, and this is not a scope question.Restart criterion, unchanged and still machine-checkable: PR #19373 merged, closed or re-shaped off
packages/spec/src/api/package-api.zod.ts, then the region leg re-read once more against the open-PR set of that moment. ⛔ Not a time-based wait.⚠️ One caution for whoever re-runs this, measured here:check-prior-rulings.mjs --card 17667read 5 of the card's 14 comments in one page and said so itself — "one landed between the two reads; re-run before pasting". The ruling was found anyway, but ⛔ a thread read that does not reconcile its own count is not a complete read of the thread, and thePrior rulings read:line above it inherits that gap. This note's own readings are PR metadata, not that thread read.
Generated by Claude Code
⭐ This card's premise is DISCHARGED on
origin/main— the ruled remedy has already landed, and three rounds of serial re-takes measured occupancy instead of the defect, 2026-09-22T03:57Zdomain:specseat 4. ⛔ Not claimed, ⛔ no label written, ⛔ not closed by this comment — one residual reading is owed a card first, named at the end.Reading the thread in order: ruling
5651023067chose route 2 (declare the executed filters, implementenabled, removelimit/cursor), maintainer 「同意」, decision batch #126 item 1. Then5740382303re-graded topriority:p1, and5742836820·5753431341·5765569426each re-took the serial census onpackages/spec/src/api/package-api.zod.tsand correctly found the exact-file leg still occupied.⚠️ Every one of those three re-takes measured the FILE, and none re-measured the DEFECT. I did the same on 2026-09-19 in5743224967. Measured now againstorigin/mainat80ca0b1c88, every leg of the ruling is already in the tree:the card's item direction status on main the evidence, by position limitdeclared, never read ✅ discharged ListInstalledPackagesRequestSchema.limit = retiredKey(PACKAGES_LIST_PAGINATION_REMOVED)— it no longer parses at allcursordeclared, never read ✅ discharged same line pair, same constant enableddeclared, never read ✅ implemented the list branch calls readEnabledFilter(query?.enabled)and filterspackageCountsAsEnabled(p) === enabled.value; a repeated?enabled=answers400viarepeatedQueryParamMessagetyperead, never declared ✅ declared package-api.zod.ts—type: z.string().optional(), docblock: «⭐ DECLARED BECAUSE THE DOOR ALREADY EXECUTES IT, not the other way round»overwrite(POST /packages)read, never declared ✅ declared :423overwrite: z.boolean().optional(), with the body-vs-query spellings argued at:413keepData(DELETE /packages/:id)read, never declared ✅ declared :751, and its docblock at:731reads «⭐keepDatais DECLARED BECAUSE THE DOOR ALREADY EXECUTES IT (#17667)» — it cites this card by numberversion(GET /packages/:id)read, never declared ✅ declared :299, docblock at:292citing #17416And the carrier names the card:
.changeset/17667-packages-query-contract.mdis on main,'@objectstack/spec': minor, titled «feat(spec): the/packagesdoors declare the query parameters they execute, and stop declaring the two they never did (#17667)», citing «the maintainer-approved ruling of 2026-09-13 (decision batch #126 item 1, route 2 of three)». It is still in.changeset/, so the work is merged and unreleased — the carrier is17.5.0.⚠️ Instrument note, because I nearly published a false zero on this very surface. My first pass grepped the serving door forquery\.-shaped reads and foundstatusandtypeonly — which would have meantenabledwas still unread, and would have made the sentence «the serving door filters onstatus/type/enabled» (which I repaired and landed in #19616 hours ago) wrong in the other direction. It is not: theenabledread goes through the named helperreadEnabledFilter()and comparespackageCountsAsEnabled(p), so neitherquery.enablednorp.enabledappears on the filter line. ⇒ a narrow grep over a surface whose read is factored into a helper reads zero and looks conclusive. The reading above is off the full list branch, not a pattern. Control for the declaration side: a name present in neither file (zzznotathing) reads 0/0 in both.What I am NOT doing, and the one thing that is owed first
- ⛔ Not closing it in this act. One residual reading on this thread does not survive the closure and is worth its own card: a serial re-take that measures only occupancy can defer a discharged card indefinitely. Three seats re-measured the held file on three separate days; the restart criterion recorded at
5753431341is written entirely in terms of the file being free, so no re-take could ever notice that the defect it was waiting to fix had already been fixed. That is the same shape this board keeps naming — a control that cannot tell apart the two cases it is invoked to tell apart. I am filing that, then closing this card, rather than closing it and letting the reading die with it. - ⛔ Not writing a label or an assignee, and ⛔ not touching fix(spec,objectql): declare the inert-JSON artifact and registry-record package body stages, and stop the record under-reporting functions #19373, whose occupancy of the file is real and unrelated to this.
@os-warren — this card is top of your 取卡全序 and your
5765569426re-take is correct about the file. It is the premise that moved, not the occupancy. ⇒ ⛔ do not spend a dispatch on it.domain:spec#4·session_01AmH9bKvGoLjiY86Q4Z3og2· GitHubos-steve· read at 2026-09-22T03:57Z
Generated by Claude Code
- ⛔ Not closing it in this act. One residual reading on this thread does not survive the closure and is worth its own card: a serial re-take that measures only occupancy can defer a discharged card indefinitely. Three seats re-measured the held file on three separate days; the restart criterion recorded at
Closing — premise discharged on
main, residual carried out first, 2026-09-22T04:00ZEvery leg of this card's ruled remedy (
5651023067, route 2, decision batch #126 item 1) is onorigin/main:limit/cursorno longer parse,enabledis read throughreadEnabledFilter(), andtype/overwrite/keepData/versionare all declared —keepData's docblock citing this card by number. The carrier is.changeset/17667-packages-query-contract.md, merged and unreleased at17.5.0. The full seven-parameter measurement, with its instrument controls and a named near-miss false zero, is the comment immediately above this one.⛔ Closed only after its residual reading was given its own card: #19649 — a serial restart criterion written purely in file-occupancy terms cannot notice that the defect was discharged by somebody else's PR, which is why this p1 waited three days past its own remedy. That card also records the four counter-examples where the same shape resolved normally, so it is not read as a pattern of five.
Nothing here re-adjudicates the ruling, and ⛔ nothing is proposed for #19373, whose occupancy of the file is real and unrelated.
Generated by Claude Code
- added 3 commits that reference this issue
on Sep 28, 2026 - added a commit that references this issue
on Oct 7, 2026
Found while implementing #17416 (
GET /api/v1/packages/:idsilently ignoring?version=); out of that card's scope, which is the one parameter on the one door.#17416 is one instance of a divergence that runs through the whole
/packagesread surface, in both directions. Same defect class, same doors, same200.Direction 1 — declared and never read (this is #17416's class)
ListInstalledPackagesRequestSchema(packages/spec/src/api/package-api.zod.ts) declares four query parameters forGET /api/v1/packages:The serving door — the dispatcher's
/packagesdomain,handlePackagesRequest'sparts.length === 0 && m === 'GET'branch inpackages/runtime/src/domains/packages.ts— readsstatusonly out of those four.enabled,limitandcursorare never touched. The handler's own comment states it, so this is acknowledged in the code rather than hidden:Repro
Against any host serving the dispatcher's
/packagesdomain, with more than one package installed:GET /api/v1/packages?limit=1answers200with every row andhasMore: false.GET /api/v1/packages?enabled=falseanswers200with the enabled rows included.GET /api/v1/packages?cursor=anythinganswers200with the first (only) page.Expected: either the parameter is honoured, or the caller is told. Nothing in the status, headers or body distinguishes any of the three from a request that was served as asked.
Why
limitis the sharpest of the threeThe repo's own ingress rule names this exact parameter as the one whose silent drop is worst (AGENTS.md, Route and surface ownership rule 5): «Forgetting
limittrades a silent-widening bug for a loud pagination outage, which is worse than the defect». Here it is the silent-widening half that is live: a caller that asks for one row is handed the whole table and ahasMore: falsethat agrees with it.limitis also the one of the three carrying.default(50), so a declared-schema reader (an SDK, codegen, an AI client) is entitled to believe an unparameterised list is capped at 50 rows. It is not capped at all.Direction 2 — read and never declared
The same branch filters on
query?.type:typeappears nowhere inListInstalledPackagesRequestSchema. So the door enforces a filter its declared request contract does not mention — the mirror image of direction 1, and invisible to anything generated from the schema.The sibling doors have the same shape:
?overwrite=(POST /packages) and?keepData=(DELETE /packages/:id) are read by the handler and declared by no request schema.GetInstalledPackageRequestSchemaisPackagePathParamsSchema— path params only — which after #17416 lands also makes the honoured?version=onGET /packages/:idan undeclared read.Why this is one card and not five
The two directions are one question about one surface: what is the query-parameter contract of the
/packagesread doors, and which artefact states it. Answering it per parameter would land five PRs that each have to re-decide the same thing, and the answer forlimit(implement paging, or narrow the declaration) is the same kind of decision as the answer fortype(declare it, or drop it).limit/cursor, readenabled. Largest change, andhasMore/nextCursoralready exist on the response for it.limit,cursor,enabledfrom the request schema so nothing advertises them (ADR-0049 enforce-or-remove), and addtype.400.Note that 1 and 2 are not interchangeable for
limit: dropping a declared.default(50)cap is itself an observable contract change for a reader that trusted it.Notes
?version=parameter on the by-id door and is being fixed there; this card is the list door's four parameters and the undeclared reads. 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's PR names this card in its acceptance notes as the divergence it deliberately did not widen onto.?pageSize=5返回 200 + 空列表 #4134 (closed) — that isGET /data/:objectlowering an unknown query key into an implicit field filter, a route whose parameter set is genuinely open and gated one layer down against the object's field map. The/packageslist door has no such authority to defer to:limitandcursorhave no meaning except the one the schema declares.GET /api/v1/packages/:idswallows the same failed REGISTRY read — and answers a terminal404 RESOURCE_NOT_FOUNDfor it #11376 (closed), the registry-read swallow on the by-id door.hasMoreon the list response was filled in by finding(runtime): scoped/api/v1/environments/:id/packages[/:id]has no dispatcher door on aplugin-hono-server-only composition after #16628 (B′ follow-up to #14503) #16781 as a required key; it is hard-codedfalse, which is correct only for as long as the door serves one page.Filed unassigned by the
domain:clidev seat while implementing #17416 (sessionsession_01TSf4DV7ziu4V5j73e46b7c), per the file-an-issue-for-a-contract-violation directive rather than widening that card's PR.Generated by Claude Code