drills/README.md — doors-unchanged condition 2 keeps its own copy of the release path, and the copy has been one file short since #198 #251
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#251
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
drills/README.md's Doors unchanged condition 2 names the release pathtwice: it says the path is exactly the output of
.github/scripts/release-path.sh, and then prints its own copy of thatoutput. The copy is wrong on this tree, and has been since the doctrine
arrived here.
drills/README.md#L86-L91— lists six paths:.github/workflows/release.yml,bin/,lib/version.sh,lib/decide.sh,lib/facts.sh,lib/changelog.sh..github/scripts/release-path.sh— prints seven.lib/forge.shis the seventh, and the script's own header says why: #191 put the release doors on the forge shim, solib/forge.shis part of the doors' executable path on this forge and stands for the two backends that are sourced through$FORGE_LIB_DIRat run time (#198).Both landed in the same commit: the #198 merge
e035130created.github/scripts/release-path.shon this tree with the forge-adapted seventhpath, and took upstream's
drills/README.md— whose condition 2 was writtenat
75d85df(#237) against the six-path upstream script — unchanged besideit. Neither side was wrong when written; the merge left them disagreeing, and
nothing has read them against each other since. The stale copy has stood
through both
0.6.1and0.6.2.Why this is worth a fix and not a shrug. Condition 2 is one of three
conditions that let a release skip a live drill. A release author who
copy-pastes the README's list measures six of the seven paths and can assert
doors-unchanged while
lib/forge.sh— a file both doors execute here —changed underneath. That is the exact failure the doors-unchanged shape was
built to refuse: #217/#237 added the executable list because the
0.5.0record had to explain a
lib/delta it had predicted away.And the README tells the reader the copy is guarded when it is not.
Condition 2 ends "its contract test keeps this inline list and the workflow's
direct and transitive dependencies in agreement."
test/release-path.test.shholds the script's list against the workflow's direct and transitive
dependencies; it never opens
drills/README.md. Standing in the README,"this inline list" reads as the list on the page — so the one sentence that
would make a reader check the script instead is the sentence that tells them
they do not have to.
Both live release records already did the right thing without the README:
drills/0.6.1.mdanddrills/0.6.2.mdeach quoterelease-path.sh's output,seven paths included, and every reviewer on !250 re-measured against the script.
0.6.2has since been cut, tagged and published —5a8fce8, 2026-08-24 — sodrills/0.6.2.mdis a shipped record rather than a candidate's, and it iscorrect. The doctrine page is the stale copy, not the records: this issue
corrects no shipped measurement. Two live releases have now been recorded past
this defect without tripping over it, which is precisely why it survived — the
copy only bites an author who trusts it instead of the script.
Surfaced as a non-blocking review note on !250 (release
0.6.2, #231) androuted here rather than into that PR:
drills/README.mdwas not a carrier !250could touch.
Spec
Decision: delete the copy rather than repair it. Repairing it — adding
lib/forge.shto the README — leaves two lists that must be kept equal byhand, permanently, with the forge one diverging from upstream's prose in a
file upstream owns. Deleting it leaves one list, in the file that is already
executable, already tested against the workflow, and already what every
record quotes.
release path is exactly what
.github/scripts/release-path.shprints, anddirects the record author to run the script and paste its output —
git diff <last-rehearsed-tag>..HEAD -- $(sh .github/scripts/release-path.sh)is the form the records already use. No path names in the sentence.
script's contract test (
test/release-path.test.sh) keeps the script'slist and the workflow's direct and transitive dependencies in agreement.
The reader must be left with no impression that any list in
drills/README.mdis machine-checked, because after this change therewill not be one.
.github/workflows/release.ymlnames the one file whose pin line is theallowed diff — that is the condition's content, not an enumeration of the
path. The
lib/ruling.shmentions in the worked-example paragraph are ameasured fact about
0.5.0(#233, #217, #237) and are equally not thelist. Do not touch either.
test/release-path.test.shasserting thatdrills/README.mdnames noneof the five path tokens that only ever appear as members of an
enumeration:
bin/,lib/version.sh,lib/decide.sh,lib/facts.sh,lib/changelog.sh. Each occurs exactly once in the file today, all fiveinside condition 2's list; none has a standalone reason to be named in
drill doctrine. The case must fail on the file as it stands on
mainandpass after the edit — write it in that order and record both runs. The
guard belongs beside the script's own contract tests, because it is the
same contract read from the other side: one list, and the script owns it.
.github/scripts/release-path.shis correct and stays byte-identical: this issue changes prose and a test,
never the release path itself. A PR that touches the script has
misunderstood the deliverable.
changelog.d/251.mdin the grouped shape theshapesentinel declares, under### Fixed.Tasks
origin/main.test/release-path.test.sh; run it against theunedited
drills/README.mdand record the failure.author to run the script, and correct the guarantee sentence to name
what the contract test actually holds.
test/release-path.test.shgreen, thenbash test/run.shwhole.git diff --check.git diff origin/main..HEAD --statnames exactlydrills/README.md,test/release-path.test.sh, and the fragment.Acceptance criteria
drills/README.mdcontains none ofbin/,lib/version.sh,lib/decide.sh,lib/facts.sh,lib/changelog.sh, and still containscondition 1's
.github/workflows/release.ymland the worked example'slib/ruling.sh..github/scripts/release-path.shand now tells the record author to runit; the sentence about the contract test claims coverage only of the
script's list.
test/release-path.test.shcarries a case that fails on the pre-changedrills/README.mdand passes after, and the PR records both runs.bash test/run.shis green whole at the PR head;git diff --checkisclean.
git diff origin/main..HEADtouches exactly three files:drills/README.md,test/release-path.test.sh, and the newchangelog.d/251.md..github/scripts/release-path.shisbyte-identical to
main.Test plan
main'sdrills/README.md— five tokens present, five reported.drills/README.md(say a helpfullib/forge.sh) turns the new case red.This is the class the guard exists for; state it in the PR.
release-pathcases, which compare thescript's output against the workflow's direct and transitive dependencies
and are untouched by this work.
bash test/run.sh. Note in the PR any test file thatcannot run in the builder's box and why, with the same file measured at the
base commit as the control.
Dependencies
Nothing open blocks this issue; the parse over this body is the empty set.
The one edge it ever carried was against the
0.6.2release PR, and itscondition was that PR's merge, not #231's close — as this section said when
it was written. !250 merged 2026-08-24T15:55:13Z as
5a8fce8, so the hazardis spent and triage flipped this issue to
readyby hand and rewrote thisparagraph in the same tick, rather than leave
blockedprose standing over areadylabel. The marker phrase is rewritten away with it: the parser unionsthat phrase even under a sentence saying the clause no longer applies
(RELEASES.md, flip mechanics).
What the edge was, kept as history. While !250 was open, its section was
proved byte-for-byte by
actions/changelog-assembledagainst the fragmentsreachable from its merge base
7bdae45. A newchangelog.d/fragment landingon
mainmoves that base and turns the release PR's own guard red. This issueadds
changelog.d/251.md, so it waited.The hazard was real, and it is worth recording that it fired on a different
issue in the window this one sat out.
changelog.d/238.mdlanded onmainat15:54 with !249, sixty-nine seconds before !250 merged — after the base, and so
never consumed.
CI / self-guardsis red at the tagged commit5a8fce8(
changelog-armedrefuses the unconsumed fragment), and 0.6.2 ships #238's codewithout crediting it. That is the exact outcome this issue's wait avoided; it is
open on #231 as an escalation to the operator, and it does not reach this
issue:
0.6.2is cut andmainis re-armed to0.6.3-devatca7ce6e, sochangelog.d/251.mdlands in an open0.6.3window with no release PR standingover it.
No collision edge is owed. This issue's deliverable set is
drills/README.md,test/release-path.test.shandchangelog.d/251.md, andno other open issue on this board writes any of them. That is stated as the
standing fact rather than as a dated roster of who holds what this hour, because
a roster expires on the next claim, merge or mint, and this claim does not.
drills/is untouched by the rest of the open board, and no open issue writestest/release-path.test.sh; thetest/files the rest of the board does holdare different files in the same directory, which is not a collision. Distinct
fragment filenames never conflict with each other (#112 D1). This issue is
concurrently claimable with every
readyissue on the board.(The roster of who else is open, and of which neighbouring
test/files theyhold, is removed here rather than re-dated a third time — triage,
2026-08-24T22:18Z. It expired twice in under four hours without its answer ever
changing: #234 closed at 18:15:11Z when !252 merged, and #240 closed at
19:58:11Z when !254 merged, each falsifying a paragraph written to record that
rosters expire. What survives is the rule, which does not: check this issue's
three paths against the deliverable set of every open
ready,claimedorblockedissue, taking each queue label from label events rather than off.labels, and re-run it against the live board rather than reading a listwritten here. Empty again at 2026-08-24T22:18Z.)
Its premises were re-measured against current
mainat the flip, not carriedforward on trust. !250 changed thirteen files and
drills/README.mdis notamong them, so nothing this issue is minted against moved. On
ca7ce6e:.github/scripts/release-path.shprints seven paths,drills/README.mdstill prints six, each of the five enumeration-only tokens (
bin/,lib/version.sh,lib/decide.sh,lib/facts.sh,lib/changelog.sh) occursexactly once,
.github/workflows/release.ymloccurs twice andlib/ruling.shtwice — exactly the counts decision 4 and the first acceptancecriterion assume. Every task and every criterion below is executable as written.
No release-window edge is owed. Under #343 membership lives in a
## Membersrecord with no fallback to the gate, and neither open issue carrying
releasehas one: #231 (
post-merge) and #228, the sync epic, which has carried thelabel since its 2026-08-17T22:26:57Z mint. So no window stands and this issue is
not a non-member of anything. (Corrected 2026-08-24T17:47Z: this read "#231 has
no such record and no other open issue carries the
releaselabel", which #228falsifies. The conclusion is unchanged — the window test is the membership
record, never the label.)
This is not a child of #228: that epic adopts upstream
0.6.1–0.6.3, and thedrift is a forge-side consequence of #198's own merge resolution, not upstream
content.
This issue's
Blocked bydeclarations parse to: {#231}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.
claude-bot-andresmgsl referenced this issue2026-08-24 16:23:59 +00:00
This issue's
Blocked bydeclarations parse to: {}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.
This issue is
blocked, but its body has no parseableBlocked by #Ndeclaration. The sweep will not guess the dependency.🟢 Gate cleared by hand — this issue is
ready, and the body no longer says otherwise.readyon at 2026-08-24T16:25:49Z,blockedoff at 16:25:50Z, and## Dependencieswas rewritten in the same tick rather than left standing over the new label.Label events paged by hand immediately before the write, not read off the thread:
documentation+blocked+scope:release-flow+scope:docsat the 2026-08-24T15:42:45–47Z mint (triage), then the reconciler'sblockers-parsed-231comment at 15:53:41Z, then nothing until this flip. Unassigned throughout; noattentionand none owed.The condition that fired was the merge, not the close
This issue's declaration named its own wake precisely, and it is worth repeating because it is unusual: the hazard cleared at !250's merge, not at #231's close. !250 merged 2026-08-24T15:55:13Z as
5a8fce8. #231 is still open —post-mergesince 15:58:08Z, on a criterion of its own — and that is irrelevant here, because what this issue waited on was a live release PR's merge base, and there is no longer a release PR.So the sweep could not have done this: it flips on a closed blocker. A hand flip was written into the declaration at mint for exactly this reason, and this is it.
The hazard was not theoretical — it fired next door in the same window
Recorded because it is the strongest possible argument that this issue's wait was correct, and because it belongs in the record of what 0.6.2 cost:
changelog.d/238.mdlanded onmainat 15:54 with !249 — sixty-nine seconds before !250 merged, and after !250's merge base7bdae45. The assembler never saw it. Consequences, measured rather than argued:CI / self-guardsis red at the tagged commit5a8fce8:changelog-armedrefuses a released tree carrying an unconsumed fragment. Reproduced locally against both commits; green again atca7ce6e.That is the exact outcome this issue's wait avoided by one issue. It is escalated to the operator on #231 and nothing about it reaches this issue:
mainis re-armed to0.6.3-dev, no release PR stands, andchangelog.d/251.mdlands in an open window.Premises re-measured against
mainbefore the flip, not carried forward!250 changed thirteen files and
drills/README.mdis not among them — so a blocker's merge invalidated nothing here. Checked anyway, atca7ce6e, because a flip is where a stale criterion surfaces:mainrelease-path.shprints seven pathsdrills/README.mdcondition 2 copies sixlib/forge.shstill absentbin/1,lib/version.sh1,lib/decide.sh1,lib/facts.sh1,lib/changelog.sh1 ✓.github/workflows/release.ymland the worked example'slib/ruling.shsurvive the guardThose are precisely the counts decision 4 and the first acceptance criterion depend on. Every task and criterion is executable as written; the failing-case-first order the spec demands still produces a red on the tree as it stands.
Two lines of Context corrected in the same tick
The body called
drills/0.6.2.md"the0.6.2candidate's" and said the stale copy had stood "through the0.6.2candidate". 0.6.2 is cut, tagged and published, so both are now false in the same small way. Corrected, not negated in place:drills/0.6.2.mdis a shipped record, it quotes all seven paths, and it is correct. Two live releases have now been recorded past this defect without tripping over it — which is exactly why it survived this long. The copy only bites an author who trusts it instead of the script.No collision edge is owed and none is written. Deliverables are
drills/README.md,test/release-path.test.shandchangelog.d/251.md; no open issue on this board carries either of the first two. This issue is concurrently claimable with #234, #240 and #241.🔧 Conflict resolved (triage, 2026-08-24T16:35Z) — the
blocked-unparseablecomment above is a two-second artefact of this tick's own flip, not a malformed queue state. No label moves; the state it complained about no longer exists.What happened, to the second. The flip is two writes and they cannot be atomic: the body write that rewrites the
Blocked by #231marker away, then the label write. Body landed ~16:25:48Z;readyon 16:25:49Z;blockedoff 16:25:50Z. The sweep read the issue at 16:25:51Z — after the marker was gone and, on its read, withblockedstill on. It did exactly what it should: refused to guess, and said so. Both its comments are accurate about the instant they describe.The state now, label events paged by hand immediately before this write:
documentation,ready,scope:docs,scope:release-flow, unassigned.readyon 16:25:49Z,blockedoff 16:25:50Z, both by triage, and nothing since. Theblockers-parsed-noneecho is correct and stays correct — this issue declares nothing, because nothing open blocks it. The parse over its body is the empty set by design.Nothing is owed to the sweep here. Its marker carries the parsed set, so an unchanged parse never re-posts, and the
blocked-unparseablepath writes no label. There is no staleblockedto clear and no falsereadyto correct: this issue is genuinely claimable, its declaration was genuinely retired at !250's merge, and the reason the marker had to be deleted rather than annotated is the same reason this warning fired — the parser unions its marker phrase even under a sentence saying the clause no longer applies.Recorded rather than left, because TRIAGE.md puts the resolution of automation's conflict comments on triage, and an unanswered warning on a
readyissue reads to the next scanner as a board that has not been looked at.For the next flip, the ordering that avoids this: remove the queue label before writing the body, so the sweep can never observe
blockedbeside an empty parse. #241's flip in this same tick did not trip it — its two label writes fell a second apart in a window no sweep ran in — which is luck rather than method.claude-bot-andresmgsl referenced this issue2026-08-24 16:37:59 +00:00
🧹 Body corrected (triage, 2026-08-24T17:47Z) — one false clause in
## Dependencies.readyis still the true label, no label moves, and nothing about this issue's contract changes.Label events paged by hand immediately before this write.
documentation/blocked/scope:release-flow/scope:docsat the 2026-08-24T15:42 mint, thenreadyon andblockedoff at 16:25:49–50Z (triage, by hand, when !250 merged). Nothing since. Current state:ready, unassigned — unchanged by this comment.The clause
## Dependenciessaid "#231 has no such record and no other open issue carries thereleaselabel". #228 falsifies it: the upstream-sync epic is open and has carriedreleasecontinuously since its mint at 2026-08-17T22:26:57Z — the label was never removed from it, unlike #231, where the lead took it off at 2026-08-17T23:33:02Z and triage returned it at 2026-08-23T17:16:46Z.The conclusion it supported is unchanged, and that is why this is a correction rather than a flip. Under #343 a standing release window is read from a release issue's
## Membersrecord, with no fallback to the gate label — and neither #231 nor #228 has such a heading (both bodies grepped just now). So no window stands, no membership call is owed, and this issue is a non-member of nothing. The clause was reasoning from the label instead of from the record; it reached the right answer by a route that does not hold.It is corrected in place with the old wording quoted, rather than swapped silently.
No
attentionis set: this issue is unassigned.🧹 Body corrected (triage, 2026-08-24T19:02Z) — one paragraph in
## Dependencies, replaced by the invariant it was serving rather than re-dated. No label moves: this issue staysreadyand unassigned, and theBlocked byparse over the body is the empty set before and after the write.The paragraph was a 16:24Z roster of the whole open board, and it had expired three separate ways without its answer changing by a word:
claimed— #234 closed at 18:15:11Z when !252 merged;ready— @codex-bot-andresmgsl claimed it at 17:47:17Z;The third is the one worth naming. A stale state is visibly stale — a reader sees
claimedon a closed issue and knows to re-check. A missing row is invisible: an incomplete roster reads exactly like a clean board, and the whole purpose of the paragraph is to prove a negative over every open issue. A snapshot cannot prove that negative for issues minted after it was taken.So the paragraph now states the fact the answer rests on: no other open issue writes
drills/README.md,test/release-path.test.shor this issue's fragment.drills/is untouched by the rest of the board, and the nearest neighbours intest/are three different files — #240'stest/forge-backends.test.sh, #243'stest/labels-reconcile.test.shand #253'stest/changelog-assembled.test.sh. The derivation is still dated so it can be re-run, but it no longer expires when somebody claims or mints something.Nothing else moved. The history of the spent !250 edge, the re-measured token counts on
ca7ce6e, the release-window paragraph and every task and criterion are untouched.ready, unassigned, noattention, claimable now.claude-bot-andresmgsl referenced this issue2026-08-24 20:09:27 +00:00
🧹 Body corrected (triage, 2026-08-24T22:18Z) — one paragraph in
## Dependencies. No label moves: this issue staysreadyand unassigned, theBlocked byparse over the body is the empty set before and after the write, and no task, criterion or decision is touched.Label events paged by hand immediately before this write, not read off
.labels:documentation+blocked+scope:release-flow+scope:docsat the 2026-08-24T15:42:45–47Z mint, thenreadyon 16:25:49Z andblockedoff 16:25:50Z. Nothing since. Current state:documentation,ready,scope:docs,scope:release-flow, unassigned — unchanged by this comment.What was false
The paragraph named "the nearest neighbours in
test/are #240 (test/forge-backends.test.sh), #243 … and #253 …", under a parenthetical deriving it "over the whole open board — #228, #231, #240, #241, #243, #247, #253". #240 closed at 2026-08-24T19:58:11Z when !254 merged. The conclusion never moved —drills/README.md,test/release-path.test.shandchangelog.d/251.mdare in no other open issue's deliverable set — but the roster carrying it did.Why it is deleted rather than re-dated
That paragraph was written at 19:02Z to replace an expired 16:24Z roster, and its own note said an incomplete roster reads as a clean board. It expired again 56 minutes later. So the enumeration is removed instead of corrected a third time, and what stands is the rule and the file-level facts it produces:
drills/is untouched by the rest of the open board, no open issue writestest/release-path.test.sh, and thetest/files the rest of the board does hold are different files in the same directory — which is not a collision. Distinct fragment filenames never conflict (#112 D1).The check — this issue's three paths against the deliverable set of every open
ready,claimedorblockedissue, each queue label read from label events — was empty again at 22:18Z. This issue is concurrently claimable with everyreadyissue on the board.Starting build as @codex-bot-andresmgsl.
Plan of record:
drills/README.mdenumeration guard totest/release-path.test.shfirst and record its expected failure on the unchanged README.lib/ruling.shworked example.changelog.d/251.md, then run the focused test, whole suite, sanctioned shellcheck,git diff --check, and exact-scope/byte-identity checks.The wrong-fix control is explicit: reintroducing any enumeration-only token, including a tempting
lib/forge.sh, must make the new guard fail.✅ Closed by merge, verified and ticked by hand (triage, 2026-08-25T09:04Z). Every acceptance criterion re-measured against merged
main; both checklists ticked in the body in this same tick. No corrective work is owed and no successor is released.!260 merged 2026-08-25T08:57:40Z as
f6f2ec7carryingCloses #251, so the forge closed this issue directly. That path skipspost-mergeand writes no transition comment, and it leaves both the Tasks and the Acceptance-criteria lists unticked — twelve empty boxes on a closed issue read as work abandoned rather than work finished. This comment is that missing tick, and it is written from a re-measurement rather than from the PR's own worklog.Closeswas the correct marker here: not one of the five criteria is post-merge, so none of them needed theRefs #Nmechanism the contract reserves for that case.Re-measured at
f6f2ec7, not taken from the PREach criterion run against the merged tree, and the RED-first premise re-run against the pre-merge head
6dc8bf6:drills/README.mdnames none ofbin/,lib/version.sh,lib/decide.sh,lib/facts.sh,lib/changelog.sh6dc8bf6.github/workflows/release.ymland the worked example'slib/ruling.shsurviverelease.ymlwas 2 before: the second occurrence was the copy)git diff <last-rehearsed-tag>..HEAD -- $(sh .github/scripts/release-path.sh)test/release-path.test.shcarries a case red before and green afterreadme_has_no_path_enumeration; 7 passed, 0 failed atf6f2ec7, and all five tokens are present at6dc8bf6, which is the failure the PR recorded as6 passed, 1 failedbash test/run.shgreen whole;git diff --checkcleangit diff --check 6dc8bf6 f6f2ec7clean.github/scripts/release-path.shbyte-identicalchangelog.d/251.md,drills/README.md,test/release-path.test.sh; the script is byte-identical to6dc8bf6One thing the builder did beyond the letter of decision 4, and it is right. The guard forbids six tokens, not five:
lib/forge.shis in the list alongside the five the spec enumerated. That is not scope creep — the Test plan named exactly this as the wrong-fix control ("re-adding any single path name … say a helpfullib/forge.sh… turns the new case red"), and a guard that permitted the seventh path would have been open at the one place this defect is most likely to be re-introduced, by someone correcting the copy instead of deleting it. The PR records the mutation run: restoringlib/forge.shgives6 passed, 1 failedwith the token named.The doctrine now has one list, and it is executable
The failure this issue was minted against is closed at its root: a release author reading condition 2 is sent to
.github/scripts/release-path.shand cannot copy a list out of the page, because there is no list on the page.drills/0.6.1.mdanddrills/0.6.2.mdwere already correct — they quoted the script — so, as the body said at mint, this change corrects no shipped measurement. It removes the trap that both records happened to step over.Label residue, stated so the next scanner does not clean it up
Label events paged by hand immediately before this write:
documentation+blocked+scope:release-flow+scope:docsat the 2026-08-24T15:42:45–47Z mint,readyon 16:25:49Z /blockedoff 16:25:50Z (triage, when !250 merged),readyoff 2026-08-25T07:30:19Z andclaimedon 07:30:20Z with @codex-bot-andresmgsl assigned, then the close at 08:57:40Z by @andres.So this issue is closed while still wearing
claimedand its assignee. That is not a board lie and it is deliberately left alone: theCloses #Npath releases no claim, and the issue-flow reconciler is open-scoped, so nothing will ever trip over it. The board's claimability invariants range over open issues; a closed one holds no slot.Nothing is released and nothing is owed
Blocked byparse over every open issue's body — driven through this repository's ownblocked_references/blocked_cross_references, not by eye — returns the empty set on all four (#228, #231, #243, #247). Nothing was waiting on this close.attention. The work is delivered; there is no next move to hand the assignee, and the issue is closed.## Dependencieshistory — the spent !250 edge, thechangelog.d/238.mdhazard that fired next door — stays as the record of why this issue waited. The 0.6.2 credit gap it describes is live on #231 and is not this issue's tail.