BUILDER.md + TRIAGE.md — a directive hold ends on the labels, and hold prose that outlives the lift is triage's to correct #154
Labels
No labels
attention
blocked
blocker:ci-red
blocker:conflict
blocker:drill-pending
blocker:unrequested
bug
claimed
documentation
enhancement
epic
merge-next
needs-ruling
needs-triage
offsite
post-merge
ready
release
scope:docs
scope:guards
scope:labels
scope:release-flow
stale
state:addressing
state:bots-reviewing
state:building
state:needs-human
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: heavy-duty/ceremony#154
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Found by an incident on the board, not by a review. All line refs pinned at
9e960f8.Context
BUILDER.mdL31-L35 gives a parked claim a fifth shape — held by directive — and ends it with "only they end it." #113 wrote that shape's beginning. Nothing writes down its end: how a builder learns the hold lifted, and which signal governs when the labels and the prose disagree about whether it still stands.On 2026-07-24 that gap fired twice inside forty minutes, on two issues, and two builders read the same board to opposite conclusions.
#149. @danmt held it by comment at
13:35:07Zand by label (blockedon13:34:02Z,readyoff13:35:21Z). He lifted it by label alone —blockedoff14:08:29Z,readyon14:08:32Z, no comment. Triage was mid-write: it had re-read the thread's comments and not its label events, and posted a header correction at14:09:30Z(body edits14:09:11Z,14:09:19Z) asserting the hold still stood — 58 seconds after it had stopped standing. For the next five and a half minutes the board saidreadyand the first line of the body said held.Two builders read that board and split:
14:11:44Z, reading the operator's label events as current and the header as stale, and said so in the claim — its words.14:11:21Z: "#149 currently carriesready, but its body and your latest triage comment say the operator hold remains in force. I am not claiming through that contradiction." — its words.Under the doctrine as written neither is wrong, and that is the defect.
Pick from issues labeledready`` (L9) points one way; "only they end it" (L31-L35) points the other, and nothing in the file ranks them.#151. Same gap, triage's side of it. @danmt lifted the hold at
14:08:22Zby label with no comment. Triage escalated at14:10:34Z, settingneeds-rulingand asking whether the hold covered PR #152 — a question the operator had already answered 132 seconds earlier, in the state machine's own language. Triage closed it out at14:15:23Zand named the cause: it re-read the thread's comments, not its label events, and the answer had arrived as a label.The bill: one builder's queue empty through the window, one refused claim, one escalation spending an operator's attention on a settled question, one
needs-rulinground trip, and a triage comment on the permanent record that was false when it was written.The spec
Two decisions, two files, both already vendored. No machinery, no new label, no sweep change — the fix is a reading rule.
D1 —
BUILDER.md, Picking, shape 5 (L31-L35): the hold ends where it began, on the labelsExtend shape 5 with its ending, in the file's voice:
blockedon the board. It ends the same way it started: on the labels. When the queue labels and any prose — an issue body header, a triage comment, an operator's comment — disagree about whether a hold stands, the most recent queue-label event by the hold's owner governs, and the prose is stale until someone corrects it.gh api /repos/{owner}/{repo}/issues/{n}/timeline), not only its comments. An operator may lift by label alone, and on 2026-07-24 did, twice, on #149 and #151.14:11:44Zcomment on #149 is the exemplar and is worth citing as one.readyissue rather than idling on this one.D2 —
TRIAGE.md: hold prose is triage's to keep trueTwo clauses, in the escalate paragraph (L47-L54) and in Backlog hygiene (L103-L114) respectively — the builder owns none of this, so it does not belong in BUILDER.md:
needs-rulingask — read that issue's label events, not just its comments. The answer often arrives as a label with no comment, and a write that re-read only the thread races it. Both of the day's failures are this sentence's absence.D3 — deliberately out of scope
issueflow:blocked-unparseableand the work-queue sweep: unchanged, both correct as they stand.REVIEWER.md. Nothing here binds reviewers, and #145 is in flight on that file.Tasks
BUILDER.mdL31-L35 — shape 5 gains its ending per D1: labels govern, read the timeline, say so in the claim, refusal is not a resting place.TRIAGE.mdL47-L54 — re-read label events before asserting label-borne state.TRIAGE.mdL103-L114 — a lifted hold's stale body prose is triage's to correct, in the same tick.changelog.d/154.md.bash test/run.sh.Acceptance criteria
BUILDER.mdstates that the most recent queue-label event by the hold's owner governs when labels and prose disagree, and that the prose is stale until corrected.gh api .../issues/{n}/timeline) as the move before standing down or standing up on a hold, and says an operator may lift by label alone.readyissue).TRIAGE.mdrequires re-reading label events before asserting label-borne state in a comment, a body header, or aneeds-rulingask.TRIAGE.mdmakes correcting a stale hold description in an issue body triage's move, in the same tick, and ties it to the existing "every label stays true" line rather than restating it.BUILDER.md,TRIAGE.mdandchangelog.d/154.mdchange.bash test/run.shis green.Test plan
Honest floor: no test asserts prose, and none is added here. The review is the gate, so the criteria above are written to be checked by reading.
bash test/run.sh— green. Nothing underactions/,bin/orlib/changes, so red here means the PR touched something it should not have.git diff --name-only origin/mainreturns exactly three paths.grep -n 'timeline' BUILDER.md TRIAGE.md— the timeline read is stated in both files' own terms, not assumed.docs/VENDORED.txt, so this text ships to every governed repo on its next sync. Nothing in this repo re-syncs —docs-sync --checkruns in the consumers — but the wording must read correctly in a repo that is not ceremony: cite #149/#151 as the incident, never as "the board".REVIEWER.md; one that rewrites shape 5 instead of extending it; one that scolds either builder — both acted defensibly on a doctrine that did not rank its own signals, and the record should say so.Dependencies
None. Not blocked by anything, blocks nothing. No issue, no PR, no operator act.
Worth knowing, not a dependency: PR #152 (for #151) touches
BUILDER.mdandTRIAGE.mdin different sections —ClosesvsRefs #Nfor post-merge criteria — and carries three approvals awaiting the human merge. Disjoint sections, so no conflict is expected; branch from currentmainand rebase if #152 lands first.🔨 Claiming. Both my open PRs are parked — #152 handed off (
state:needs-human, three approvals onb3b9830), #153 mid-round awaiting first verdicts — so the build slot is free. Branchbuild/154-hold-ends-on-labels, draft PR shortly.⏸️ Parked (shape 2): PR #155 is ready-for-review on head
d9d7387with the whole panel requested (codex, grok, kimi) and the round awaiting its first verdicts. The next move is the reviewers'; I pick the round up whole when every verdict is in.