actions/issueflow-reconcile — bind refs_references to the reference token that follows the keyword #234
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
3 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: heavy-duty/ceremony#234
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?
Ready to claim. The collision edge this issue was minted with is spent: the
reconciler port #230 landed 2026-08-23 as
1f5dd39(!239 merged 16:58:12Z) andthe sweep flipped this issue to
readyat 17:00:52Z. No open declarationremains — see Dependencies.
Context
refs_referencestreats aRefskeyword as opening a clause rather thanbinding to a single reference, so ordinary prose on the same physical line
is read as a declared
Refsedge. Filed on this board as a stray byheavy-duty/crew's triage — the code is here, so the work is here — and
normalized to the issue contract on 2026-08-21. Nothing in crew is blocked
on it; crew is not working around it.
The parser:
refs_references,actions/issueflow-reconcile/issueflow-reconcile.sh:208-223.After matching
refs[[:space:]:]+anywhere in a line it keeps the wholeremainder, truncates only at the first
.,(or;, and hands what isleft to
issue_references,which extracts every reference in it.
Reproduced at this repo's
main(27f702a) against the real body ofcrew!64, whose round log contains one physical line reading
- **Claude verified … safe `Refs #52`, TDD provenance, … and inherited #65 CI failure: agree and preserve.**:There is no
.,(or;betweenRefs #52and#65on that line, sothe clause runs to end of line and
#65becomes an edge the PR neverdeclared. (The same body wrapped across two physical lines does not
reproduce — the bug needs the reference and the prose on one line.)
The function is byte-identical at tag
0.6.1, which is the ref crew's.github/workflows/labels.ymlpins, so consumers run this parser today.What the false edge reaches
claimed→post-mergetransition inreconcile_issuefires when a merged Refs PR exists, no open Refs-linked PR does, and
unchecked criteria remain — and it unassigns the builder. A builder who
has claimed but not yet opened a draft satisfies "no open PR", so any
other PR whose prose reads
Refs #A … #Bmerging moves #B topost-mergeand drops its assignee.OPEN_PR_ISSUESgains issues no open PR is about(
open_pr_issues, :225-233,same parser), which suppresses the 48-hour reclaim on an issue whose real
claim is abandoned and makes
post_merge_decisionanswerKEEPfor anissue whose deliverable PR has merged.
post_merge_pr_for_issue, :253-266answers the last-merged Refs PR, so a false edge that merges later wins:
crew#65's transition comment carries
post-merge-transition-pr-64whileits deliverable was crew!72. Cosmetic there, but it is the observable
symptom that surfaced the parser.
Prior art in this repo: the sibling parser in
actions/refs-not-closing/refs-not-closing.sh:35-47already binds the number to the keyword (
refs?[[:space:]]*:?[[:space:]]*[[]?#[0-9]+)and loops over every occurrence. Two parsers for one relation disagree; the
reconciler's is the loose one.
Spec
Rebind
refs_referencesso aRefskeyword contributes exactly thereference token that follows it, and nothing else:
not just the first, so
Refs #8. Refs #9.keeps both. This matches thesibling parser's loop shape.
#Norowner/repo#Ntoken immediately following the keyword (allowingonly the separator the keyword already permits,
[[:space:]:]+). Nothingafter that token is examined.
sub(/[.(;].*/, "", line)) becomes deadand is removed — the token boundary is now the whole rule.
issue_references, soowner/repo#Nremains classified
CROSSand never degrades to local#N. Output stayssorted local issue numbers (
sort -nu).Decided narrowing, on purpose: a comma list —
Refs #8, #9— now yields8alone, andRefs owner/repo#4, #12yields nothing local. Doctrinedeclares one issue per PR (
Refs #N, BUILDER.md "one issue →one PR"), so no supported form loses an edge; a list is prose as far as this
parser is concerned. The direction of error is also the right one here, and
it is the opposite of the one
blocked_reference_recordsdeliberatelychose (
:272-283): a droppedRefsedge leaves aclaimedissueun-transitioned, which is visible on the board and one triage comment away,
whereas a false edge writes
post-mergeand un-assigns a builder nobodynamed.
Refsis a declaration, not prose.Out of scope:
post_merge_pr_for_issue's last-merged rule (correct given acorrect parser),
blocked_reference_records,lib/closes_references.sh, andactions/refs-not-closing(already bound; its own cross-repo blindness isseparate and not opened here).
Tasks
Ticked by triage 2026-08-24T20:12Z against the merged head
46458ba. The2026-08-24T18:56Z pass verified and ticked all ten acceptance criteria but left
the task list untouched, so this issue read as half-done after its close; the
Closes #234auto-close moved neither list. Each task is confirmed present in!252's diff.
test/issueflow-reconcile.test.shfirst: a fixture line carrying
`Refs #52`followed by prosecontaining
#65on the same physical line must yield52alone.refs_references's awk to loop over every keyword occurrenceon the line and emit only the reference token bound to each.
sub(/[.(;].*/, "", line)truncation and theline ~ /^(#|…)#[0-9]+/guard it served, keeping theissue_references→LOCAL→sort -nutail unchanged.below, each with a comment naming this issue.
changelog.d/.Acceptance criteria
All ten verified by triage 2026-08-24T18:56Z against the merged head
46458ba, not against the PR's own claims. !252 referenced this issue withCloses #234, so the merge auto-closed it and the sweep never wrote atransition: none of these boxes moved at the merge, and every tick below was
measured after it. No criterion here is post-merge — each one is decidable at
the merged tree, which is why
Closeswas the right form and why this repairis a tick rather than a follow-up.
refs_referencesfed crew!64's body (fixture in the test file, not anetwork read) returns
52and nothing else. Verified: the fixture iscrew_round_lineattest/issueflow-reconcile.test.sh:105, pinned by"Refs parser ignores issue prose after the bound token" (
:108).Driven by hand over the merged tree it returns
52and nothingelse — the
#65in the same line is prose, not a bound token.test/issueflow-reconcile.test.sh:101-103still returns
8\n12\n175, unmodified. Verified twice over: thecitation is still literally true —
refs_bodyis still on:101and itsassertion still on
:102-103— andgit diff ca7ce6e..46458ba -- test/issueflow-reconcile.test.shis a pure addition of 22 lines with thefixture and its assertion appearing only as context. Re-driven by hand:
8\n12\n175.Refs #8, #9returns8alone, pinned by a test whose name says thenarrowing is deliberate. Verified: "Refs comma-list narrowing is
deliberate" (
:110) — the name states the intent, so a later readercannot mistake the narrowing for an oversight. Measured:
8.Refs heavy-duty/rig#4andRefs heavy-duty/rig#4, #12returnnothing local, and
Refs #175 (split from #150)returns175— theparen case survives the removal of the truncation. Verified: three
assertions at
:112,:114and:116; measured empty, empty,175.Refs #8. Refs #9.on one line returns both. Verified: "every Refsoccurrence on one line contributes its bound token" (
:118); measured8\n9. Its guard against the opposite error —Refs #8Refs #9returning8alone, the lost left boundary the review probe found at5232027andd712f06fixed — is pinned beside it at:120.fix refs parsing from #200andCloses #40; refs: nonestill returnnothing. Verified:
:122and:124; both measured empty.open_pr_issuesunion tests (:104-110) pass unchanged. Verified:the three union assertions are byte-unchanged — they appear in the diff
only as context — and green in the run below. They sit at
:127-133now;the
:104-110citation moved because the new assertions were insertedabove them, and nothing about the assertions themselves did.
test/issueflow-reconcile.test.shpasses whole on this forge's runner.Verified on the runner, not only locally:
CI / testissuccessatthe PR head
4fb01e8(17:36:45Z) and again at the merged46458baonpush (18:53:44Z). The file reports 574 passed, 0 failed.
OOM-kills 7.7 GiB boxes). Verified:
shellcheck -xover the two filesthis issue changed —
actions/issueflow-reconcile/issueflow-reconcile.shandtest/issueflow-reconcile.test.sh— exits 0 with no output, andCI / self-guardsissuccessat both heads. Chunking is what makes thefull-tree sweep survivable; it is CI that ran the full tree here.
\xescapes) perCONTRIBUTING.md. Verified by execution rather than by
reading: the diff introduces no
\xescape and the new matcher usesPOSIX classes only (
[[:alnum:]_-],[[:space:]]), and the whole suitere-run with
awkresolved tomawk 1.3.4 20250131gives 574 passed, 0failed — with every
refs_referencescase above re-measured under mawkand byte-identical to the gawk run.
Test plan
Drive
refs_referencesdirectly intest/issueflow-reconcile.test.sh, theway the existing
refs_bodycheck at:101-103does. Measured columns arethis repo's
mainat27f702a.Must change (red first — each of these is wrong today):
- **… safe `Refs #52`, TDD provenance, … and inherited #65 CI failure: …**52,6552Refs #8, #98,98Refs heavy-duty/rig#4, #1212Refs #8. Refs #9.88,9Must keep passing:
refs_bodyfixture (:101)8,12,175Refs #175 (split from #150)175Refs heavy-duty/rig#4Also refs: #8 and heavy-duty/rig#4.8fix refs parsing from #200Closes #40; refs: noneopen_pr_issuesunion / cross-repo / dedup checks (:104-110)Dependencies
Nothing open is declared here; the parse over this body is empty. The one
edge this issue was minted with was a collision edge, not a logical dependency:
#230 rewrote
actions/issueflow-reconcile/issueflow-reconcile.shandtest/issueflow-reconcile.test.sh, the same two files this issue changes, andit was the newest open carrier of that deliverable. Two concurrently claimable
issues on one file is the collision the edge exists to prevent (#288), so this
one was sequenced after. #230 landed 2026-08-23 as
1f5dd39(!239 merged16:58:12Z) and is closed, so the edge is spent and the sweep flipped this
issue to
readyat 17:00:52Z.The declaration is rewritten away here rather than negated in place: the
blocker parser unions its marker phrase even under a sentence saying the clause
no longer applies, so history is preserved only after the marker is gone
(RELEASES.md, flip mechanics).
No new collision edge is owed. This issue is the only open carrier of those
two files. The rest of the open board, re-read 2026-08-24T16:33Z with every
queue label confirmed from label events rather than
.labels. (The rosterthat stood here was dated 14:37Z and every line of it has since moved: it had
#238
claimedwith !249 open, #231claimedwith !250 open, #240 and #243blockedbehind #238, #241blockedbehind #231, and this issue as the onlyreadyone on the board. None of that is true now — three PRs merged and arelease published between that read and this one.)
5be223a. Its files arefacts about
main, not carriers. #235, #236 and #246 likewise.post-mergeand unassigned — !250 merged 15:55:13Z as5a8fce8,the sweep moved it and released the claim at 15:58:08–09Z.
0.6.2is taggedand published;
mainis re-armed to0.6.3-devatca7ce6e. It is open onone post-merge criterion under an operator
needs-ruling, andpost-mergeisnot a claimable state, so it is not a carrier any edge can point at.
ready— the sweep flipped it at 15:58:02Z when #238 closed. Itcarries
lib/forge-forgejo.shandtest/forge-backends.test.sh.ready— triage flipped it by hand at 16:24:01Z once #231 stoppedbeing a claimable carrier of
.github/workflows/labels.yml.blockedbehind #240, the same pair plustest/labels-reconcile.test.sh.ready— minted 15:42Z, flipped by hand at 16:25:49Z when !250merged. It carries
drills/README.mdandtest/release-path.test.sh.needs-triagewith the operator'sneeds-rulingstanding ondocs/CONSUMERS.md; it carries none ofready/claimed/blockedwhile thatdecision stands, and is not a carrier of anything this issue writes.
Every deliverable stays disjoint, so every
readyissue on this board isconcurrently claimable — that set is now #240, #241 and #251, three issues
rather than the zero this roster last recorded.
The one apparent overlap in that roster is not one. This issue's Tasks add a
fragment under
changelog.d/, which #231 now carries in full.changelog.d/234.mdis a distinct filename, and distinct fragment filenames never conflict with each
other — that is what the directory exists for (#112 D1). #231's carry was
consumption, not authorship:
bin/changelog-assemble:122-126runs
rm -- "$f"over every fragment it folds in, so a fragment that lands herebefore the 0.6.2 release PR assembles — and is reachable from that PR's merge
base — is folded into the 0.6.2 section and deleted in the same diff. That question is now settled by the merge rather than predicted: !250 merged
2026-08-24T15:55:13Z having consumed exactly the six fragments reachable from
its base
7bdae45—217,229,230,235,236,246.changelog.d/234.mdwas not among them, so this issue's fragment lands in theopen
0.6.3window, withmainre-armed to0.6.3-devand no release PRstanding over it. That changes nothing about what the builder writes — fragments
carry no version, only a
###section heading, and the assembler decides whichrelease absorbs them. A consumption edge is not a collision edge (the rule as
stated on #246, 2026-08-24T04:15Z). No edge is owed in either direction and none
is written.
One live artifact of that window, so it is not mistaken for debris:
changelog.d/238.mdis sitting unconsumed onmain. It landed at 15:54, after!250's merge base and sixty-nine seconds before !250 merged, so the assembler
never took it — which is why
CI / self-guardsis red at the0.6.2tag andwhy 0.6.2 ships #238's code uncredited. That is escalated to the operator on
#231 and reaches nothing here. Leave it alone.
Not a child of #228: the sync epic adopts upstream 0.6.1–0.6.3, and this is
forge-side debt found by a consumer, like #232. It did not gate #231, and it takes
no release-window edge: #231 still carries
release, but it enumerates no## Membersrecord and under #343 there is no fallback to the gate — so it wasnever a window carrier, no other open issue carries
release, and no windowstands to be a member of. 0.6.2 is cut in any case (label events paged by hand
2026-08-24T16:33Z).
issueflow-reconcile: refs_references takes a Refs edge from prose, so an unrelated issue can be transitioned by another PR mergingto actions/issueflow-reconcile — bind refs_references to the reference token that follows the keywordTriage: accepted, and normalized in place. Thanks for filing it here
rather than working around it in crew — the parser is this repo's, and the
measurement belongs where the fix lands.
I reproduced it against
mainat27f702ausing crew!64's real body, andthe finding holds, with one correction to the reproduction as written: the
round-log line in the report renders as two physical lines, and that form
does not reproduce —
refs_referencesis line-oriented. The realcrew!64 body carries
`Refs #52`and#65on one physical line (two ofthem, at body lines 321 and 336), and that yields
52and65. Worthnoting because a fixture pasted from the report's code block would come out
green against the unfixed parser.
Both downstream consequences check out as described: the
claimed→post-mergetransition (:842-870) unassigns on a merged Refs PR when noopen Refs PR exists, and
OPEN_PR_ISSUES(:225-233) runs through thesame parser. Added a third:
post_merge_pr_for_issueanswers thelast-merged Refs PR, which is the mechanism that spent crew#65's marker on
!64.
On the suggested direction — taken, with one widening. Binding the
reference to the keyword's own token is the fix. I widened it to every
occurrence of the keyword on a line rather than the first, because the
sibling parser in
actions/refs-not-closing/refs-not-closing.sh:35-47already binds and loops exactly that way — one relation should not have two
shapes — and because
Refs #8. Refs #9.currently returns8alone, whichis its own quiet drop. The narrowing you proposed is otherwise recorded as
decided:
Refs #8, #9will return8, and it is pinned by a test that saysso, on the reasoning you gave —
Refsis a declaration, and under-retainingthere leaves a visible
claimedissue, while over-retaining unassigns abuilder nobody named.
The issue now carries the contract: spec, tasks, acceptance criteria, and a
test plan whose "must change" rows are the four measured against
27f702a,so the red-first step is unambiguous.
Queue state:
blocked, onBlocked by #230— a collision edge, not alogical one. #230 (upstream-0.6.3 reconciler port) rewrites
actions/issueflow-reconcile/issueflow-reconcile.shandtest/issueflow-reconcile.test.sh, the same two files, and is the newestopen carrier of that deliverable, so this lands after it. The sweep flips
this to
readywhen #230 closes. Not a child of #228 and not a gate on#231.
One thing crew should know while it waits: on this board the exposure is
narrow but real — a builder who has claimed an issue and not yet pushed a
draft satisfies "no open PR", so a merge elsewhere can release the claim.
Until this lands, a
claimedissue that turns uppost-mergewith anassignee dropped and no PR of its own is this bug, not an abandoned claim.
This issue's
Blocked bydeclarations parse to: {#230}That is the exact set this sweep gates on — what the machine read, never a
judgment about whether it is what you meant. The parse unions every clause it
finds, so a sentence like
no longer blocked by #9contributes#9likeany other; over-retaining is the deliberate direction of error, because a stale
blockedis a triage comment away and a falsereadysends a builder intowork that cannot merge. If this set names something you did not declare, or
omits something you did, edit the declaration — the next sweep echoes the
correction.
Comment only: nothing on this path writes a label. The marker carries the set
itself, so a parse unchanged since the last echo never re-posts.
Every issue named by
Blocked byis closed. The sweep is moving this issue toready.Collision edge spent — this issue is
ready, and the body is corrected in the same tick.Label events re-read by hand immediately before this write, not the thread: the sweep flipped
blocked→readyat 2026-08-23T17:00:52Z;bugandscope:labelshave been on since 2026-08-21T16:27Z and nothing else has moved. Unassigned, noattention, noneeds-ruling.Why it flipped. The one edge this issue carried was a collision edge, not a logical dependency: #230 rewrote
actions/issueflow-reconcile/issueflow-reconcile.shandtest/issueflow-reconcile.test.sh, the same two files this issue changes, and was the newest open carrier of that pair. #230 landed 2026-08-23 as1f5dd39(!239 merged 16:58:12Z by @andres) and is closed, so the edge is spent.Body corrected. The header line and the Dependencies paragraph both still declared
Blocked by #230, which stopped being true at 16:58:12Z. A lifted hold makes its body prose stale in the same instant (TRIAGE.md, #149). The declaration is rewritten away rather than negated in place, because the parser unions its marker even under a sentence saying the clause no longer applies (RELEASES.md, flip mechanics). Verified against the reconciler's ownblocked_references: the new body parses to{}.No new edge is owed. This issue is now the only open carrier of those two files. The rest of the board is disjoint from it: #235 carries
actions/labels-reconcile/labels-reconcile.sh, #236/#238/#240 carrylib/forge-forgejo.sh, #231 carriesVERSION/CHANGELOG.md/docs/UPSTREAM-SYNC.mdand the three workflow pins, #241 carries.github/workflows/labels.yml, and #228 is the sync epic.Nothing else changed. The spec, the
refs_referencestable, the tasks and the acceptance criteria are untouched. #230's port landed on the same file this issue fixes, so a builder claiming this should branch from currentmainat1f5dd39or later — the line numbers in the permalinks predate that merge and the surrounding function moved.Ready to claim.
Body correction (triage, 2026-08-23) — the carrier inventory in Dependencies predated #243. No label moved, no edge is owed here, and the parse over this body is still empty.
Label events re-read by hand immediately before this write, not the thread: this issue carries
bug,ready(added 2026-08-23T17:00:52Z by the sweep, whenblockedcame off in the same second) andscope:labels. Nothing about that changed here.What was stale. The inventory was written at 17:17Z and enumerated the whole open board by file so a reader could check this issue's disjointness themselves. #243 was minted at 18:40Z — after that write — and it carries
lib/forge-forgejo.sh,test/forge-backends.test.sh,test/labels-reconcile.test.shandchangelog.d/243.md. It was missing from the list, so the enumeration no longer covered every open issue and the closing sentence could not be verified against it.What changed: one token.
#236/#238/#240 carry lib/forge-forgejo.shnow reads#236/#238/#240/#243. Nothing else in the body moved.No edge is owed in either direction. This issue's deliverables are
actions/issueflow-reconcile/issueflow-reconcile.shandtest/issueflow-reconcile.test.sh; #243's set, derived from its Tasks rather than its title, touches neither — and its criteria bar it fromactions/labels-reconcile/labels-reconcile.shas well. This issue remains the only open carrier of its two files, so it staysreadyand concurrently claimable, which is exactly what the corrected sentence now says truthfully.The same mint stales this inventory everywhere it appears; #231's copy is corrected in the same tick, and #235/#236/#238/#240 already took theirs at 18:48Z.
🔗 Body correction (triage, 2026-08-24) — the open-carrier roster named two closed issues. No label moved, no gate changed, nothing claimed.
Label events re-read by hand at 2026-08-24T00:31Z before this write, per TRIAGE.md. This issue's labels and its empty blocker parse are both unchanged;
blocked_reference_recordsover the corrected body returns the same empty set it did before the edit.What was stale. The Dependencies section enumerated the board's other open carriers to support its disjointness claim, and that roster still listed #235 and #236, both since closed — #236 on !242 (2026-08-23T22:52:09Z,
17a1368) and #235 on !244 (2026-08-24T00:16:46Z,68b304d). A carrier roster is what a builder and every later mint read to decide whether a collision edge is owed under #288, so a stale one is how a wrong edge gets written.Corrected to the board as it now stands: #238 inherited
actions/labels-reconcile/labels-reconcile.shandtest/labels-reconcile.test.shwhen #235 closed, and is itselfreadyas of 00:31Z; #240 and #243 remainblockedbehind it in that order; #235 and #236 are recorded as facts aboutmainrather than carriers.The disjointness claim this roster supports still holds. The claimable set is #231, #234 and #238, and their deliverables do not intersect — so every
readyissue on this board stays concurrently claimable. Nothing in this issue's context, spec, tasks, criteria or test plan is touched.🔧 Body correction (triage, 2026-08-24T05:23Z) — the board roster in Dependencies was dated 00:31Z and predated two mints, a claim and a carrier finding. No label moved, no edge written, and this issue stays
readyand claimable.Label events re-read by hand immediately before this write, not the thread:
needs-triageat the 2026-08-21T16:14:45Z mint, cleared 16:27:40Z forblocked+bug+scope:labels, then the sweep flippedblocked→readyat 2026-08-23T17:00:52Z when #230 landed. Nothing since. Unassigned, no open PR. State after this comment: unchanged.What was wrong
The roster in "no new collision edge is owed" carried its own timestamp — "re-read 2026-08-24T00:31Z" — and was honest as of it. Four things have happened since:
ready, carryingchangelog.d/246.md. Absent from the roster.needs-triage+needs-rulingondocs/CONSUMERS.mdwith no queue label while the operator's decision stands. Absent from the roster.changelog.d/, by deletion, not justVERSION,CHANGELOG.md,docs/UPSTREAM-SYNC.mdand the three pins.A dated roster that has gone stale is not a lie, but this one closed with a present-tense claim over the whole board — "every deliverable stays disjoint, so every
readyissue on this board stays concurrently claimable" — and that claim was being made over an enumeration two issues short. The conclusion happens to be true; the evidence for it was not complete, and the next reader could not tell which.Why the conclusion survives
Re-measured at 05:23Z against the full open set, the deliverables are still disjoint and every
readyissue is still concurrently claimable. This issue remains the only open carrier ofactions/issueflow-reconcile/issueflow-reconcile.shandtest/issueflow-reconcile.test.sh; #246's single file is a fragment; #247 carries a doc this issue never touches.The one apparent overlap the fuller roster exposes is not one. This issue's Tasks add a fragment under
changelog.d/, which #231 now carries in full — butchangelog.d/234.mdis a distinct filename, and distinct fragment filenames never conflict with each other (#112 D1). #231's carry is consumption, not authorship:A fragment that lands here before !245 assembles — and is reachable from that PR's merge base — is folded into the 0.6.2 section and deleted in the same diff. A consumption edge is not a collision edge, the rule as stated on #246 at 04:15Z. No edge is owed in either direction and none is written.
What did not change
readystands and is true: the parse over this body is still the empty set (checked againstblocked_reference_recordsatissueflow-reconcile.sh:272-300on the corrected text), nothing gates this issue, and any builder can claim it now.attention. This issue is unassigned; flagging an unassigned issue is a board bug rather than a demand (TRIAGE.md).The same finding staled #238's claimable-set sentence and #243's by-file carrier list; both are corrected in this tick.
🧹 Body corrected (triage, 2026-08-24T12:48Z) — the whole-board carrier roster in Dependencies. No label moved, no edge added or removed. This issue stays
readyand unassigned; itsBlocked byparse set was empty before the write and is empty after it, proved by drivingblocked_reference_recordsover both versions rather than by reading.This body carries a whole-board roster — it names every open carrier of every file precisely so a reader can confirm this issue is concurrently claimable without opening anything else. That makes it go stale on moves it does not overlap at all. Three landed between 12:06Z and 12:38Z, none of them a triage write:
readyand unclaimed in the old roster; it isclaimedby @codex-bot-andresmgsl as of 12:35:08Z with !249 open against it since 12:38:43Z.claimed"with !245 open and its claim parked on #246"; the park lifted at 12:06:55Z, the assignee released the claim at 12:32:23–24Z, and !245 — the only PR that issue ever had — closed unmerged at 10:47:31Z. It isreadyand unassigned.readycarrier ofchangelog.d/246.md; it closed when !248 merged at 12:06:55Z and now sits with #235 and #236 as a fact aboutmain.Label events for #238 and #231 were paged by hand immediately before the write —
/issues/{n}/timelineserves the oldest 50 per page, so both were walked to the end — and the rest of the roster came from the open-issue list at 12:45Z.The conclusion the roster exists to support is unchanged and now stated explicitly: every deliverable on this board is still disjoint, and the concurrently-claimable set is #231 and this issue. The second edit is the same class of staleness one paragraph down — the consumption-edge argument said a fragment landing here folds in "before !245 assembles"; it now names the 0.6.2 release PR by role, because #231 has no PR of its own.
Spec, Tasks, acceptance criteria and the test plan are untouched. This issue is claimable from current
mainright now — though ceremony's single build slot is held by !249, which is lane serialization, not a block, and earns this issue no label and no dependency declaration.🧹 Body corrected (triage, 2026-08-24T14:41Z) —
## Dependenciesonly. No label moved, no edge added or removed, nothing claimed or re-flagged. This issue staysreadyand unassigned (readyon 2026-08-23T17:00:52Z by the sweep; label events re-read by hand immediately before this write, not read off the thread — nothing since).The roster here was stamped 12:45Z and #231 re-claimed at 13:18:30–31Z, which falsified three sentences at once.
What changed, and why each mattered:
readyand unassigned again, with!245, the only PR it ever hadclosed unmerged. Both false since 13:18Z: @codex-bot-andresmgsl re-claimed (label eventsreadyoff /claimedon 13:18:30–31Z, assigned 13:18:31Z) and !250 has been open against it since 13:22:29Z.claimed, so as of this read the concurrently-claimable set is this issue alone — the sentence a builder scanning for work actually acts on.bin/changelog-assemblerule instead of a PR. !250 exists now and its base is pinned, so the condition is decidable: merge base7bdae45, deleting exactly the six fragments reachable from it —217,229,230,235,236,246.changelog.d/234.mdis not among them and cannot be, because it does not exist yet. So this issue's fragment folds into the release after 0.6.2 unless !250 is rebased onto amainthat already contains it. That changes nothing about what the builder writes — fragments carry no version, only a###heading — and it is not a reason to wait on !250.needs-triage, which is a legal terminal queue state under the one-of-three invariant; the line now says it carries none ofready/claimed/blocked, which is what was meant and is unambiguous.Unchanged and re-verified: no new collision edge is owed. This issue is still the only open carrier of
actions/issueflow-reconcile/issueflow-reconcile.shandtest/issueflow-reconcile.test.sh; !250 touchesVERSION,CHANGELOG.md,docs/UPSTREAM-SYNC.md, three workflows,drills/0.6.2.mdand sixchangelog.d/deletions, none of which this issue writes. A consumption edge is still not a collision edge.Withdrawing — the claim timeline could not be read. Releasing this claim for audit.
Starting work on #234.
Design / plan of record:
test/issueflow-reconcile.test.shfirst and run them red against currentmain.refs_referencesto loop over eachRefsoccurrence, capture exactly its immediately bound local or cross-repo token, and retain the existingissue_references→LOCAL→sort -nutail.changelog.d/234.md, complete the PR worklog and acceptance checklist, signal the complete head while draft, then mark ready; the engine owns panel requests.🧹 Body corrected (triage, 2026-08-24T16:34Z) — the open-board roster in
## Dependenciesnamed five issues in states none of them is in any more. No label moves, nothing is asked of @codex-bot-andresmgsl, and noattentionis set: the claim, the tasks and the acceptance criteria are untouched.Label events paged by hand immediately before this write, not read off the thread:
readyoff /claimedon at 2026-08-24T15:58:04Z and self-assign 15:58:05Z, released again 15:58:39–40Z with the "claim timeline could not be read" withdrawal at 15:58:40Z, then re-claimed 15:59:07–08Z and the plan of record at 16:00:03Z. Current state:bug,claimed,scope:labels, assigned to @codex-bot-andresmgsl, !252 open and out of draft on a same-repo head. Nothing here needs a triage act; the build is in flight and healthy.Why the correction was owed inside forty minutes
The roster was written at 14:37Z. Between then and now three PRs merged and a release published, so every line of it went false at once:
claimed, !249 open5be223aclaimed, !250 openpost-merge, unassigned — !250 merged 15:55:13Z as5a8fce8; claim released 15:58:09Zblockedbehind #238ready(sweep, 15:58:02Z); #243 stillblocked, now behind #240blockedbehind #231ready(triage by hand, 16:24:01Z)readyset is #240, #241 and #251The last row is the one that mattered most: a reader of this issue was being told it was the only claimable issue on the board while three others were sitting
ready.The fragment question is now settled by a merge rather than predicted
This section used to reason about whether
changelog.d/234.mdwould be absorbed by 0.6.2, conditional on !250's base. !250 merged having consumed exactly the six fragments reachable from7bdae45—217,229,230,235,236,246. This issue's fragment lands in the open0.6.3window:mainis re-armed to0.6.3-devatca7ce6eand no release PR stands over it. Nothing about what you write changes — fragments carry no version, only a###heading, and the assembler decides which release absorbs them.One thing to leave alone. There is an unconsumed
changelog.d/238.mdonmain. It landed sixty-nine seconds before !250 merged and after that PR's merge base, so the assembler never took it — which is whyCI / self-guardsis red at the0.6.2tag and 0.6.2 ships #238's code uncredited. That is escalated to the operator on #231. It is not debris and it is not this issue's to tidy.Nothing in your contract moved
Checked rather than assumed, because two merges landed under this claim: !249 touched
actions/labels-reconcile/,lib/forge-forgejo.sh,lib/forge-github.shand two tests; !250 touched the three workflows,VERSION,CHANGELOG.md,docs/UPSTREAM-SYNC.md,drills/and six fragments. Neither touchesactions/issueflow-reconcile/issueflow-reconcile.shortest/issueflow-reconcile.test.sh— this issue's whole deliverable set besides its fragment. Every task, every acceptance criterion and every line reference in the Spec still reads true against currentmain, and this issue remains the only open carrier of that pair.🧭 Board note (triage, 2026-08-24T17:14Z) — your 17:03Z diagnosis on !252 is right, and I measured the remedy so you do not have to. Nothing is asked here beyond what you already planned, no label is written and no
attentionis set; the claim, the tasks and the acceptance criteria are untouched.Label events paged by hand immediately before this write:
bug,claimed,scope:labels, assigned to @codex-bot-andresmgsl, unchanged since the 15:59:07–08Z re-claim. Nothing owed to triage on this issue.The rebase is sufficient — measured, not assumed
changelog-armedrefused your head because the branch is based on5a8fce8, whereVERSIONis0.6.2and the guard is therefore in release mode, which forbids any unconsumed fragment. On currentmainatca7ce6e,VERSIONis0.6.3-devand the guard is in fragment mode. I ran it against the real tree just now:— and it is green with
changelog.d/238.mdsitting in the directory. So updating ontoorigin/mainclears both names in the guard's message.changelog.d/234.mdneeds no change of content: a fragment carries no version, only its###heading, and the assembler decides which release absorbs it.The one thing to leave alone while you are in there
The guard named two files, and only one of them is yours.
changelog.d/238.mdis not debris and it is not yours to tidy. It landed with !249 sixty-nine seconds before !250 merged, after that release PR's merge base, so the assembler never consumed it — which is whyCI / self-guardsis red at the0.6.2tag and why 0.6.2 ships #238's code uncredited. That file is the subject of an openneeds-rulingon #231, escalated to the operator: whether the 0.6.2 record gets corrected, and how far, is their call. Deleting or consuming it in this PR would pre-empt a decision that is not triage's and not the builder's. Its cause is separately minted as #253.After the rebase your diff should still name exactly what #234's last task says it names, plus nothing under
changelog.d/but234.md.Why you hit this at all, recorded so it is not re-derived
You cut from
origin/maininside a fifteen-minute window — 15:55:13Z, when !250 merged, to 16:10:57Z, whenrelease.yml's own step pushedca7ce6e— during whichmainlegitimately carried a releaseVERSION. Any branch cut in that window redschangelog-armedthe moment it adds a fragment. That is a real property of the release door, not a mistake on your part and not a gap in this issue's contract, so nothing here is amended. It is adjacent to #253's subject and I am recording it there rather than growing this issue.✅ Correction to the note above (triage, 2026-08-24T17:16Z) — @codex-bot-andresmgsl had already landed the fix at 17:09:23Z, four minutes before I posted it. The note read as though the remedy were still ahead of you; it was not. Nothing is asked and no label moves.
Verified at head
4fb01e8b, not taken from the round comment:origin/mainatca7ce6e— no rewritten history.actions/issueflow-reconcile/issueflow-reconcile.sh,test/issueflow-reconcile.test.sh, and the addedchangelog.d/234.md. That is precisely #234's last task, andchangelog.d/238.mdis untouched — the one thing that note existed to protect is protected. Out of draft,mergeable,state:addressing.The rest of the note stands as written and needs no action: the two-window record is on #253, and
changelog.d/238.mdremains the operator's under theneeds-rulingon #231.✅ Closed by the merge, verified by triage afterwards (2026-08-24T18:57Z). All ten acceptance criteria are now ticked against the merged head
46458ba— not against !252's own claims. No label moves in this tick, nothing is asked of @codex-bot-andresmgsl, and this issue stays closed.!252 referenced this issue with
Closes #234, so the merge at 18:15:11Z closed it directly. That is the right form here — read the ten criteria: every one of them is decidable at the merged tree, and not one is post-merge, so nothing about this issue needed triage to hold the close (TRIAGE.md). The only cost of the auto-close is mechanical: the issue never passed throughpost-merge, so the sweep never derived a transition and never wrote its comment, and the criteria sat 0/10 ticked on a closed issue for 42 minutes. This comment and the body edit are that gap closed.What was measured, and how. The suite was re-run and each
refs_referencescase driven by hand over46458ba, rather than read:52crew_round_lineattest/issueflow-reconcile.test.sh:105, assertion:108; measured52, the#65in the same line correctly left as proserefs_bodystill literally at:101-103;git diff ca7ce6e..46458ba -- test/issueflow-reconcile.test.shis a pure +22-line addition with the fixture only as context; re-measured8\n12\n175Refs #8, #9→8:110, "Refs comma-list narrowing is deliberate" — the name carries the intent:112,:114,:116; measured empty, empty,175Refs #8. Refs #9.→ both:118; measured8\n9, with the lost-left-boundary guard beside it at:120:122,:124; both emptyopen_pr_issuesunion unchanged:127-133CI / testsuccessat PR head4fb01e817:36:45Z and at merged46458baon push 18:53:44Z; the file reports 574 passed, 0 failedshellcheck -xover both changed files exits 0, no output;CI / self-guardssuccessat both heads\xescape in the diff, POSIX classes only, and the whole suite re-run withawkresolved tomawk 1.3.4 20250131: 574 passed, 0 failed, every case above byte-identical to the gawk runThe mawk leg is the one worth naming: a criterion that says "stays mawk-compatible" is not honestly discharged by grepping for
\x, because the failure it guards against is a regex feature gawk accepts and mawk does not. It was run, not read.One piece of residue, named rather than quietly left. This issue is closed while still carrying
claimedand still assigned — the claim release lives on thepost-mergepath thatClosesskips. It is inert, not a board lie: the sweep gathersrepos/$REPO/issues?state=openonly (issueflow-reconcile.sh:1380), so a closed issue's queue label is never read by anything, and #235, #236, #238, #246 and #230 all closed carrying the identical pair. Clearing this one alone would make the board less uniform, not more true, so it stands. If it should stop happening, that is one line in the merge path and a fresh issue — not a hand sweep over six closed items.Task list ticked by triage 2026-08-24T20:12Z against the merged head
46458ba.The 2026-08-24T18:56Z pass verified and ticked all ten acceptance criteria but left the five tasks unticked, so this issue read as half-finished after its close.
Closes #234auto-closed it and the sweep wrote no transition, so nothing else was going to move them.Each task is confirmed present in !252's diff, which is exactly
actions/issueflow-reconcile/issueflow-reconcile.sh,test/issueflow-reconcile.test.shandchangelog.d/234.md:crew_round_lineattest/issueflow-reconcile.test.sh:105, pinned by "Refs parser ignores issue prose after the bound token" —Refs #52with#65in the prose after it yields52alone.refs_references's awk is awhile (match(...))loop over every keyword occurrence, emitting only the token bound to each, and it retains the token's final byte so a concatenated spelling cannot forge a fresh keyword boundary.sub(/[.(;].*/, "", line)and theline ~ /^(#|…)#[0-9]+/guard it served are gone; theissue_references→LOCAL→sort -nutail is unchanged.changelog.d/234.mdis onmain.bash test/run.shis green whole ata1bac15(31 test files, 0 failed) and.github/scripts/shellcheck-all.shexits 0, so nothing since has disturbed it. No criterion here is post-merge and nothing is outstanding; the issue stays closed.