Skip to content

Resolve what a blocked issue says it waits for [#182] - #183

Merged
iderex merged 1 commit into
mainfrom
ci/an-issue-that-says-it-is-blocked-names-one
Aug 22, 2026
Merged

Resolve what a blocked issue says it waits for [#182]#183
iderex merged 1 commit into
mainfrom
ci/an-issue-that-says-it-is-blocked-names-one

Conversation

@iderex

@iderex iderex commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

Closes #182.

What was wrong

One label on this board means "waits on work tracked by another open issue on
this board", and it cannot say which issue. The body says it, and nothing read
the body. Two things follow from that, and the second is the one that costs.

A dependency written as a description cannot be followed by anybody who does
not already know the board. And a dependency that has closed goes on being
asserted, because there is no moment at which anything looks again: the label
is correct on the day it goes on, and the issue then sits there reading as
unavailable work while nothing is holding it.

Read against the board at the head of this branch:

go run . blockers
blockers: 20 open issue(s) carry blocked-on-another-issue, out of 182 row(s) the tracker holds
  #47: NAMES NO ISSUE, it carries blocked-on-another-issue and its body has no number to resolve
  #135: NAMES NO ISSUE, it carries blocked-on-another-issue and its body has no number to resolve
20 issue(s) read, 2 naming no issue, 0 no longer blocked, 0 unresolved.
blockers: 2 name no issue by number
exit status 1

Run 2026-08-22 against f91e4b7, with the eighteen lines that report a blocked
issue waiting on an open one cut so the two failures are readable. Nothing is
in the cleared state today because the four that were in it were repaired by
hand before this existed, which is exactly the point: nothing could tell, and
nothing would tell the next time.

What this does

go run . blockers reads the tracker and resolves every reference in the body
of every open issue carrying the label. It reports what each one waits on, and
it refuses four things, kept apart because their repairs are different:

An issue whose body names no number at all. Nothing can be resolved for it and
no later run will do better, so the repair is an edit to the body.

An issue whose named issues have all closed. That issue is available work every
reader walks past, and the repair is removing the label.

A reference this board does not hold. Reading it as closed would report an
issue as available on the strength of a typo.

A reading that failed, and a board declaring no such label. Both fail closed. A
listing filtered by a label nobody declares comes back empty, and an empty
result is otherwise indistinguishable from a board on which nothing is blocked;
one of those is the check working and the other is the check reading a name
that no longer exists.

The issue's done-when asks for the second of those to be reported rather than
refused. It is reported and it also reds the run, which is more than was asked
and is stated here rather than left for a reader to notice. A scheduled run
that prints a finding and exits green is read by nobody, and being read by
nobody is the failure this check is about.

Why it is not a leg of the gate

What it reads is the tracker, so its verdict moves when somebody edits an issue
rather than when this tree changes, and a merge blocked by that would punish
the wrong change. That is the reason pins and tokens are not legs either.
It runs daily rather than on their weekly cadence, because what they read moves
when somebody else publishes and what this reads moves when work on this board
closes.

The means

Go, in this repository's own module, with no dependency added. The reading is a
parameter rather than a call inside the run, so the suite produces every state
the run has to keep apart, including the ones a tracker cannot be asked for on
demand: a renamed label, a reference nobody ever opened, a reading that failed.
A shell block in a workflow would have carried the same logic in a language
this tree has no suite for, and none of those three states could then be
proved. The workflow is a wrapper around the verb, which is the shape every
other check here already takes.

What proves it

Every refusal is proved by a fixture that trips exactly it, and each guard was
shown to bite by switching it off and watching the suite go red. Run
2026-08-22 against f91e4b7, one line per mutation:

the label-declared refusal        --- FAIL: TestRunRefusesToPassABoardThatDeclaresNoSuchLabel
the nameless refusal              --- FAIL: TestRunRefusesABlockedIssueWhoseBodyNamesNoNumber
the cleared refusal               --- FAIL: TestRunRefusesABlockedIssueWhoseNamedIssuesHaveAllClosed
the unresolved refusal            --- FAIL: TestRunReportsAReferenceThisBoardDoesNotHoldAsUnresolved
the self-reference skip           --- FAIL: TestRunIgnoresAnIssueNamingItsOwnNumber
the pull-request and closed skip  --- FAIL: TestRunDoesNotJudgeAPullRequestCarryingTheLabel
the trailing-byte filter          --- FAIL: TestReferencesKeepsOutTheShapesThatAreNotIssueNumbers
the reading-failed refusal        --- FAIL: TestRunRefusesToPassAReadingThatFailed

The self-reference skip did not bite on the first attempt. Its fixture named an
open issue, so removing the skip changed nothing a reader could see; the
fixture now names one whose dependency has closed, where the skip is what
decides between "blocked by itself forever" and "cleared".

The gate, at the head of this branch:

go run . ci
gate: 7 legs, in order: format, vet, test, build, links, sitemap, invariants
  needs-network was not asked for.
  format: ok, 54 file(s)
  vet: ok
  test: ok, 26 test file(s)
  build: ok, 8 file(s)
  links: every reference that stays inside this site resolves to a file the build wrote
  sitemap: every page the build wrote is listed once, and every entry has a page behind it
  invariants: ok, 38 rule(s) decided, 1 owed and not decided
7 of 7 legs ran. None was skipped.

Run 2026-08-22 against f91e4b7. The cost line under needs-network is cut
for width and the run prints it in full.

go run . hygiene origin/main HEAD
1 commit(s) judged, none refused.

What this does not reach

Nothing here judges whether a reference is the dependency or a mention of
something else. An issue naming a closed number in passing beside its open
dependency reads as still blocked, which under-reports rather than sending
somebody to an issue that is genuinely waiting. #70 on the board today is
that case: it names #25 in prose about something else and the run counts it.

Nothing reads a reference to another board. The label's own description says
"another open issue on this board", so a number is resolved here or reported as
unresolved.

The two issues the run refuses today, #47 and #135, are not repaired by
this change. Both are outside what this branch covers and neither is touched.

The size

723 added lines, above the 400 this project inherits. 241 of them are the
package and 234 are its suite, with the rest comment. Dividing it would produce
either a package with no suite or a suite with nothing to run, and the rule
that no guard ships without proof that it bites is what refuses both halves.

No second reader

This change carries no second reader. The evidence above stands in place of
one: every refusal has a fixture, every guard was switched off and watched to
go red, and every number here carries the command that produced it, run at
f91e4b7.

An issue on this board declaring itself blocked names what it waits for in its
own body, and nothing read that. So a dependency written in prose could not be
followed, and a dependency that had closed went on being asserted: the issue
sat on the board reading as unavailable work while nothing was holding it.
Four were in that state when this was measured, two of them for a fortnight.

The blockers verb reads the tracker and resolves every reference. It refuses an
issue carrying the label whose body names no number, refuses one whose named
issues have all closed, and refuses a reference this board does not hold, and
it keeps those three apart because their repairs are different. A reading that
failed is a failure and never a pass, and a board that declares no such label
is a failure too: an empty result there means the label was renamed rather than
that nothing is blocked.

It is not a leg of the gate, for the reason the pins and tokens comparisons are
not legs. What it reads moves when somebody edits an issue rather than when
this tree changes, so it runs on a schedule and a merge is never blocked by it.

Every refusal is proved by a fixture that trips exactly it, and each was shown
to bite by switching it off and watching the suite go red.

Signed-off-by: Nils Lehnen <30603423+iderex@users.noreply.github.com>
@iderex
iderex merged commit 51a1f10 into main Aug 22, 2026
17 checks passed
@iderex
iderex deleted the ci/an-issue-that-says-it-is-blocked-names-one branch August 22, 2026 20:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Sixteen of twenty-four dependencies are written as descriptions, and four had gone stale unnoticed

1 participant