Skip to content

feat(ci): refuse a merge group whose queued PR still carries needs:contract-review - #9212

Merged
baozhoutao merged 1 commit into
mainfrom
claude/issue-9018-queue-guard-contract-review-leg
Sep 11, 2026
Merged

feat(ci): refuse a merge group whose queued PR still carries needs:contract-review#9212
baozhoutao merged 1 commit into
mainfrom
claude/issue-9018-queue-guard-contract-review-leg

Conversation

@baozhoutao

Copy link
Copy Markdown
Contributor

Fixes #9018

A second, independent merge_group predicate in scripts/check-governed-queue-guard.mjs, keyed on a label rather than on paths. It is a re-implementation — against this file's own shapes — of the leg objectstack landed as objectstack-ai/objectstack#17484 (merged 2026-09-10, 33e07f7c, card objectstack#17040).

What it does

  • Enumerates every pull request the merge group lands, PER COMMIT, reusing the same decomposition the governed leg already uses. merge_group.head_ref names only the last pull request in the group, so keying this leg to it would let an earlier pull request's open carrier ride into main behind a clean one. ⭐ That is the entire reason this is a port and not a fresh derivation: the trap is invisible at review time and a head-ref implementation passes every test anyone would think to write, because a single-PR group is the shape everyone tests. The self-test replays the defect itself — a gated PR sitting behind a bare head ref — and requires a refusal.
  • Reads each pull request's labels from the pull object, so the leg costs no new workflow permission: pull-requests: read (already declared at governed-surface-guard.yml line 64) covers GET /repos/{o}/{r}/pulls/{n}, while the issues/{n}/labels route would need issues: read, which this workflow does not grant.
  • exit 6 while needs:contract-review is on any queued pull request.
  • exit 7 when a label set cannot be read, or the group names no pull request at all. Split from 6 for the same reason 4 is split from 3 — 7 is not a worse 6, and the two carry different refusalKinds so a queue log can tell them apart.
  • The pull_request leg is untouched: not-applicable, renders the empty string, and makes zero label reads — measured with a spy that throws if it is called at all.

⭐ The honest boundary: this leg reads the LABEL, not the verdict

A carrier stripped seconds before an enqueue with no PASS on record is, to a label reader, identical to one that was never hung — 「被剥」 and 「从未挂过」 are the same bytes. Of the eleven enqueues measured on 2026-09-09, five are refused and six pass. objectui#8164 — the row that landed a real published-face defect — is one of the six that pass: its carrier was stripped a day before the enqueue, so there is no label for a label reader to see. Making it refuse would require reading the verdict, which is a strictly larger rule than the one ruled, and is deliberately not taken here.

⚠️ objectui has no verdict checker to defer that question to. The sibling defers it to scripts/pm/check-clause2-carriers.mjs; that file does not exist in this repository (measured). scripts/pm/check-half-states.mjs H31 compares the gate's two carriers with each other, which is a different question. The refusal text and the CLEAR text both say so out loud, so a green is never read as "the review happened".

⚠️ What this refusal actually does today

Until the maintainer adds Governed Surface Queue Guard to objectui main's ruleset required set (objectui#6596's still-open Maintainer-action: line), a refusal here is a red check, not a blocked queue. ⛔ This PR does not claim otherwise and does not touch the ruleset. It also does not create or apply the label — both are out of the card's reach.

