Skip to content

[finding] check-clause2-carriers --pair demands the clause-② dual carrier on an EPIC TRACKER whenever the PR body says Part of #N — residual exit 4 with nothing wrong on the diff (#18213 / #14122) #18214

Description

@os-elon-musk

check-clause2-carriers --pair derives a pair against an epic tracker whenever the PR body carries Part of #N, and then demands the clause-② dual carrier on that epic. Exit 4 with nothing wrong on the diff.

Observed — #18213 / #14122, 2026-09-14

The #18204 dev round ran the pre-arm predicate on its own PR:

node scripts/pm/check-clause2-carriers.mjs --pair 18213

The target pair is legible and agrees: PR #18213 / card #18204 — the clause-② declaration is readable in the fixed spelling and both carriers agree. needs:contract-review is hung on both carriers; read back and confirmed by the PM (PR #18213 labels size/m, tests, skip-changeset, needs:contract-review; card #18204 labels bug, priority:p1, pm:dispatched, domain:cli, needs:contract-review).

The run still exits 4, for a SECOND pair it derives on its own: #18213 / #14122. The PR body carries a Part of #14122 line naming the epic tracker the card sits under. The script reads that as a delivery relation, so #14122 becomes a "card" that must carry the clause-② declaration and the label.

The dev did not hang a per-PR review gate on an epic that stays open across many PRs, and said so rather than silently satisfying the check:

That residual 4 is a protocol question for the PM, not a reading about this diff.

Four independent occurrences — two devs, then the PM seat on both PRs

Confirmed 2026-09-14: the sibling dispatch on #18202 hit the identical residual on its own PR. --pair 18212 exits 4 for #18212 / #14122 alone, with the target pair #18212 / #18202 clean. That dev also refused to label the epic, in its own words:

I deliberately did NOT label #14122: hanging a review gate on a tracking epic would misstate the tracker's state and it is not this card's to write. Flagged, not touched.

⇒ two executors, two diffs, one board, the same false demand and the same refusal.

The PM seat then reproduced it on both PRs directly, with the exit code written down before any pipe (a | tail reports the pager's 0, not the script's):

$ for pr in 18212 18213; do node scripts/pm/check-clause2-carriers.mjs --pair $pr > log 2>&1; echo "$pr :: exit $?"; done
18212 :: exit 4
18213 :: exit 4

In both runs the target pair is clean in the script's own words, and the 4 is carried entirely by the #141xx epic row. Four measurements, four times the same cause. This is a mechanism defect, not a one-off reading.

Cause — deliberate design, not an accident

derivePairs (line 3282) pairs through prDeliversCard, imported so this file "can never disagree with [H8 and H31] about which PR delivers which card". Part of is one of the two accepted channels alongside the closing keywords. Since #16706 each pair carries the evidence kind it was derived from — and the docblock is explicit that the kind is reported but never used to route:

The kind rides on the pair so the row can say what it was built from; ⛔ it is never a filter here — a pair is derived exactly when prDeliversCard says so, as before.

⇒ the strong channel (Fixes/Closes) and the weak one (Part of) are distinguished and then treated identically. That is exactly where this residual enters.

⚠️ A prose disclaimer does not help, and the specimen proves it: PR #18213's body says, on the same line, "Part of #14122 — the ADR-0130 tracker stays open; nothing here is a closing keyword for it." The pair is derived anyway. ⇒ the fix has to be a property read off the board, not a sentence an author can write.

Why the demand is wrong on an epic

Clause ② gates one change: this diff widened the accept set, so this diff waits outside the queue until the contract seat clears it. The carrier must share the gated change's lifetime. An epic tracker does not:

  1. needs:contract-review hung on an epic can never be cleared truthfully — the epic outlives every PR under it, so the label degrades into decoration.
  2. Every later PR under the same epic reads a standing "unclear contract review" that belongs to somebody else's diff.
  3. The dual-carrier ruling of 2026-08-22 (「两边都挂好」) is about the card and the PR — a pair, not a tree.

Ruling in force (PM, 2026-09-14, #18204)

Part of #N 指向的 epic 跟踪卡,不是 clause-② 的载体。
A card named only by Part of and identifiable as an epic tracker is not a clause-② carrier.

Applied for #18213: the #18213 / #18204 pair is the only real carrier pair, it is dual-hung and agrees; the #18213 / #14122 residual does not block, and the dev's refusal to label the epic was correct.

Ask

Make the ruling machine-readable so the next seat does not have to re-litigate it:

  • When a pair's ONLY evidence is the Part of channel and the named card is an epic tracker, report the row as informational and do not count it toward the exit code.
  • Detect "epic tracker" from a property the board already carries, not from prose. Measured, not assumed — over the last 1000 issues on this board the two conventions are disjoint: tracking × 7 (#5499 #7070 #9857 #11753 #12104 #14122 #18065), pm:epic × 2 (#15952 #15953), intersection empty. One artifact, N packages: let a release bundle carry co-owning packages so a product can be split into modules without renaming objects #14122 itself carries tracking, NOT pm:epic — so a remedy keyed on pm:epic alone would not fire on the specimen that motivated this card. Read either label, and say in the row which one was read, per this file's own provenance convention.
  • ⚠️ The disjointness is itself a latent trap for any future rule keyed on one name. Worth a separate reading; not this card's ask.
  • Keep the existing behaviour intact for a Part of pair whose target is an ordinary delivered card: the weak channel stays a real pair there. The narrowing is epic-only.
  • Self-test: a Part of-only pair against a tracking card exits 0 with an informational row; the same against a pm:epic card likewise; the same pair against a card carrying neither keeps today's exit 4; a Fixes pair against a tracker keeps today's exit 4 (a closing keyword on a tracker is a different defect and is not in scope here).

Surface

scripts/pm/check-clause2-carriers.mjsderivePairs (~3282) and the row/exit-code assembly that consumes it, plus its --self-test. ⛔ Not prDeliversCard itself: the delivery relation is shared with H8/H31 and must keep reporting the pair; the narrowing belongs to this file's clause-② reading of it.

Not this card

#17919 (a --pair run quoting a Claim: comment from a different card), #18141, #18174, #16833 — all separate readings on the same script; deduped against the last 1000 issues including closed (REST list + local grep; /search/* is refused by the egress proxy by design).


Generated by Claude Code

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions