issueflow-reconcile — a repeated Blocked by declaration promotes on its first sentence alone (false ready on rig#154)
#184
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#184
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?
Context — a false
readypromotion on a live board, 2026-07-25 13:32Zheavy-duty/rig#154 declares three dependencies and the sweep promoted it to
readywith theblockers-clearedcomment —"Every issue named by
Blocked byis closed" — while two of the three were open (rig#153 open andclaimed, its PR in review; rig#148 open, waiting on an operator act). An idle builder found it, refused to
claim through it, and flagged it
instead. Nothing was lost, because a human read the board more carefully than the sweep did.
Root cause, reproduced against
0.3.0(
blocked_reference_records): the parser binds to the first occurrence of theblocked bymarker in thebody and stops at the first
.or;after it — one clause, once. rig#154's declaration was threesentences (
Blocked by #152. Blocked by #153. Blocked by #148 — …), so it resolved to152alone, whichwas closed:
Two failure modes, one cause:
Blocked bysentence after the first is invisible. When the first onehappens to name a closed issue, the sweep promotes — the exact outcome the function's own comment
forbids ("conservatively retain later references so ambiguity can keep an issue blocked, never promote it
prematurely"), and which
unterminated blocker prose errs toward retaining dependenciesalready asserts for the adjacent case.
section, so any earlier sentence containing the words — a triage header saying "restored because it is
blocked by an operator act." — becomes the declaration, and the real clause below is never read:
Mode 2 flags rather than promotes, so it is the cheaper half — but it is a live trap for triage: it makes the
sweep's verdict depend on prose that has nothing to do with dependencies, and it means writing about being
blocked in an issue body silently disarms that issue's declaration. Both modes are one-line-of-awk deep.
Not this issue: the cross-repo classification (#61) and the
KEEP/READY/FLAG_*decision table arecorrect and stay as they are — only the extraction changes.
Spec
blocked_reference_recordsscans the whole body, and foreach
blocked byoccurrence takes that clause up to its own first./;(unterminated → to end of input,as today). The union of all clauses is the record set; ordering and dedup stay with the existing
sort -nu/sort -u.like "this was blocked by #9 before the split" now contributes
#9and can hold an issueblockedthatcould have moved. That is the correct direction of error for this sweep and is already its documented
posture: a stale
blockedis a triage comment away, a falsereadysends a builder into work that cannotmerge. Say so where the next reader will look.
FLAG_CROSS_REPOviablocked_cross_references(#61's invariant holds unchanged over the larger set).byte-identically; this only stops dropping what a body already said.
Tasks
actions/issueflow-reconcile/issueflow-reconcile.sh: multi-occurrence extraction inblocked_reference_records, with the retain-bias comment.test/issueflow-reconcile.test.sh: the cases below, beside the existing invariant-3 block.changelog.d/fragment.Acceptance criteria
blocked_references <<<'Part of #151. Blocked by #152. Blocked by #153. Blocked by #148 — prose. Blocks #155.'→148 152 153(the rig#154 body, verbatim).Blocked by #152, #153, #148.→ all three; the prose sentence's refs, if any, are retained too, never substituted for the declaration.KEEP, notREADY.test/run.shgreen.Test plan
Must fail: reverting the extraction to first-occurrence-only turns the two new cases red (they are precisely
the promotion this issue is about); dropping the union and keeping only the last clause turns the existing
soft-wrap case red.
Boundaries: marker inside a code fence or a link title still contributes (the parser is textual by design —
over-retention is the accepted direction);
Blocks #Nnever contributes;UNKNOWNstates still flag.Control: an issue with one well-formed clause and all blockers closed still promotes, with the same comment.
Dependencies
None. Consumers pick it up at the next pin bump; rig#154's body was hand-rewritten into a single clause on
2026-07-25 as the interim fix, so no board is currently mis-set on this.
Claiming — starting now. Plan: multi-occurrence extraction in
blocked_reference_records(union of everyblocked byclause, each terminated at its own first./;, unterminated → end of input), retain-bias comment, the new test cases beside the invariant-3 block, and a changelog.d fragment. Draft PR shortly.Claimed by @codex-bot-andresmgsl. I’m starting the multi-occurrence blocker extraction fix and will open a draft PR shortly.
Draft PR open: heavy-duty/ceremony#186 — extraction unioned, tests and fragment in, suite green locally (145 issueflow checks, 18 test files). Waiting on draft CI, then marking ready-for-review for the panel.
Parked (shape 2, live review round): PR #186 is ready-for-review at head
0eea112with CI green, and the whole panel (codex, grok, kimi) is requested. Waiting on the round's first verdicts — the next move belongs to the reviewers.