Design notes specific to objectui

  • Not added to scripts/upstream-port-pin.json. objectui's guard is objectui-native by decision (objectui#6596 → PR objectui#6739): check-upstream-port-parity.mjs's validatePin refuses a divergence whose ported side is empty, so a pin cannot express this file's deletion divergence. check-upstream-port-parity still reports 4 ported file(s) match … modulo their declared divergences, unchanged.
  • The label spelling is MIRRORED from scripts/pm/check-half-states.mjs, which owns it, and pinned to that file's source. ⭐ objectstack mirrors the same constant because importing it there is a module-eval cycle; ⛔ that reason does not hold here and is deliberately not restated — objectui's check-half-states.mjs has no module-scope top-level await and does not import this guard (measured, and asserted). The reason the mirror stays is availability: governed-surface-guard.yml runs this file with no install and no build, and its own comment states the property that rests on — "the script imports node builtins and one local module (scripts/invoked-as.mjs) only". The self-test asserts this file imports nothing out of scripts/pm/.
  • The merge_group zero-cost-clear rendering is SCOPED, not rewritten. This leg makes an unconditional API call there, so the old line "an API outage can never block a diff that touches nothing governed" is no longer true on that leg. The pull_request leg's bytes are unchanged, and the self-test pins both directions.
  • content/docs/guide/ci-cd-pipeline.md is updated for the same reason: it carried the now-false zero-cost promise verbatim.

Verification

Self-test 132 → 185 cases (node scripts/check-governed-queue-guard.mjs --self-test, exit 0), including:

  • the eleven 2026-09-09 enqueues replayed individually and again as one merge group — the group is refused although its queue head ref names a bare pull request;
  • an ACCEPTANCE case in the same run: a three-pull-request group with no carrier anywhere is CLEAR at exit 0, with all three actually read (reads === 3), so the after-state is not "a guard that now refuses everything";
  • fail-closed on an unreadable label set, on a reading that never arrived, and on a group naming no pull request.

Ablation — four legs, each proving the mutation landed on disk before anything was read (anchor counts with the matched lines printed, plus a moved blob hash; an empty hash coded as FAILURE), each restored under trap … EXIT INT TERM and each restore proven by blob equality and an empty git diff HEAD:

mutation self-test cases reddened
key on the queue head ref instead of per commit exit 1 4 — incl. the under-enumeration replay and the whole-group replay
never see the carrier (state: 'bare' always) exit 1 8 — incl. all five REFUSED replays ⇒ exit 6 is reached for the right reason
unreadable reads as CLEAR exit 1 6exit 7 is reached for the right reason, not as a worse 6
a throwing label read reads as "no carrier" exit 1 1 — the end-to-end fail-open

A first attempt at the head-ref leg was refused by the harness rather than silently applied: its anchor matched 2 lines (the same for (const row of …) also occurs in decomposeGovernedWork), so it would have been a no-op or a mutation of the wrong function. It was re-anchored and re-run, and this is recorded rather than quietly retried.

Gates, exit codes captured by redirect-then-capture (never through a pipe), verdict lines quoted:

gate exit verdict
--self-test 0 OK check-governed-queue-guard self-test: 185 cases pass (…)
pnpm exec vitest run scripts/__tests__/check-governed-queue-guard.test.ts … 0 Test Files 3 passed (3) · Tests 106 passed (106)
type-check:scripts 0 tsc -p tsconfig.scripts.json — clean
lint:root 0 ✖ 32 problems (0 errors, 32 warnings) — all pre-existing warnings, 0 errors
check-changeset-presence 0 ✅ No source or published contract of a released package changed in this range, so no changeset is owed.
check-control-bytes 0 ✅ check-control-bytes: OK (scanned 7407 tracked text file(s); skipped 85 binary).
check-pre-install-import-graph 0 ✅ … 34 module(s) walked, every non-relative leaf a node builtin.
check-upstream-port-parity 0 ✓ … 4 ported file(s) match … modulo their declared divergences
check-node-esm-load 0 clean
check-doc-links · check-doc-fences · check-doc-snippets · check-doc-example-ids · check-lint-coverage · check-new-cross-file-line-citations 0 all green

turbo ls --affected from the branch point reports 0 packages, so no package test or type-check is owed; Test (shard N/4) is left to CI, with scripts/__tests__/ run in full locally as the narrowed stand-in (147 passed / 1 failed / 2 skipped of 150 files).

