Skip to content

Name a set of issues that waits on itself [#234] - #235

Merged
iderex merged 1 commit into
mainfrom
ci/a-dependency-cycle-reads-as-an-ordinary-block
Sep 4, 2026
Merged

Name a set of issues that waits on itself [#234]#235
iderex merged 1 commit into
mainfrom
ci/a-dependency-cycle-reads-as-an-ordinary-block

Conversation

@iderex

@iderex iderex commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Closes #234.

What was wrong

go run . blockers asks two questions of every issue carrying
blocked-on-another-issue: whether its body names a number at all, and
whether the numbers it names are still open. Neither asks whether the issue an
issue waits for is waiting back.

A set in which every member waits on another member of the same set passes both
questions untouched. Every member prints as ordinarily blocked, every member is
subtracted from the count of available work by its own label, and the run ends
saying nothing is wrong. Nothing closing outside the set ends any of it, so no
later run does better and no reader is sent to look. At 766fb1b the board
had one such set and the run called it clean:

go run . blockers | tail -1
16 issue(s) read, 0 naming no issue, 0 no longer blocked, 0 unresolved.

Run 2026-09-04 at 766fb1b.

What this does

A third reading over the same references, printed on its own line and counted
in the last line beside the three counts already there:

go run . blockers | tail -2
  WAITING ON ITSELF: #35, #36, #50, #63, #71, #85, #87, #90 each wait on another issue in this set, so nothing closing outside it makes any of them available
16 issue(s) read, 0 naming no issue, 0 no longer blocked, 0 unresolved, 1 set(s) waiting on themselves.

Run 2026-09-04 at 0e04670, over the live tracker.

Eight issues, and six of the pairs in that set are visible by eye. #85 is not:
it names #35, #35 names #63, #63 names #87, and #87 names #85, so it arrives
back at itself over four edges with no two of those five bodies naming each
other. That is why the walk follows the waits as far as they go rather than
comparing bodies two at a time, and the suite carries that near miss as a row.

Only an issue carrying the label is a member. An issue without it is claiming
to wait for nothing, so a number in its body is a mention and not an edge,
which keeps a popular open issue out of a set it is merely named by. Two rows
prove that in both directions, over an unlabelled issue and over a closed one.

What it refuses, which is nothing

The set is reported and the verdict does not move. This is the one place the
package prints a state in capitals without failing on it, and the package
comment says so where a reader meets it rather than only here.

The reason is the repair. The two questions that refuse are answered by the
issue's own author editing a body or removing a label. Which member of a set
gives way is a reading of what those issues are for, which no run takes and no
single author takes alone, so refusing would stand a red gate on this board
until somebody takes it. Whether it should refuse anyway is the open half of
#234 and is not decided here. One row proves the report is printed beside a
failure rather than instead of one, so every state that does refuse still
reaches the verdict.

The inherited bound is the one References already declares: a number named
in passing reads as a wait. A set needs the reference to run in both
directions, so both bodies would have to be describing something other than a
wait, in opposite directions, at once. The set found on this board is not that
case, and #35 records the reciprocal wait in its own words rather than only in
the pattern.

What was run

go run . ci
gate: 7 legs, in order: format, vet, test, build, links, sitemap, invariants
  needs-network was not asked for. Asking costs a request to the public name from whatever machine runs it, and a verdict that moves when somebody else's service does rather than when this tree changes. Ask with: go run ./harness/needs-network
  format: ok, 74 file(s)
  vet: ok
  test: ok, 37 test file(s)
  build: ok, 22 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, 39 rule(s) decided, 2 owed and not decided
7 of 7 legs ran. None was skipped.

go run . hygiene origin/main HEAD
hygiene: 1 non-merge commit(s) in origin/main..HEAD, origin internal
  0e04670b6898: subject carries its reference
1 commit(s) judged, none refused.

git diff --name-only origin/main...HEAD
internal/blockers/blockers.go
internal/blockers/blockers_test.go

All run 2026-09-04 at 0e04670.

Each new row was shown to bite by returning no set from the walk and re-running
the suite:

go test ./internal/blockers/
--- FAIL: TestRunNamesTwoIssuesThatWaitOnEachOther (0.00s)
--- FAIL: TestRunNamesASetLongerThanAPair (0.00s)
--- FAIL: TestRunNamesEachSetSeparatelyRatherThanMergingThem (0.00s)
--- FAIL: TestRunReportsASetWithoutMovingTheVerdictItWouldOtherwiseReach (0.00s)
FAIL

Run 2026-09-04 with return nil at the head of the walk, reverted before the
commit. The two rows that prove an unlabelled issue and a closed one contribute
no edge stay green under that switch, because what they assert is the absence
of a set.

No second reader

Nothing in this change was read by a second person. The ruleset on this branch
requires no approving review, so a merge here is not evidence of one:

gh api repos/Flowfin/site/rulesets/20572614 --jq '{bypass: .bypass_actors, required: [.rules[].type]}'
{"bypass":[],"required":["deletion","non_fast_forward","pull_request","required_signatures"]}

Run 2026-09-04. No approving review is among those rules, so nobody but the
author has to have read this before it merges. The evidence above stands in
place of one.

The blockers run resolves what each blocked issue names and asks whether those
issues are still open. Neither question asks whether the issue an issue waits
for is waiting back, so a set in which every member waits on another member of
the same set passes both untouched: every member reads as ordinarily blocked,
every member is subtracted from the count of available work by its label, and
the last line of the run calls the board clean. Nothing closing outside such a
set ends any of it, so no later run does better on its own and no reader is
asked to look.

Eight open issues on this board are in one today, and only six of the pairs are
visible by eye. #85 reaches the set over four edges with no two of the five
bodies on the path naming each other, which is why the walk follows the waits
as far as they go instead of comparing bodies two at a time.

Only an issue carrying the label is a member, because an issue that does not
carry it is claiming to wait for nothing and a number in its body is a mention
rather than an edge. That is what keeps a popular open issue out of a set it is
merely named by, and it is proved in both directions: an unlabelled issue and a
closed one.

The set is reported and refuses nothing, which is the one place this package
prints a state in capitals without failing on it, and the code says so where a
reader meets it. Refusing would put a red gate on a condition whose only repair
is a reading of what those issues are for, which the run cannot take and the
author of a single issue cannot take alone. Whether it should refuse anyway is
the open half of #234 and is not decided here. A row proves the report is
printed beside a failure rather than instead of one, so the states that do
refuse still reach the verdict.

Each row was shown to bite by returning no set from the walk and watching four
of them go red.

Signed-off-by: Nils Lehnen <30603423+iderex@users.noreply.github.com>
@iderex
iderex merged commit 2add524 into main Sep 4, 2026
17 checks passed
@iderex
iderex deleted the ci/a-dependency-cycle-reads-as-an-ordinary-block branch September 4, 2026 18:28
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.

A dependency cycle reads as an ordinary block, and five issues sit in one

1 participant