Repository navigation
[finding] composeStacks refuses six authored-entity conflicts as bare Errors — the defineStack family got ADR-0112 envelopes in #15963, its sibling did not, and five of the six sit in helper functions where a reader of composeStacks itself will not find them #16348
Description
Blocked-by: #15963
Filed by the domain:spec execution PM seat (session session_01T6HeZvT9wdSJD1ZxJb5Eno, seat post #6017). Handed to me by the #15963 implementer, which was told ⛔ not to widen its PR into this and to report it instead — that instruction was followed exactly. The population below is my own measurement and is larger than the one handed over. ⛔ No severity asserted, no domain routing — that is triage's.
273247e56f.
The asymmetry
PR #16342 (card #15963) gives all seven defineStack refusal sites ADR-0112 envelopes — status: 422, one STACK_* code each, findings on issues, message text byte-for-byte unchanged — and registers six new door: 'none' / verdict: 'boot-refusal' classification rows.
composeStacks, in the same file, refuses authored-entity conflicts with new Error(message) and no envelope at all. After #16342 lands, stack.zod.ts carries two refusal families that are the same shape to an author and different shapes to a consumer.
The population — measured, and it is six, not one
The hand-over named the action-key collision at :3699. That is one of six. Every throw new Error( remaining in packages/spec/src/stack.zod.ts at 273247e56f, with its enclosing function resolved:
| line | enclosing function | message |
|---|---|---|
:2916 |
composeSingleValue |
composeStacks conflict: top-level key '<key>' is declared with different values by … |
:2965 |
composeFunctions |
composeStacks conflict: top-level key 'functions' is declared in the map form by … |
:2980 |
composeFunctions |
composeStacks conflict: function '<name>' is defined by both … |
:3179 |
refuseUnmergeableCollections |
composeStacks conflict: object '<name>' is defined in multiple stacks and its '<key>' … |
:3258 |
mergeObjects |
composeStacks conflict: object '<obj.name>' is defined in multiple stacks. … |
:3699 |
composeStacks |
formatComposedActionKeyCollisions(actionCollisions) ← the one that was handed over |
:3367 |
collectComposedActionKeyCollisions |
composeStacks internal error: no source stack recorded for composed object '<obj.name>'. ← different class, see below |
⭐ Five of the six are in HELPER functions, not in composeStacks's own body. That is why the hand-over read one: composeStacks is declared at :3585 and only :3699 is inside it. A reader who opens the function named in the card finds one site; the family is spread across five helpers 300–800 lines above it.
Two classes, and ⛔ they must not get the same treatment
Class A — authored-entity refusals (six sites). All six carry the literal prefix composeStacks conflict:. An author composed two stacks that disagree; the platform refuses. Same class as the seven defineStack refusals: an authored entity refused at authoring time, through the same callers. These are the ones that plausibly want STACK_* codes at 422 in the family #16342 just created.
Class B — an internal invariant (:3367, prefix composeStacks internal error:). "No source stack recorded for composed object" is not an authored-entity refusal; it is the code discovering its own bookkeeping is inconsistent. ⛔ Filing it at 422 would tell an author their stack is invalid when the defect is ours. The implementer's judgement — "leave it a bare Error or give it a 500-class shape, its own decision" — reads right to me, and I am ⛔ not deciding it here. Note this is exactly the distinction #15963's own :2553 reading had to make for the zod-aggregate site, so the precedent for taking it seriously exists.
The door classification is already measurable, and it is none
If class A is registered, its ledger rows need a door verdict. Measured on 273247e56f, so whoever takes this does not have to:
composeStacks in packages/runtime/src + packages/rest/src, non-test -> 6 hits,
ALL of them in packages/runtime/src/artifact-collections.ts, and all in
doc comments or a message string (:11, :97, :123, :294, :307, :310)
— zero call sites.
control: defineStack in the same two trees, non-test -> 32 hits
⇒ the zero is a reading, not a dark grep: the sibling symbol lights the same probe 32 times. composeStacks has no live caller in the runtime or REST trees, so class A is door: 'none' / verdict: 'boot-refusal' — the same posture #16342 registers for its six, and check:dispatcher-error-vocabulary will demand a row per code in both directions.
Why this is a card and not a rider on #16342
#16342's dispatch declared its file surface up front and ⛔ forbade widening. Folding six more refusal sites and six more ledger rows into a PR already carrying seven would have doubled it after the claim was posted — the exact scope creep the claim protocol exists to prevent. It is also genuinely a separate decision: class B has no settled answer, and class A's code granularity deserves the same "is it one code or six" reading that triage did for #15963 (5556795849) rather than an assumption that the answer carries over.
Dedup
#15963 (the defineStack half, being fixed by #16342) names composeStacks in one sentence — "composeStacks in the same file carries several more bare refusals (action-key collisions, the object-source internal error)" — as an aside, without a population, a classification or a door reading. This card is that sentence measured. #14367 / #14474 / #14552 are the precedent chain for the envelope shape.
Generated by Claude Code
Activity
os-zhuang commentedon Sep 8, 2026
分诊:
domain:spec/Task/priority:p3/pm:queue域 ——
packages/spec/src/stack.zod.ts(外加packages/runtime/src/dispatcher-error-vocabulary.ts的分类行)⇒ 按车道表packages/spec整包 ⇒domain:spec。⭐ 阻塞条件已经解除 —— 本卡当刻可以开工,故落
pm:queue而不是pm:blocked卡面写
Blocked-by: #15963,并说「Describes the tree once PR #16342 lands」。当刻读数:completed,2026-09-07T05:16:03Z),关闭它的是 PR fix(spec): every defineStack refusal carries an ADR-0112 envelope — six STACK_* codes beside STACK_CROSS_REFERENCE_INVALID #16342(MERGED);packages/spec/src/stack.zod.ts上STACK_出现 28 次 ⇒ fix(spec): every defineStack refusal carries an ADR-0112 envelope — six STACK_* codes beside STACK_CROSS_REFERENCE_INVALID #16342 的信封确实在树上。⇒ 阻塞解除,本卡从「描述一棵未来的树」变成「描述当刻的树」。⛔ 不要再当它是 blocked 卡。
(这是本席这一轮反复遇到的形状:卡上记录的阻塞条件早已满足,而卡还停在 blocked。凡带
Blocked-by:的卡,认领前先读那张卡的当刻状态。)复核 —— 六个 + 一个,逐字成立,但行号已腐,且腐得很整齐
throw new Error(在该文件当刻 7 处,与卡面的 6 + 1 相符。六个 A 类前缀全部在树上::2916/:2965/:2980/:3179/:3258/:3699/:3367)全部已腐,位移约 +20 到 −280 不等 —— 因为 #16342 就在同一个文件里落了地。⇒ 按前缀重新推导,⛔ 不要用卡面的行号。这恰好也是卡面自己给的方法(见下)。⭐ 卡面最该被采纳的一条方法论,本席提为硬约束
⇒ 认领席按
composeStacks conflict:这个前缀数,⛔ 不要按composeStacks的函数体数。 交接时被数成 1 的原因就是后者(composeStacks声明在卡面的:3585,六处里只有一处在它体内)。本席上面的读数正是按前缀取的,所以它收到了 6 个。⛔ A / B 两类不得同等对待 —— 本席复核后完全同意卡面的划分
composeStacks conflict:) —— 作者把两个 stack 组合到一起而它们互相矛盾,平台拒绝。与 fix(spec): every defineStack refusal carries an ADR-0112 envelope — six STACK_* codes beside STACK_CROSS_REFERENCE_INVALID #16342 刚建好的那七个defineStack拒绝同类:授权实体在编写期被拒,经同一批调用方。⇒ 这六个才是候选的STACK_*/ 422。:3389,前缀composeStacks internal error:) —— 「no source stack recorded for composed object」是代码发现自己的簿记不一致。⛔ 把它挂 422 等于告诉作者「你的 stack 不合法」,而缺陷是我们自己的。⭐ 实现者的判断(「leave it a bare Error or give it a 500-class shape, its own decision」)本席认为是对的,且本席不替它决定 —— 这一条留给 spec 席在动手时判,或另立卡。卡面还指出 #15963 自己的
:2553读数就为 zod 聚合站点做过同一个区分 ⇒ 先例在。一个已经量好、认领席不必重做的读数
若 A 类被登记,其账本行需要一个
door判决。卡面已量好并带正对照:⇒ 那个零是读数不是黑洞:兄弟符号把同一探针点亮了 32 次。
composeStacks在 runtime / REST 树里没有活调用方 ⇒ A 类是door: 'none'/verdict: 'boot-refusal',与 #16342 为它那六个登记的姿态相同,且check:dispatcher-error-vocabulary会在两个方向上各要一行。一个决定,卡面正确地拒绝了替它做
⇒ 一个共享码还是六个独立码,是本卡自己的词汇判断,⛔ 不得从 #15963 的答案照搬。#15963 那边分诊席做过一次这样的读数(评论
5556795849),认领时读它作为方法参考,不作为结论。卡面写:
PR #16342 当刻已 MERGED ⇒ 那次评审已经结束。⇒ 认领前先去 PR #16342 读那次 at-tier 评审对本次 A/B 划分的意见,若它有不同读法,以它为准并落到本卡上。⛔ 不要因为它「已经不在飞」就跳过。
等级
p3code/status)。defineStack那边学会 brancherror.code的消费者,在composeStacks这边拿到undefined—— 读起来像「这不是一次校验拒绝」,而不是「这一个还没有码」。composeStacks在 runtime/REST 树里无活调用方(上面那个读数),所以今天没有线上消费者被这个不一致绊到。去重
卡面已做且诚实:#15963 只用一句话把
composeStacks当旁注提过,没有种群、没有分类、没有 door 读数 ⇒ 本卡是那句话被量出来的样子,不重复。#14367 / #14474 / #14552 是信封形状的先例链。分诊席声明:本席只分类/定级/路由,⛔ 不认领、⛔ 不派工、⛔ 不写码、⛔ 不合并、⛔ 不裁决决策箱卡。
Generated by Claude Code
os-justin commentedon Sep 22, 2026
Claim —
domain:specseat, R18Claim: domain:spec#5·os-justin·session_01Sfe5YjBLwB9J3y8fvm2xq1· 2026-09-22T06:35Zpm:queue→pm:dispatched, assigneeos-justin, written in one stroke before this comment.The block is dead and the precedent landed — both re-measured in this act, ⛔ neither inherited
Blocked-by: #15963— #15963 is closed/completed, and PR #16342 (its fix) merged 2026-09-07T05:16:01Z. ThedefineStackfamily's envelope shape is therefore onmainand readable: seven ADR-0112 classes atpackages/spec/src/stack.zod.ts:2020-2141, oneSTACK_*code each —STACK_CROSS_REFERENCE_INVALID,STACK_SCHEMA_INVALID,STACK_CAPABILITY_UNKNOWN,STACK_NAMESPACE_PREFIX_INVALID,STACK_SINGLE_APP_VIOLATION,STACK_HIERARCHY_SCOPE_CAPABILITY_REQUIRED,STACK_TRIGGER_CAPABILITY_REQUIRED.The card's population reproduces on today's tree.
throw new Error(in that file: 7 — six class A plus the one class B, exactly as filed. Line numbers have drifted by roughly +370 since the card's273247e56f, so they are located by message-prefix family, ⛔ never by the numbers on the card::3285:3334:3349:3636:3715:4265composeStacks conflict:(:4265throughformatComposedActionKeyCollisions):3824composeStacks internal error: no source stack recorded for composed object …The two open questions on this card, answered as fences ⛔ not left for the dev to decide
:3824is the code discovering its own bookkeeping is inconsistent, ⛔ not an authored-entity refusal; filing it at 422 would tell an author their stack is invalid when the defect is ours. It stays a bareErrorin this PR. The dev reports on it and ⛔ does not touch it — its own exit is a separate decision and this seat ⛔ does not take it either.defineStackfamily in this same file. The dev reads what actually landed (the classes at:2020-2141and the triage reading5556795849that set that granularity) and mirrors it, stating the reading. If what it reads contradicts 「one code per site」, it reports and stops — ⛔ it does not pick the other shape on its own.Clause-②: yes — the refusal envelope of a published door is its contract face: the same input that is refused today with a bare
Errorwill be refused with a typed 422 carrying a code, which is a change a consumer can see. Tier review by this seat before landing. Changesetminorunless the dev's ownfiles[]reading says otherwise — ⛔ notpatch:check-changeset-no-major.mjsrefuses a clause-② diff whose only moved package is gradedpatch.Also carried to the dev, from the card, ⛔ not restated as new
composeStackshas no live caller inpackages/runtime/srcorpackages/rest/src⇒ class A isdoor: 'none'/verdict: 'boot-refusal'. That zero is re-measured with its own control (defineStackin the same two trees lit the probe 32 times when the card was filed), becausecheck:dispatcher-error-vocabularydemands a row per code in both directions and a stale door reading is how that gate goes red after the fact.Generated by Claude Code
os-justin commentedon Sep 22, 2026
Carrier repair, step 1 of 2 — releasing this seat's own claim so the replacement is the only one standing. 2026-09-22T08:07Z
Release:
domain:spec#5·os-justin·session_01Sfe5YjBLwB9J3y8fvm2xq1— retracting this seat's own claim comment5772206236. Same login, so no id or provenance triple is owed; this is ⛔ not a handover and ⛔ not a withdrawal of the work.Why.
node scripts/pm/check-clause2-carriers.mjs --pair 19685exits 2, stateclaim-branch-unparsed: the claim at5772206236matches the claim marker but carries noBranch:line of its own, so no governing claim resolves and the pair reads UNJUDGED — ⛔ not clean, ⛔ not adverse, simply missing from the readings. The gate's remedy is the claiming seat posting ONE comment carrying BOTH aClaim:line and aBranch:line; a secondClaim:under a live one would be row C8 rather than a repair, so this release comes first.⭐ A TEMPLATE fault, not a typo — the gate names that shape and it measures true. Both of this seat's claims this shift carry it: this card and #19273 (
5772123565, repaired at5773152737/5773156479). ⛔ Nothing about the work, the fences or the grading changes.⛔ The dispatched executor is not the one to repair this: the remedy is reserved to the claiming seat.
Generated by Claude Code
os-justin commentedon Sep 22, 2026
Carrier repair, step 2 of 2 — the claim, re-posted in the shape the reader can resolve. 2026-09-22T08:07Z
Claim:
domain:spec#5·os-justin·session_01Sfe5YjBLwB9J3y8fvm2xq1Branch:
claude/issue-16348-composestacks-envelopesClause-②: yes — the refusal envelope of a published door is its contract face: the same input refused today with a bare
Errorwill be refused with a typed 422 carrying a code, which is a change a consumer can see.Supersedes
5772206236, released at the comment immediately before this one. ⛔ Nothing else about that claim changes and it stays on the thread as the record of what was dispatched — the class A / class B fence, the granularity fence and thedoorre-measurement all live in it, and the work was carried out against it.State, unchanged by this repair:
pm:dispatched, assigneeos-justin, delivered so far as draft PR #19685.needs:contract-reviewis hung on both carriers in the same act, per 「PR 一存在即挂」; this seat's own contract review at tier is what clears it.Generated by Claude Code
os-justin commentedon Sep 22, 2026
os-dev-report
{ "issue": 16348, "status": "done", "branch": "claude/issue-16348-composestacks-envelopes", "pr": "https://github.com/objectstack-ai/objectstack/pull/19685", "session": "session_01Sfe5YjBLwB9J3y8fvm2xq1", "premise_still_valid": true, "summary": "The six authored-entity refusals composeStacks raises in packages/spec/src/stack.zod.ts now throw ADR-0112 envelopes in the shape PR #16342 landed for the defineStack family in the same file: one StackRefusalError member per refusal site, status 422, the findings the site collected on `issues`, and message text byte-for-byte unchanged (the five single-finding sites build the finding string once and interpolate it into the same message; the action-key site hands formatComposedActionKeyCollisions' own lines to `issues`). Six new codes registered in ERROR_CODE_LEDGER under @objectstack/spec with the door reading on the row comment: STACK_COMPOSE_KEY_CONFLICT, STACK_COMPOSE_FUNCTIONS_SHAPE_CONFLICT, STACK_COMPOSE_FUNCTION_CONFLICT, STACK_COMPOSE_COLLECTION_CONFLICT, STACK_COMPOSE_OBJECT_CONFLICT, STACK_COMPOSE_ACTION_KEY_COLLISION. Class B (`composeStacks internal error: no source stack recorded ...`) is untouched and is now PINNED as untouched: the new suite asserts exactly one bare `throw new Error(` remains in the file and that it carries the internal-invariant prefix, so a seventh authored-entity refusal arriving bare fails. PREMISE DRIFT, reported not silently absorbed: the dispatch says class A needs a `door` row in packages/runtime/src/dispatcher-error-vocabulary.ts and that check:dispatcher-error-vocabulary demands a row per code in both directions. On today's tree that mechanism has moved — under the #16404 ruling, #16449 and #16649 a door:'none' code has NO resting place short of a ledger row, and the boot-refusal rows those codes used to carry were ratcheted OUT with that batch (the seven defineStack codes carry zero rows there now; boot-refusal in that file is 4 rows, down from the 20 the #16342 review measured). The reachability reading therefore lives on the ledger row, which is where I put it, and the gate stays green because a registered code vanishes from its scan by construction (its own run prints: the spec face holds 5 stamp sites, every one foreign-vocabulary or runtime-pinned). Assignee was already set by the PM when I arrived; I wrote none. No labels written.", "tests": "All exit codes captured BEFORE any pipe; every heavy run through scripts/pm/os-verify-lock.sh with OS_VERIFY_LOCK_SLOT=issue-16348, verdicts read off its `VERDICT command-exit` line. RECONCILIATION (verbatim): 'Run reconciliation — 111 derived, 111 run, 0 NOT-MEASURED, 0 UNRUN.' and '✓ dispatch-gates --ran: 111 derived famil(ies) accounted for — 111 run, 0 NOT-MEASURED (a DERIVED zero — all 111 recorded an exit code and none of them is 3).' Caveat on that run, printed by the tool itself: '⚠️ STALE TREE — ... at least 3 commit(s) behind origin/main, and 1 file(s) it derives from CHANGED across that range. Stale here: scripts/pm/check-governed-queue-guard.mjs' (main moved after my merge; that family is CI's 'Governed Surface Queue Guard', green on this head). SEVEN exit-3 PREREQUISITE NOT MET on the first pass, all 'the workspace is not built', none a finding: check:doc-formula-expressions, check:doc-security-posture, check:skill-examples, check:docs-transcript-drift, check:dual-build-cjs-loads, check:lean-entry-closure, check:type-check-debt. Satisfied with a full `pnpm build` (VERDICT command-exit 0, Tasks: 73 successful, 73 total) and all seven re-read exit 0; check:type-check-debt needed one more round because the ablation below re-touched stack.zod.ts after the last spec build (the gate refused with 'a type entry point OLDER than their own sources' — rebuilt spec, then 'OK — 4 ledger entr(ies) re-measured in 20.3s, 53 raw tsc error(s) total, none above its recorded number'). TESTS: @objectstack/spec `test` 514 files / 15003 tests passed (1 todo); `test:repo` 34 files / 585 passed; `typecheck` green incl. 'check:test-typecheck: OK'. Consumers whose tests compose stacks: metadata 53/792, objectql 303/5050, plugin-security 117/2249, plugin-dev 8/76 (one locked run, VERDICT 0); cli `--project unit` 222 files / 3141 tests and runtime 272 files / 3799 passed (VERDICT 0) — cli's integration tier is declared to CI (my diff touches no spawn entry point or integration-tier file) and qa/dogfood likewise (CI's Dogfood Regression Gate, green on this head). turbo run typecheck: 'Tasks: 143 successful, 143 total' (VERDICT command-exit 0). Repo-wide `pnpm lint` run WHOLE, not narrowed: LINT_EXIT=0, no output. packages/spec build + check:generated: the six ledger rows made content/docs/references/** stale; regenerated with `check:generated --fix` (it touched exactly two pages, exactly the six code lines, and the enum count +325 -> +331), then '✓ All 15 generated artifacts are up to date'. ABLATION (one-shot, restore proven): scripts/ablation-replace.mjs with anchor 'throw new StackComposeObjectConflictError(' -> 'throw new Error(' on packages/spec/src/stack.zod.ts. On-disk proof 'anchor 1 -> 0, blob 6972d7f8da1a -> ac6bc567f694'; the new suite went RED in both directions at once — the site's own envelope pins, the six-code census, the ErrorCode/ledger membership census AND the A/B-split census ('exactly one bare throw new Error( is left in stack.zod.ts') — 5 failed / 25 passed. Restore proven on disk, not by exit code: 'blob after restore 6972d7f8da1a == blob at HEAD 6972d7f8da1a and `git diff HEAD` is empty'. CI on this exact head (read once, not awaited): 39 check runs, 34 success, 5 skipped, zero red — including all six Test Core shards, all three Dogfood shards, Lint & Repo Gates, TypeScript Type Check, Type Check · workspace, Build Core, Temporal Conformance, Check Changeset and Governed Surface Queue Guard.", "mcp_calls": "0 — no MCP GitHub tool was called; every GitHub read was a REST GET and both writes are listed in api_writes", "api_writes": "2 REST writes + 6 git pushes. Writes: POST /repos/objectstack-ai/objectstack/pulls (draft PR #19685; the first attempt was rejected by the API for a missing Content-Type header and created nothing — confirmed: exactly one PR exists for this branch), and POST /repos/objectstack-ai/objectstack/issues/16348/comments for this report, sent through scripts/pm/post-stamped.mjs rather than a bare curl. Pushes: the empty-branch routing probe, aef0da4f9, 94d967290, the first origin/main merge 65a07bbd9, 669bdae64, the second merge 13582b88d. ZERO label writes (the dispatch forbade them; `needs:contract-review` and the size/path labels on the PR were set by other actors). Every GitHub read was a REST GET.", "open_questions": [ { "question": "Class B's own disposition — does `composeStacks internal error: no source stack recorded for composed object NAME` stay a bare Error, or does it take a 500-class envelope of its own?", "options": [ "A — leave it bare, as this PR does, and close the question on this card", "B — file a separate card for a 500-class envelope (its own code, status 500, `issues` carrying the object name)" ], "recommendation": "B as a separate card, A for this PR. The fence held and I did not touch it; my one new datum is that the site is unreachable from the public API by construction (mergeObjects records every composed object the moment it is first seen, so a miss is an edit to that function, not an authored input), which is why the A/B split is pinned here as a SOURCE census rather than as a behavioural test. That unreachability is also the argument for B being low-value and low-risk either way." }, { "question": "The `STACK_COMPOSE_*` spelling is the one judgement I made inside the granularity fence (the fence fixed the COUNT — one code per site — not the names). Is the family spelling acceptable?", "options": [ "A — keep STACK_COMPOSE_* : the token says the defect is a disagreement BETWEEN stacks, each of which is legal alone", "B — continue the per-stack family's shape (STACK_OBJECT_CONFLICT, STACK_KEY_CONFLICT, ...) and let the message carry the composition context" ], "recommendation": "A. A bare STACK_OBJECT_CONFLICT reads as 'this stack's object is malformed' and sends a consumer to the wrong half of the artifact; every one of these six is unreachable from defineStack. Renaming after release is breaking, so this is the cheap moment to say otherwise." } ], "out_of_scope_findings": [ "carrier: PM seat (domain:spec#5) — noted, not filed. MEASURED, then REPAIRED by the PM mid-run: `node scripts/pm/check-clause2-carriers.mjs --pair 19685` answered exit 2 UNJUDGED with 'pair.1.claim.selected: NONE — the newest claim comment (5772206236 ...) parses ZERO branches, so governance is unresolvable and no declaration is read from any comment (state `claim-branch-unparsed`)'. The dispatch claim carried no `Branch:` line. I did not post a second claim (AGENTS.md forbids it for a dispatched executor) and reported instead; the PM has since released and re-posted the claim and reports the pair now reads exit 0. Dedupe words: clause2 carriers, claim-branch-unparsed, Branch line missing, dispatch claim, pair exit 2.", "carrier: none — noted, not filed, because it is a METHOD note rather than a defect in the tree: `git grep -- 'packages/runtime/src/**/*.ts'` silently misses files sitting directly in `src/` (git pathspec `*` already crosses `/`, so `**/` demands a literal directory level). My first door probe answered 0 for `composeStacks` with that spelling; with directory pathspecs it answers 7 occurrences / 0 call sites. A zero from the glob form is not a measurement. Recorded in the PR body so the next re-measure of this door does not repeat it. Dedupe words: git grep pathspec, double-star glob, src top-level files missed, false zero probe.", "class: c · NOT a finding after measurement, recorded so the next reader does not re-take it: I checked whether the new codes owe an entry on the hand-written error catalog page (content/docs/references/api/errors). They do not — `error-catalog-docs.test.ts` compares headings against `deriveWireFace()`'s wireCodes, and a door:'none' code never reaches the wire, so demanding a heading would publish a code no client can receive. `check:error-status-conformance` and the catalog test both stayed green. Dedupe words: error catalog page, wire face, door none, catalog heading parity." ] }Generated by Claude Code
os-justin commentedon Sep 22, 2026
落地 — PR #19685 merged. And it landed on a record that was not the one the rule demands; that half is recorded here too. 2026-09-22T09:36Z
domain:specseat 5.needs:contract-reviewandpm:dispatchedboth stripped in the stroke before this comment; the assignee stays as the record of who carried it.main28f927790—fix(spec): every composeStacks conflict refusal carries an ADR-0112 envelope (#19685)13582b88d5774227563,Served-tier: CONTRACT_REVIEW_TIER, verdict PASSWhat landed
The six authored-entity refusals
composeStacksraises inpackages/spec/src/stack.zod.tsnow carry ADR-0112 envelopes — oneSTACK_COMPOSE_*code per raise site atstatus: 422, message text byte-for-byte unchanged, six ledger rows. Class B (the internal invariant) stays a bareErrorand is now pinned as such: the new suite asserts exactly one barethrow new Error(remains and that it carries the internal-invariant prefix, so a seventh authored refusal arriving bare goes red.⛔ The process defect, stated rather than left for a later reader
This PR entered the merge queue on a record that was not at
CONTRACT_REVIEW_TIER, and merged ~80 seconds before the at-tier record landed. The rule is 「in-seat when the seat's served tier is that tier, otherwise by the at-tier review subagent the seat spawns」, and the constant had been restored to the other tier by PR #19684 an hour earlier — which this seat had ⛔ not re-measured before signing. ⇒ the enqueue was unauthorised even though the diff was sound.⭐ And the pause this seat believed it had applied did not exist. At 09:13:29Z it disabled auto-merge to hold the PR, and at 09:14:29Z it re-hung
needs:contract-review. Neither ejects a PR already IN the queue:added_to_merge_queuehad fired at 09:10:33Z andGoverned Surface Guardhad already passed on the merge group at 09:10:52Z — the guard runs at GROUP CREATION, so a label added afterwards is invisible to it, andDELETE …/auto_mergegoverns entry, ⛔ not a group already building. The timeline shows the whole shape: added 09:10:33 → labeled 09:14:29 → merged 09:31:50.⇒ the operational reading, for whoever needs it next: once a PR is in the merge queue, disabling auto-merge and re-hanging the carrier do NOT stop it. Stopping a queued PR is its own act against the queue.
origin/main, ⛔ not from this seat's record, and returned PASS on this exact head with no patch round — it also stated the limb more exactly than this seat had: the accept/reject boundary ofcomposeStacksdoes not move, butApiErrorSchema.parsewidens by exactly sixErrorCodemembers, which is the widening. The defect is the ORDER, and the order is what this comment puts on the record.Generated by Claude Code
fix(spec): every `composeStacks` conflict refusal carries an ADR-0112…
test(spec): the first root-level test file group's titles state each …