⚠️ That one failure is pre-existing and unrelatedscripts/__tests__/check-side-effects-array.test.ts › finds a stylesheet subpath and a second build format… (expected 0 to be greater than 0). Proven by reverting both changed files to HEAD~1 (blob hashes verified equal to HEAD~1's) and re-running: identical failure; restore then proven by blob equality and an empty git diff HEAD. Neither the test nor scripts/check-side-effects-array.mjs mentions either changed file. Filed bare, out of scope.

Preconditions re-measured rather than inherited (the card's table was the PM's reading):

precondition this seat's reading
the needs:contract-review label exists node_id LA_kwDOQ40TZM8AAAACyH71rA, colour ededed
it is actually applied 6 open items carry it right now — not 16; ⭐ two of the six are open pull requests (#9144, #9078), which is precisely this leg's subject
a merge queue exists governed-surface-guard.yml:42 merge_group: with types: [checks_requested]
the leg is genuinely absent ✅ zero occurrences in the guard; code-only count under scripts/ was 1 (check-half-states.mjs:4452), raw 3
exits 6 and 7 are free EXIT_CLEAR 0 · EXIT_CANNOT_RUN 1 · EXIT_BAD_USAGE 2 · 3 · 4 · 5

⛔ Nothing in this diff touches the governed surface — node scripts/check-governed-queue-guard.mjs --test <the two changed paths> answers ✅ NOT GOVERNED — 2 path(s) checked against 5 governed surface(s); none matched.

🤖 Generated with Claude Code

https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr


Generated by Claude Code

…s `needs:contract-review`

`scripts/check-governed-queue-guard.mjs` gains a second, independent `merge_group`
predicate, keyed on a LABEL rather than on paths (objectui#9018). It is a
re-implementation — against this file's own shapes — of the leg objectstack landed
as objectstack#17484; objectui's guard is objectui-native by decision (objectui#6596
-> PR objectui#6739) and is deliberately not registered in
`scripts/upstream-port-pin.json`, because that pin cannot express the deletion
divergence.

What it does:

- enumerates EVERY pull request the merge group lands, PER COMMIT, reusing the same
  decomposition the governed leg uses. `merge_group.head_ref` names only the LAST
  pull request in the group, so keying this leg to it would let an earlier pull
  request's open carrier ride into `main` behind a clean one. That trap is invisible
  at review time and a head-ref implementation passes every test anyone would think
  to write, which is why this is a port rather than a fresh derivation;
- reads each pull request's labels from the PULL object, so the leg costs no new
  workflow permission: `pull-requests: read` already covers it, while the
  `issues/{n}/labels` route would need `issues: read`;
- exit 6 while `needs:contract-review` is on any queued pull request;
- exit 7 when a label set cannot be READ, or the group names no pull request at all.
  Split from 6 for the same reason 4 is split from 3 — 7 is not a worse 6;
- leaves the `pull_request` leg untouched: it is `not-applicable`, renders the empty
  string, and makes zero label reads (measured with a spy that throws).

The honest boundary is stated in the code, in the verdict text on the CLEAR path as
well as the refusal, and in the docs: this leg reads the LABEL, not the verdict. A
carrier stripped before any PASS existed is indistinguishable here from one that was
never hung. Of the eleven enqueues measured on 2026-09-09, five are refused and six
pass — objectui#8164, which landed a real defect, is one of the six, and upgrading
this predicate until it catches that would make it a verdict check.

The label spelling is MIRRORED from `scripts/pm/check-half-states.mjs`, which owns
it, and pinned to that file's source. objectstack mirrors the same constant because
importing it there is a module-eval cycle; that reason does not hold in objectui and
is deliberately not restated — the reason here is that
`.github/workflows/governed-surface-guard.yml` runs this file with no install, so it
imports nothing out of `scripts/pm/`. Both the pin and the reason are asserted.

The `merge_group` zero-cost-clear rendering is SCOPED rather than rewritten: this leg
makes an unconditional API call there, so the old promise that an outage can never
block a governed-clear diff is no longer true on that leg. The `pull_request` leg's
bytes are unchanged.

Self-test 132 -> 185 cases, including the eleven 2026-09-09 enqueues replayed
individually and again as one merge group, and an ACCEPTANCE case showing a
three-pull-request group with no carrier anywhere clearing at exit 0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr

Copy link
Copy Markdown
Contributor Author

ACCEPT — objectui#9018 / PR #9212

Reviewed against the diff at 1b2abb3ebf4cb067602ed784b6d1b84261d4a603; ls-remote, the report's sha and the branch head agree. 36 5 in content/docs/guide/ci-cd-pipeline.md and 775 3 in scripts/check-governed-queue-guard.mjs.

0. ⛔ My verification line contradicted my own fence, and the round chose the fence

I wrote — carrying the card's sentence forward — "objectui#8164 is the row that landed a real defect: it is the case that must REFUSE."

It must CLEAR. The objectstack implementation I ordered ported says so in its own fixture: { pr: 8164, carrier: false, note: 'stripped 2026-09-08, enqueued 11:59:19Z — SELF-REVIEW, real defect landed' }, predicting EXIT_CLEAR. #8164 is one of the six the leg does not cover — it is the named boundary, not the flagship case. The carrier was stripped a day before the enqueue, so there is no label for a label reader to see.

Verified independently just now: PR objectui#8164's labels today are ["tests", "package: app-shell"] — ⛔ no carrier.

⇒ making #8164 refuse is reachable only by reading the verdict — which the same dispatch forbade two bullets earlier ("do not quietly upgrade it into a verdict check"). ⭐ The round implemented the fence and refused the verification line, replayed #8164 as CLEAR, and pinned the 5/6 split so only two of objectui's six rows refuse. That is the correct resolution of a self-contradicting order, and it is the second time today a round has had to choose between two things I wrote.

1. ⚠️ And my "measured" precondition was measured on the wrong population

I reported 16 items carrying needs:contract-review as proof this is not "a gate with no subject". The round measures 6 open, via ?labels=…&state=open.

My query omitted the state filtersearch_issues defaults to including closed items, so I counted a population that has nothing to do with a queue. Same predicate error as the rest of today's family.

⭐ And the sharper half, which I would not have found: two of the six are open pull requests. My three cited examples — objectui#9108, #9092, #9091 — are all issues, which a carrier leg at the merge queue can never see. The precondition still holds, but my evidence for it did not bear on the leg's actual subject.

2. ⭐ Three paths in the ported prose do not exist in this repository

objectstack's refusal text defers the verdict question to scripts/pm/check-clause2-carriers.mjs and cites .claude/skills/pm-dispatch/SKILL.md. Verified on origin/main:

path reading
scripts/pm/ contains exactly one file: check-half-states.mjs
.claude/skills/ objectui-contributor, verify — ⛔ no pm-dispatch
check-clause2-carriers.mjs, anywhere in the tree 0

⇒ porting that sentence verbatim would have shipped a refusal pointing a reader at nothing — the exact false-signpost class this lane keeps paying for. The round kept the quote (it is the operative criterion) and changed the citation to name the sibling repository, then pinned it in the falsifiable direction.

⭐ I checked that pin by printing the matched lines rather than counting them — and it is worth showing, because my own echoed expectation was wrong:

 187: * `scripts/pm/check-clause2-carriers.mjs`, and this repository has no such file
1959:  // ⚠️ objectui has no `check-clause2-carriers.mjs` to defer the verdict question
1966:      !renderCarrierVerdict(bareOne).includes('check-clause2-carriers'),

A raw count says 3; two are prose and the third is the negative assertion itself. The refusal rendering is clean.

3. The "port, do not re-derive" fence is right about enumeration and wrong about the label constant

objectstack mirrors CONTRACT_REVIEW_LABEL rather than importing it, because importing its check-half-states.mjs is a module-eval cycle (top-level await → the queue guard → back). ⛔ That reason does not exist in objectui — measured: no module-scope top-level await, and no import of this guard.

Copying the comment would have shipped a paragraph describing a mechanism this repo does not have. The round still mirrors, for objectui's own reason — governed-surface-guard.yml runs this file with no install and no build, and the file's own comment rests on "imports node builtins and one local module only", so coupling the one script whose failure direction is "the gate stops running" to a 13,103-line PM sweeper is the wrong trade — and pins that reason so it reds if H31 ever grows the cycle, telling the next reader to revisit rather than to copy.

⇒ ⛔ "Port, do not re-derive" is a rule about the ALGORITHM, not about the prose around it. I did not draw that line and should have.

4. Structural verification

claim verdict
exits 6 and 7 added, 0–5 untouched EXIT_REFUSED_CARRIER = 6, EXIT_REFUSED_CARRIER_UNREADABLE = 7
not added to scripts/upstream-port-pin.json ✅ that file is not in the diff — this is a re-implementation, not a sync
enumeration reuses decomposeGovernedWork's rows ✅ — ⛔ never the queue head ref
the self-test moved 132 → 185 (⚠️not objectstack's 144 → 183, which does not transfer — a figure I passed on without checking it described this repo)
⚠️ collision with PR objectui#9141 ✅ both touch content/docs/guide/ci-cd-pipeline.md — this PR at its lines 43 and 1683, objectui#9141 at 218. git merge-tree: 0 conflict markers against main and 0 pairwise against objectui#9141

⭐ And the acceptance leg landed: a three-PR group with no carrier anywhere is CLEAR at exit 0 with reads === 3 — so the after-state cannot be read as "a guard that now refuses everything", and the read count proves it actually looked.

⭐ The ablation set is four-legged and each leg fails for its own reason — including that exit 7 is not a worse 6. And the first attempt at leg 1 was refused by the harness because its anchor matched two lines (the same for-loop header also occurs in decomposeGovernedWork), which would have been a no-op or a mutation of the wrong function. ⛔ Recorded rather than quietly retried.

The one red in the wider run — check-side-effects-array.test.ts — was proven pre-existing by reverting both changed files to HEAD~1 (blob hashes verified) and reproducing the identical assertion, and is already open as objectui#9124. ⛔ Correctly not re-filed.

5. Two things for my own template, both handed to me

⚠️ My fences collide with the repo's own hook. "Clone fresh into your own scratchpad" produces a primary checkout — and .claude/hooks/guard-main-checkout-bash.sh blocks writes to any repo whose git-dir is not */worktrees/*, a fresh clone included. The only shape satisfying both is clone fresh AND cut a linked worktree from that clone. ⇒ into the template; the naive reading of my own fence is blocked by a hook my own dispatch cites.

⚠️ The attribution footer's durability differs by endpoint. The round's --- + _Generated by…_ block was dropped by issue-create (read back: the body ends at the Refs line) but survived PR-create. That bears on this seat's own filing practice, since I have filed eight bare cards today.

6. Routed, ⛔ not folded in

objectui#9213AGENTS.md's account of the queue guard's merge_group refusal names only the approval predicate, so after this card a reader concludes a non-governed PR can never be refused by that check. That is now false (exits 6 and 7). AGENTS.md is governed surface ⇒ ⛔ correctly left alone and filed bare.

Proceeding to the merge queue.


Generated by Claude Code

@baozhoutao
baozhoutao marked this pull request as ready for review September 11, 2026 20:56

Copy link
Copy Markdown
Contributor Author

Armed — auto-merge SQUASH, and the probe I will run afterwards, written first

Armed 2026-09-11T20:56:58Z on head 1b2abb3ebf4cb067602ed784b6d1b84261d4a603. ⓘ The arm call echoed method: MERGE; ⛔ not the authority — origin/main is linear, so the queue squashes.

The probe, posted BEFORE the merge

Every value extracted from the tree at 1b2abb3ebf and ad80b06ff2 just now. Legs scoped to <merge>^ <merge>, ⛔ never origin/main. ⚠️ Every read names an explicit ref — this seat's checkout is ~580 commits stale and shallow.

L1 — the file set. Exactly 2 paths: scripts/check-governed-queue-guard.mjs (775 3) and content/docs/guide/ci-cd-pipeline.md (36 5).

L2 — ⛔ what must STAY: the existing exit vocabulary. All six of EXIT_CLEAR = 0, EXIT_CANNOT_RUN = 1, EXIT_BAD_USAGE = 2, EXIT_REFUSED_UNAPPROVED = 3, EXIT_REFUSED_UNREADABLE = 4, EXIT_REFUSED_UNATTRIBUTED = 5 must appear byte-identical on both sides of the merge. ⛔ This leg exists because a port that renumbers an existing exit breaks every caller silently.

L3 — what moved, positive in both halves.

anchor <merge>^ <merge>
export const EXIT_REFUSED_CARRIER = 6; 0 1
export const EXIT_REFUSED_CARRIER_UNREADABLE = 7; 0 1
guard file line count 1,399 2,171

L4 — ⛔ a re-implementation, not a sync. scripts/upstream-port-pin.json must not appear in the diff — count 0. ⭐ Its control is L1: the file set is exactly 2, so this is an absence with a positive beside it. (objectui's guard is objectui-native by objectui#6596's decision; the pin cannot express its deletion divergence.)

L5 — nothing moved in transit, by blob.

path expected
scripts/check-governed-queue-guard.mjs 2b842f2c115d2388e6e02a1b862a2155565e8809
content/docs/guide/ci-cd-pipeline.md 0a824eca19ced863907ac147e02add9cabb363da

L6 — the false-signpost pin, which is this round's own finding. objectstack's refusal text defers to scripts/pm/check-clause2-carriers.mjs, a file that does not exist anywhere in this repository. On the merge commit:

  • CONTRACT_REVIEW_LABEL = 'needs:contract-review' declared exactly once;
  • the self-test's negative assertion !renderCarrierVerdict(bareOne).includes('check-clause2-carriers') present exactly once, and it is the only code-line occurrence of that string — the other two are prose saying the file is absent.

⚠️ A raw count of that string reads 3 and would say "the dead path shipped". ⛔ The count is the wrong instrument; the printed lines are the reading — which is the same trap that a PR I landed an hour ago turned on.

⛔ NOT MEASURED: the self-test's 185 cases, the four ablation legs, and every gate. Bare stale checkout, and the standing lesson is that a runnable script here answers fluently about the wrong tree. CI on this head is the reading.

⚠️ One thing I will re-check at merge time rather than assume

This PR and open PR objectui#9141 both touch content/docs/guide/ci-cd-pipeline.md — this one at its lines 43 and 1683, objectui#9141 at 218. Measured now: git merge-tree reports 0 conflict markers against main and 0 pairwise against objectui#9141. ⇒ no serialisation is needed, but objectui#9141 is held and moving further behind, so I will re-run the pairwise read after this lands rather than carry today's answer forward.

A leg that comes back amber will be published with its cause. ⛔ It will not be re-run into green.


Generated by Claude Code

@baozhoutao
baozhoutao added this pull request to the merge queue Sep 11, 2026
Merged via the queue into main with commit a94e4d0 Sep 11, 2026
35 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-9018-queue-guard-contract-review-leg branch September 11, 2026 21:27

Copy link
Copy Markdown
Contributor Author

LANDED — a94e4d073a3e69203a9e1685c8c92790d97d3645

Probe run scoped to a94e4d073a^ a94e4d073a (parent ad80b06ff2). All six legs green.

leg reading
L1 file set 2scripts/check-governed-queue-guard.mjs and content/docs/guide/ci-cd-pipeline.md; 775 3 and 36 5
L2 ⛔ the existing exit vocabulary 6 of 6 EXIT_* lines byte-identical on both sides ✅ — a port that renumbers an existing exit breaks every caller silently, and this is the leg that would have caught it
L3 what moved EXIT_REFUSED_CARRIER = 6 0 → 1, EXIT_REFUSED_CARRIER_UNREADABLE = 7 0 → 1, guard 1,399 → 2,171 lines ✅
L4 ⛔ a re-implementation, not a sync upstream-port-pin.json absent from the diff ✅, with L1's file count of 2 as its control
L5 nothing moved in transit 2b842f2c115d2388e6e02a1b862a2155565e8809 · 0a824eca19ced863907ac147e02add9cabb363da
L6 the false-signpost pin CONTRACT_REVIEW_LABEL declared once

⭐ L6 is the one worth printing, because the count and the reading disagree

raw grep for 'check-clause2-carriers'  →  3
code-only occurrences, matched lines printed:
  1966:      !renderCarrierVerdict(bareOne).includes('check-clause2-carriers'),

One code occurrence, and it is the assertion that the refusal text must not name a script this repository does not have. The other two are prose recording its absence. ⇒ a raw count says "the dead signpost shipped"; the printed line says the opposite. That is the third time today the count and the reading have disagreed on a diff I was accepting.

⚠️ The collision read, re-run rather than carried forward

This PR touched content/docs/guide/ci-cd-pipeline.md, which open PR objectui#9141 also owns a hunk in. I measured 0 conflicts before the merge and said I would not trust that afterwards. Re-measured on the landed tree:

reading
objectui#9141 vs main 41 behind, 1 ahead, head b41bea3927 unchanged
conflict markers 0
where they touch the file objectui#9141 at 218; this PR landed at 43 and 1683

⇒ still clean, and now measured against the tree that actually exists rather than the one I predicted.

What the round established that the card did not

  • objectui#8164 must CLEAR, not refuse. The card and my dispatch both called it "the case that must refuse"; the objectstack fixture names it as one of the six the leg does not cover — its carrier was stripped a day before the enqueue, so a label reader sees nothing (verified: its labels today are tests, package: app-shell). Making it refuse is reachable only by reading the verdict, which the same dispatch forbade. The round implemented the fence and refused the verification line.
  • objectstack's refusal text cites two paths that do not exist herescripts/pm/check-clause2-carriers.mjs (nowhere in the tree) and a pm-dispatch skill (.claude/skills/ holds two, neither of them that). Porting verbatim would have shipped a refusal pointing a reader at nothing. The quote was kept; the citation was corrected; L6 pins it.
  • "Port, do not re-derive" is a rule about the algorithm, not the prose around it. objectstack mirrors the label constant to dodge a module-eval cycle that does not exist in objectui; the round mirrors for objectui's own reason — this file runs with no install — and pins that reason so it reds if the cycle ever appears.

⛔ What this green does not say

It says what landed. It does not run the self-test's 185 cases or the four ablation legs — bare stale checkout, and the standing lesson is that a runnable script here answers fluently about the wrong tree. The queue build on this head is that reading.

And the leg's own boundary stands as written: it reads the LABEL, not the verdict. A carrier stripped before any PASS existed is indistinguishable here from one that was never hung. That is stated in the code, on the clear path as well as the refusal, and in the docs.

Closing objectui#9018. objectui#9213 stays open: AGENTS.md's account of this guard's merge_group refusal names only the approval predicate and is now false — governed surface, ⛔ correctly left alone and filed bare.


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Port the queue guard's contract-review carrier leg to objectui — refuse a merge group whose queued PR still carries needs:contract-review

2 participants