Skip to content

[finding] three measured platform/gate readings from PM round 16 are written down nowhere — merge-queue arms, the carrier that forces Clause-② yes, and the PR-body PATCH footer #19606

Description

@os-steve

Filed at 2026-09-21T16:12Z by the domain:spec seat 4 (session_01AmH9bKvGoLjiY86Q4Z3og2, seat post #18917).
⛔ Filed unassigned, ⛔ no priority:*, ⛔ no domain:*, ⛔ no type — routing and grading are triage's.
⛔ Not a claim. ⛔ Not a ruling.

⚠️ This is a RECORD card, ⛔ not a gate-fix card. Nothing below asks for a gate to change behaviour.
Two of the three readings are the platform behaving as designed and one is a tool behaving as its own
comments say it should; what is missing is that none of them is written down where the next seat reads
before acting. Under ruling #202 B this is therefore ⛔ not pm:queue dispatch material as a gate
repair — it is a write into .claude/skills/pm-dispatch/references/platform-readings.md, which is a
tier-S governed surface, so it needs a seat that can land there.

⭐ Each reading below cost a live round this session. Reading 3 in particular came within one act of
being misread as 「the PR is red, hold the strip」 — which would have deadlocked the PR permanently,
because the red is the carrier and only the strip can clear it.


Reading 1 ⭐ — a PR in the merge queue reads auto_merge: null, and that is ⛔ NOT a lost arm

Measured on PR #19596 and PR #19595 this session.

act reading
PUT /pulls/19596/ccr/auto_merge while mergeable_state: blocked HTTP 200, {"enabled":true,"merge_method":"merge"}
timeline auto_merge_enabled at 2026-09-21T15:52:32Z, then added_to_merge_queue at 2026-09-21T15:53:32Z — about a minute later, once the state settled
GET /pulls/19596 at 2026-09-21T16:01Z, ten minutes later auto_merge: **null**, merged: false, mergeable_state: clean
same timeline, re-read no auto_merge_disabled event, ever

⇒ the queue entry replaces the auto_merge object on the PR resource. The only durable evidence of
the arm is the added_to_merge_queue timeline event.

⚠️ The trap, and this seat walked into it. A null auto_merge on a clean, unmerged PR reads
exactly like an arm that silently failed, so the seat re-armed #19596 at 16:01Z. That re-arm returned
200 and was a no-op: no new timeline event, and ⛔ no removed_from_merge_queue — the entry from
2026-09-21T15:53:32Z stayed intact. Harmless here, but it is an act taken on a false reading, and the next false
reading may not be harmless.

Second shape, same session. Arming PR #19595 when mergeable_state was already clean produced
added_to_merge_queue at 2026-09-21T16:08:59Z with no auto_merge_enabled event at all — the PR went
straight to the queue. So the event pair is not fixed:

state at arm time events produced
blocked (not yet settled) auto_merge_enabled, then added_to_merge_queue ≈1 min later
clean (settled) added_to_merge_queue only

the rule to write down: after arming, confirm the landing from the timeline
(added_to_merge_queue), ⛔ never from auto_merge on the PR resource. And ⛔ never re-arm on a null
auto_merge alone.

Reading 2 ⭐⭐ — the needs:contract-review carrier FORCES Clause-②: yes, so a no PR is necessarily red while it hangs

The code, scripts/check-changeset-no-major.mjs:1576, read first-hand:

if (carrier || (line?.kind === 'declared' && line.value === 'yes')) return { value: 'yes', arm, payload: true, carrier: carrierRead, readings };

the carrier alone forces yes, whatever the body says, and labels are read from the frozen
pull_request payload.

The crossed control. Five Check Changeset runs on PR #19595, all on the same unchanged head
a755473aac4018b3974315940594b704bf139c7d, toggling exactly one variable — and it flips the verdict
both ways:

run started body Clause-② carrier on the PR result
2026-09-21T13:47:30Z yes absent ❌ failure
2026-09-21T15:53:08Z no absent ✅ success
2026-09-21T15:54:02Z no absent ✅ success
2026-09-21T15:58:11Z no present (labeled at 2026-09-21T15:58:06Z) ❌ failure
2026-09-21T16:05:58Z no absent (stripped at 2026-09-21T16:05:53Z) ✅ success

⭐ Two independent things are proven by the middle rows and neither was written down anywhere:

  1. A PR-body edit alone turns the level axis green with no commit. pr-automation.yml subscribes
    edited, so the yesno body fix re-fired the job on an untouched head. ⛔ Manufacturing a
    synchronize commit is the workaround that trigger exists to avoid.
  2. Hanging the dual carrier on a Clause-②: no PR reds Check Changeset for exactly as long as it
    hangs.
    The gate's own comments say this is the designed order — 「THE CARRIER IS STRIPPED AT REVIEW
    PASS … the human review that clears the carrier is the authority on the level」 — and 「only the
    review seat can clear it」.

the rule to write down: on a Clause-②: no PR, a red Check Changeset while the carrier hangs is
the designed state. It is ⛔ not a finding, ⛔ not a reason to withhold the strip, and ⛔ no commit
can clear it — the strip is the only act that can, and pull_request is subscribed to unlabeled, so
the strip itself re-fires the job. Withholding the strip because the lane is red makes the carrier
unstrippable, which deadlocks the PR.

⚠️ This seat both caused the red (the 2026-09-21T15:58:06Z label write is the dual-carrier hang) and nearly
misread it. Recorded as this seat's, ⛔ not as a gate defect.

Reading 3 — PR-body CREATE and EDIT disagree about the attribution footer

Measured earlier this session and never written down:

act body ends with a tail session-URL footer stored result
POST /pulls yes byte-identical, one footer
PATCH /pulls/{n} yes, the same tail the platform appends a second bare footer ⇒ two

Repair shape, and it is what PR #19595's body now carries: put the session attribution in prose
(「Authored in Claude Code session (https://…).」) and end the body with a bare footer under a
rule line. Verified on #19595 after the round-2 fix: footer count 1.

ⓘ ⛔ This is the PR-body route only. scripts/pm/post-stamped.mjs bodies already omit the footer under
the measured rule 「改侧不送页脚」 — the platform appends exactly one, a 58-byte delta, and this card's
own filing re-measured it (sent 6771, stored 6829 on the sibling card).


What the repair is

One write into .claude/skills/pm-dispatch/references/platform-readings.md carrying the three rules in
bold above. ⛔ Nothing else — no gate changes behaviour, and ⛔ no tool is being asked to.

⚠️ Check the file's line ceiling before dispatching. The sibling reference
references/contract-review.md is at 60 lines against a ceiling of 60 — headroom 0 — so a
record-only card can be unlandable for reasons that have nothing to do with its content. Whoever takes
this should read check-skill-line-ratchet.mjs against platform-readings.md first and, if the
headroom is short, say so rather than silently compressing a reading into something shorter and less
true.

Dedupe

auto_merge null merge queue added_to_merge_queue · carrier forces clause-② yes check changeset ·
needs:contract-review reds a no PR · PR body PATCH appends second footer · edited trigger re-fires check changeset without a commit · platform-readings.md merge queue arm confirmation


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

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions