lib/forge-forgejo.sh — the commit-status rollup carries no workflowName, so labels-reconcile's #208 self-exclusion is inert and the label machine grades its own red run #243

Closed
opened 2026-08-23 18:39:09 +00:00 by claude-bot-andresmgsl · 17 comments

Delivered and closed. !261 merged 2026-08-25T11:52:50Z as 484eb79 with
Closes #243, so the merge closed this issue directly and it never passed
through post-merge. Triage completed the bookkeeping that path skips at
2026-08-25T12:07Z
: every task and every acceptance criterion below is ticked
against triage's own re-measurement of the merged tree — the full suite, both
focused counts, the sanctioned shellcheck sweep and an independent RED-first
replay at the merge base f6f2ec7 — and never against the PR's self-reported
checklist. The evidence, criterion by criterion, is in the completion comment on
this thread. The claimed label and the assignee are left standing on purpose:
LABELS.md's one-queue-label rule is scoped to open issues, and on a closed issue
the assignment is the plainest record of who built it.

The collision edge this body once carried is spent history, kept only because
it explains the shape.
#240 was the newest open carrier across the deliverable
set (lib/forge-forgejo.sh, test/forge-backends.test.sh,
test/labels-reconcile.test.sh), never a logical dependency in either
direction; it closed 2026-08-24T19:58:11Z when !254 merged as a1bac15, and
triage lifted the gate by hand at 2026-08-24T20:07:24Z with the defect and every
criterion re-measured against that merged head rather than carried forward on
trust — see Dependencies.

Context

actions/labels-reconcile was given one absolute exclusion in #208: the
label machine never grades its own runs.
Its rationale is written out at
labels-reconcile.sh:351-369
and ends: "a genuine reconcile failure surfaces on the Actions tab instead of
as blocker:ci-red, which is right because no PR edit can fix the label
machinery."

On this forge that exclusion has never fired. It is implemented as a field
test:

| select($self == "" or (.workflowName // "") != $self)

labels-reconcile.sh:415

and the Forgejo backend never emits workflowName. forge_pr_view maps the
combined commit status into the node shape checks_state parses
(forge-forgejo.sh:435-448)
and carries exactly four fields — __typename, context, state,
createdAt, completedAt. No workflow name. So .workflowName // "" is "",
"" != "labels" is always true, and every entry survives the filter,
including the label machine's own.

lib/forge-github.sh is unaffected: gh pr view --json statusCheckRollup
returns CheckRun nodes that carry workflowName, which is why the field test
was written this way.

Measured, on PR !242

The head 8f9f7e560f1b307b922750d0804acb777cde56dc rollup, read from
/repos/heavy-duty/ceremony/commits/{sha}/status at 2026-08-23T18:32Z:

status context
pending CI / test (pull_request)
pending CI / release-exercise (pull_request)
pending CI / self-guards (pull_request)
pending CI / action-exercise (pull_request)
pending CI / docs-sync-exercise (pull_request)
pending Refs guard / refs-not-closing (pull_request)
failure labels / labels (pull_request)

The only FAILURE in the rollup belongs to SELF_WORKFLOW, and
blocker:ci-red was set on !242 by forgejo-actions at 17:54:39Z. Under
#208 that write must not have happened: with the self entry dropped the
remaining set is all-pending, which classifies PENDING, and PENDING sets no
blocker.

Each status object carries context, created_at, creator, description, id, status, target_url, url, updated_at — measured, not assumed. The workflow name
is present on this forge only as the prefix of context before the first
/, and it does match the workflow's name: exactly: labels
self-labels.yml, CIci.yml, Refs guardrefs-guard.yml.

Why the suite is green while this is broken

Every assertion covering the #208 exclusion builds CheckRun fixtures that
carry workflowName by hand —
labels-reconcile.test.sh:456
and
:465.
No assertion drives the exclusion through a StatusContext-shaped rollup —
the only shape this forge produces. This is the same family as #209, #210,
#235, #238 and #240: the Forgejo backend drops a field the shared decision
code reads, and the shared code's own tests cannot see it.

What it costs

blocker:ci-red pins a PR to state:addressing and makes the reconciler
refuse state:needs-human
(LABELS.md).
Per FLEET.md,
notify.sh's only label filter is state:needs-human — so a PR whose every
real check is fine never reaches the operator's queue, because the label
machinery's own red is counted against it. #208 exists precisely to prevent
that, and #241 is why the labels run is red in the first place. The two are
independent: #241 is why the check fails, this issue is why that failure
becomes a blocker when #208 says it must not.

Spec

Decided, and not open for the builder to revisit:

  • Fix the backend, not the reconciler. forge_pr_view's stated job is to
    assemble "the shape the state machine reads … so the decision code is
    untouched"
    (forge-forgejo.sh:420-422).
    A missing field in that mapping is a backend gap. checks_state must stay
    byte-identical across backends, so actions/labels-reconcile/labels-reconcile.sh
    gets no diff.
  • Derive workflowName from the context prefix: everything before the
    first /.
    That is the only place this forge carries it.
  • A context with no / separator emits "", not a guess. An empty name
    filters nothing, which is the exclusion's own stated safe direction
    (labels-reconcile.sh:369:
    "the exclusion must never widen into dropping entries on a guess"). A
    third-party commit status such as codecov must keep counting.
  • Leave context whole — do not strip the derived prefix. ctx becomes
    [workflowName, context], still unique per check, so the
    newest-per-context collapse at
    :416
    is unchanged, and context stays the string a human reads in the logs.
  • Keep __typename: "StatusContext", state, createdAt, completedAt
    exactly as they are.
    Nothing in the reconciler reads __typename; only
    test fixtures set it. Do not restyle these entries as CheckRun.
  • lib/forge-github.sh gets no diff. GitHub's nodes already carry the
    field.
  • Do not "fix" this by dropping blocker:ci-red, by special-casing the
    string labels, or by widening the exclusion to a substring match.
    The
    bug is one absent field; restoring it is the whole fix.

Not in scope, and named so it is not silently absorbed: the six pending
entries above never settle, because runs 1416/1417 report "Blocked by required
conditions"
and no CI job ever ran at that head (checked against every run
Forgejo retains, back to 2026-08-04). That is a separate question about CI
approval on fork-headed PRs. It is not this issue, and this issue does not fix
it — a PENDING rollup is a correct read of a rollup that is pending.

Tasks

  • RED first. Add a test/forge-backends.test.sh assertion that
    forge_pr_view populates workflowName from the context prefix, and
    one that a context with no / yields "". Record the failing output.
  • RED first. Add a test/labels-reconcile.test.sh assertion driving
    checks_state through a rollup built only of StatusContext nodes
    in the shape forge_pr_view emits, whose sole FAILURE belongs to
    SELF_WORKFLOW, asserting PENDING. Record the failing output — this is
    the assertion whose absence hid the bug.
  • Add the workflowName field to the forge_pr_view rollup mapping and
    make both tests green.
  • Add changelog.d/243.md.
  • Run the full suite and the sanctioned shellcheck; record the counts.
  • In the PR, state that Forgejo carries the workflow name only inside
    context, and that lib/forge-github.sh needs no change because
    CheckRun nodes already carry the field.

Acceptance criteria

  • forge_pr_view emits workflowName for every rollup entry, set to the
    substring of context before the first /, with RED-first output
    recorded for the assertion.
  • A context carrying no / emits workflowName: "" and is not
    excluded by the reconciler, with RED-first output recorded.
  • checks_state, driven by a rollup of StatusContext nodes whose only
    FAILURE belongs to SELF_WORKFLOW, returns PENDING — not FAILURE — with
    RED-first output recorded.
  • A rollup whose only entries belong to SELF_WORKFLOW returns NONE, not
    SUCCESS — the accepted consequence at
    labels-reconcile.sh:365-367,
    asserted through the Forgejo shape.
  • A rollup carrying a real FAILURE from another workflow — e.g.
    CI / test (pull_request) — still returns FAILURE, so blocker:ci-red
    is still set on genuinely red PRs.
  • actions/labels-reconcile/labels-reconcile.sh has no diff.
  • lib/forge-github.sh has no diff.
  • No existing assertion is deleted or weakened; the assertion counts of
    both touched test files rise, with the before/after counts stated in the
    PR.
  • The full suite and the sanctioned shellcheck are green at the PR head,
    with the file and script counts stated.

Test plan

What proves it:

  • forge_pr_view against a fixture status list containing
    labels / labels (pull_request), CI / test (pull_request) and
    Refs guard / refs-not-closing (pull_request) yields workflowName of
    labels, CI and Refs guard.
  • checks_state with SELF_WORKFLOW=labels over the measured !242 rollup
    above returns PENDING.

What must fail:

  • A rollup whose only failure is CI / test (pull_request) must not
    become PENDING. If it does, the exclusion has widened past its workflow and
    the fix is wrong.
  • A third-party status with a bare context — codecov, no / — must not
    be dropped when SELF_WORKFLOW is empty or set. If it disappears, the
    derivation is guessing.
  • A context whose workflow name itself contains / truncates at the first
    separator and therefore does not match SELF_WORKFLOW. That is the safe
    direction — it filters nothing — and the test asserts the entry survives
    rather than asserting a clever parse.

Dependencies

Nothing open blocks this issue; the parse over this body is the empty set.
The one edge it ever carried was a collision with #240 on lib/forge-forgejo.sh
and test/forge-backends.test.sh, and it is spent. It is recorded here as
history only, outside any parseable declaration and with its marker phrase
rewritten away — the parser unions that phrase even under a sentence saying the
clause no longer applies, so a spent leg survives as prose only after the marker
is gone (RELEASES.md, flip mechanics).

The deliverable set is unchanged by the flip. lib/forge-forgejo.sh,
test/forge-backends.test.sh and test/labels-reconcile.test.sh — Task 2 adds
the StatusContext-shaped checks_state assertion to that third file — plus
changelog.d/243.md. actions/labels-reconcile/labels-reconcile.sh is
explicitly not in the set; a criterion above requires it to have no diff.

Why the edge existed, and why it is gone. #240 rewrote the paginator in
lib/forge-forgejo.sh and its regressions in test/forge-backends.test.sh, and
being the newest open carrier across this issue's whole set it took the edge
under #288 while both were concurrently claimable. #240 closed
2026-08-24T19:58:11Z
!254 merged as a1bac15, carrying Closes #240 — and a
closed issue is not one of the ready/claimed/blocked carrier states an edge
can be owed to. It is a fact about main instead, exactly as the rest of this
chain already was: #235 landed as 68b304d (!244, 2026-08-24T00:16:46Z), #236 as
17a1368 (!242, 2026-08-23T22:52:09Z), #238 as 5be223a (!249,
2026-08-24T15:54Z) and #246 as 7bdae45 (!248, 2026-08-24T12:06:55Z). Every link
is spent; nothing stands behind this issue, and its close releases no successor.

No collision edge is owed to any other open issue, and 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. No
other open issue on this board writes any of the three paths above
— not one of
them has any of the three in its deliverable set, so there is no state any of
them could move into that would owe this issue an edge.

(The roster of nearest neighbours that stood here is removed rather than
re-dated a third time — triage, 2026-08-24T23:26Z. It named #253 as an open
neighbour holding test/changelog-assembled.test.sh; #253 closed
2026-08-24T22:55:26Z
when !255 merged as e55e996. That is this paragraph's
third expiry in six hours — #240 at 19:58:11Z, the 22:18Z re-date, and now #253
and not one of them moved its answer. What survives is the check, which does not
expire: derive the neighbour set from the live board, taking every open ready,
claimed or blocked issue's deliverable set against the three paths above and
reading each queue label from label events rather than off .labels. Re-derived
and empty again at 2026-08-24T23:26Z; the only other open issue naming any of
these paths is #231, which is post-merge and therefore not a claimable carrier
an edge can be owed to.)

And distinct
fragment filenames never conflict with each other, which is what changelog.d/
exists for (#112 D1), so changelog.d/243.md owes nothing either: #231's carry
of that whole directory is consumption, by the rm -- "$f" at
bin/changelog-assemble:122-126,
and a consumption edge is not a collision edge (the rule as stated on #246,
2026-08-24T04:15Z). This issue is concurrently claimable with every ready issue
on the board.

Its premises were re-measured against the merged head at the flip, not carried
forward on trust — the blocker touched two of this issue's own three files.

!254's diff is exactly changelog.d/240.md, lib/forge-forgejo.sh and
test/forge-backends.test.sh, so each was re-read on a1bac15:

  • forge_pr_view's statusCheckRollup mapping is byte-identical to the
    pinned 1f5dd39 this issue was minted against — the only diff anywhere in that
    function since is #236's four-arm mergeable, which this body already
    accounts for. It still emits exactly __typename, context, state,
    createdAt and completedAt, and workflowName is still absent. The
    defect is intact and the spec, tasks and criteria below are executable as
    written.
  • The function moved from :435-448 to :449-482 on current main!254 added
    the exhaustive paginator above it. The permalinks in Context are pinned at
    1f5dd39 and still resolve, which is the whole reason they are pinned; a
    claimant reading current main should expect the new offsets.
  • test/forge-backends.test.sh rose 134 → 147 assertions under !254 with
    none deleted or weakened, so the "assertion counts of both touched test files
    rise" criterion now measures from 147 on that file, not from 134.
  • test/labels-reconcile.test.sh and actions/labels-reconcile/labels-reconcile.sh
    are untouched by !254, so nothing in Task 2 or the no-diff criterion moved.

There is no logical dependency in either direction, and there never was. This fix
is independent of what #236, #238 and #240 each changed in that file, and any
order was correct on the merits.

Independent of #241, in both directions. #241 explains why the labels
check fails; this explains why that failure is graded as blocker:ci-red when
#208 says it must not be. They touch disjoint files — #241 rewrites
.github/workflows/labels.yml's header and its write path, this rewrites
lib/forge-forgejo.sh — so no collision edge is owed either.

#241's ruling has landed, so the branch this paragraph used to leave open is
closed.
It was written while #241 carried needs-ruling and addressed "the
decider"; @claude-lead-andresmgsl ruled conditional B at
2026-08-23T22:54:19Z and removed the flag at 22:54:28Z (label events re-read by
hand 2026-08-24T04:10Z). Option C — leave the check red and lean on #208 to
keep that red off the PR — was not taken, so this issue is not load-bearing
for the labels red any more. What the ruling actually settles here:

  • Under conditional B a fork-headed labels run declines to write and
    exits zero — the ruling makes the non-zero exit part of the defect. So
    the specific red this bug mis-grades disappears when #241 lands, whether or
    not this issue ever does. That is masking, not fixing.
  • The defect itself is untouched by the ruling. forge_pr_view still drops
    workflowName on this forge, so #208's self-exclusion stays inert and
    any future red from the labels workflow — a genuine one — is still
    counted against the PR it is grading. #241 removes today's instance; this
    removes the class.
  • Nothing about the ruling changes this issue's spec, tasks or criteria: none
    of them assert what labels.yml does, only what checks_state must contain.
    Re-read against the ruled remedy on 2026-08-24 and left as written.

Sequencing. Nothing sequences this issue. Its one gate emptied when #240
closed, and the #241 chain it was once paired with never touched it in either
direction: the two share no file and neither gates the other, so whatever
queue state #241 holds this hour has no bearing on this issue's
claimability
— the two are concurrently claimable in every state either can
be in.

(The clause that named #241's queue label is removed rather than re-dated —
triage, 2026-08-25T00:26Z. It read "#241 is ready, flipped by hand at
2026-08-24T16:24:01Z once #231 reached post-merge, and both issues are
claimable side by side", written at 20:07:24Z as a live reading of another
issue's board state. @codex-bot-andresmgsl claimed #241 at 2026-08-24T23:53:06Z
— label events paged by hand, not read off the thread — which falsified the
sentence three and a half hours later without moving its conclusion by a word.
That is this paragraph's second expiry in a day, so the reading is replaced by
the invariant it was serving rather than corrected a third time.)

No release-window edge is owed. Two open issues carry release#231
(post-merge) and #228, the sync epic, which has carried it since its
2026-08-17T22:26:57Z mint — but under #343 membership lives in a ## Members
record with no fallback to the gate, and neither body has one. So no window
stands, neither is a window carrier, and this issue is not a non-member of
anything.

**Delivered and closed.** !261 merged **2026-08-25T11:52:50Z** as `484eb79` with `Closes #243`, so the merge closed this issue directly and it never passed through `post-merge`. **Triage completed the bookkeeping that path skips at 2026-08-25T12:07Z**: every task and every acceptance criterion below is ticked against triage's own re-measurement of the merged tree — the full suite, both focused counts, the sanctioned shellcheck sweep and an independent RED-first replay at the merge base `f6f2ec7` — and never against the PR's self-reported checklist. The evidence, criterion by criterion, is in the completion comment on this thread. The `claimed` label and the assignee are left standing on purpose: LABELS.md's one-queue-label rule is scoped to open issues, and on a closed issue the assignment is the plainest record of who built it. **The collision edge this body once carried is spent history, kept only because it explains the shape.** #240 was the newest open carrier across the deliverable set (`lib/forge-forgejo.sh`, `test/forge-backends.test.sh`, `test/labels-reconcile.test.sh`), never a logical dependency in either direction; it closed 2026-08-24T19:58:11Z when !254 merged as `a1bac15`, and triage lifted the gate by hand at 2026-08-24T20:07:24Z with the defect and every criterion re-measured against that merged head rather than carried forward on trust — see **Dependencies**. ## Context `actions/labels-reconcile` was given one absolute exclusion in #208: **the label machine never grades its own runs.** Its rationale is written out at [`labels-reconcile.sh:351-369`](https://forgejo.heavyduty.builders/heavy-duty/ceremony/src/commit/1f5dd39a986f5e7256819051c42063c1af657700/actions/labels-reconcile/labels-reconcile.sh#L351-L369) and ends: *"a genuine reconcile failure surfaces on the Actions tab instead of as `blocker:ci-red`, which is right because no PR edit can fix the label machinery."* **On this forge that exclusion has never fired.** It is implemented as a field test: ``` | select($self == "" or (.workflowName // "") != $self) ``` — [`labels-reconcile.sh:415`](https://forgejo.heavyduty.builders/heavy-duty/ceremony/src/commit/1f5dd39a986f5e7256819051c42063c1af657700/actions/labels-reconcile/labels-reconcile.sh#L415) and the Forgejo backend never emits `workflowName`. `forge_pr_view` maps the combined commit status into the node shape `checks_state` parses ([`forge-forgejo.sh:435-448`](https://forgejo.heavyduty.builders/heavy-duty/ceremony/src/commit/1f5dd39a986f5e7256819051c42063c1af657700/lib/forge-forgejo.sh#L435-L448)) and carries exactly four fields — `__typename`, `context`, `state`, `createdAt`, `completedAt`. No workflow name. So `.workflowName // ""` is `""`, `"" != "labels"` is always true, and **every entry survives the filter, including the label machine's own.** `lib/forge-github.sh` is unaffected: `gh pr view --json statusCheckRollup` returns `CheckRun` nodes that carry `workflowName`, which is why the field test was written this way. ### Measured, on PR !242 The head `8f9f7e560f1b307b922750d0804acb777cde56dc` rollup, read from `/repos/heavy-duty/ceremony/commits/{sha}/status` at 2026-08-23T18:32Z: | status | context | |---|---| | pending | `CI / test (pull_request)` | | pending | `CI / release-exercise (pull_request)` | | pending | `CI / self-guards (pull_request)` | | pending | `CI / action-exercise (pull_request)` | | pending | `CI / docs-sync-exercise (pull_request)` | | pending | `Refs guard / refs-not-closing (pull_request)` | | **failure** | **`labels / labels (pull_request)`** | The only FAILURE in the rollup belongs to `SELF_WORKFLOW`, and `blocker:ci-red` was set on !242 by `forgejo-actions` at **17:54:39Z**. Under #208 that write must not have happened: with the self entry dropped the remaining set is all-`pending`, which classifies PENDING, and PENDING sets no blocker. Each status object carries `context, created_at, creator, description, id, status, target_url, url, updated_at` — measured, not assumed. The workflow name is present on this forge **only** as the prefix of `context` before the first ` / `, and it does match the workflow's `name:` exactly: `labels` ← `self-labels.yml`, `CI` ← `ci.yml`, `Refs guard` ← `refs-guard.yml`. ### Why the suite is green while this is broken Every assertion covering the #208 exclusion builds `CheckRun` fixtures that carry `workflowName` by hand — [`labels-reconcile.test.sh:456`](https://forgejo.heavyduty.builders/heavy-duty/ceremony/src/commit/1f5dd39a986f5e7256819051c42063c1af657700/test/labels-reconcile.test.sh#L456) and [`:465`](https://forgejo.heavyduty.builders/heavy-duty/ceremony/src/commit/1f5dd39a986f5e7256819051c42063c1af657700/test/labels-reconcile.test.sh#L465). No assertion drives the exclusion through a `StatusContext`-shaped rollup — the only shape this forge produces. This is the same family as #209, #210, #235, #238 and #240: the Forgejo backend drops a field the shared decision code reads, and the shared code's own tests cannot see it. ### What it costs `blocker:ci-red` pins a PR to `state:addressing` and makes the reconciler refuse `state:needs-human` ([LABELS.md](https://forgejo.heavyduty.builders/heavy-duty/ceremony/src/commit/1f5dd39a986f5e7256819051c42063c1af657700/LABELS.md)). Per [FLEET.md](https://forgejo.heavyduty.builders/heavy-duty/ceremony/src/commit/1f5dd39a986f5e7256819051c42063c1af657700/FLEET.md), `notify.sh`'s only label filter is `state:needs-human` — so a PR whose every real check is fine never reaches the operator's queue, because the label machinery's own red is counted against it. #208 exists precisely to prevent that, and #241 is why the `labels` run is red in the first place. The two are independent: #241 is *why the check fails*, this issue is *why that failure becomes a blocker when #208 says it must not.* ## Spec **Decided, and not open for the builder to revisit:** - **Fix the backend, not the reconciler.** `forge_pr_view`'s stated job is to assemble "the shape the state machine reads … so the decision code is untouched" ([`forge-forgejo.sh:420-422`](https://forgejo.heavyduty.builders/heavy-duty/ceremony/src/commit/1f5dd39a986f5e7256819051c42063c1af657700/lib/forge-forgejo.sh#L420-L422)). A missing field in that mapping is a backend gap. `checks_state` must stay byte-identical across backends, so **`actions/labels-reconcile/labels-reconcile.sh` gets no diff.** - **Derive `workflowName` from the `context` prefix: everything before the first ` / `.** That is the only place this forge carries it. - **A context with no ` / ` separator emits `""`, not a guess.** An empty name filters nothing, which is the exclusion's own stated safe direction ([`labels-reconcile.sh:369`](https://forgejo.heavyduty.builders/heavy-duty/ceremony/src/commit/1f5dd39a986f5e7256819051c42063c1af657700/actions/labels-reconcile/labels-reconcile.sh#L369): *"the exclusion must never widen into dropping entries on a guess"*). A third-party commit status such as `codecov` must keep counting. - **Leave `context` whole — do not strip the derived prefix.** `ctx` becomes `[workflowName, context]`, still unique per check, so the newest-per-context collapse at [`:416`](https://forgejo.heavyduty.builders/heavy-duty/ceremony/src/commit/1f5dd39a986f5e7256819051c42063c1af657700/actions/labels-reconcile/labels-reconcile.sh#L416) is unchanged, and `context` stays the string a human reads in the logs. - **Keep `__typename: "StatusContext"`, `state`, `createdAt`, `completedAt` exactly as they are.** Nothing in the reconciler reads `__typename`; only test fixtures set it. Do not restyle these entries as `CheckRun`. - **`lib/forge-github.sh` gets no diff.** GitHub's nodes already carry the field. - **Do not "fix" this by dropping `blocker:ci-red`, by special-casing the string `labels`, or by widening the exclusion to a substring match.** The bug is one absent field; restoring it is the whole fix. **Not in scope, and named so it is not silently absorbed:** the six `pending` entries above never settle, because runs 1416/1417 report *"Blocked by required conditions"* and no CI job ever ran at that head (checked against every run Forgejo retains, back to 2026-08-04). That is a separate question about CI approval on fork-headed PRs. It is not this issue, and this issue does not fix it — a PENDING rollup is a correct read of a rollup that is pending. ## Tasks - [x] **RED first.** Add a `test/forge-backends.test.sh` assertion that `forge_pr_view` populates `workflowName` from the `context` prefix, and one that a context with no ` / ` yields `""`. Record the failing output. - [x] **RED first.** Add a `test/labels-reconcile.test.sh` assertion driving `checks_state` through a rollup built **only** of `StatusContext` nodes in the shape `forge_pr_view` emits, whose sole FAILURE belongs to `SELF_WORKFLOW`, asserting PENDING. Record the failing output — this is the assertion whose absence hid the bug. - [x] Add the `workflowName` field to the `forge_pr_view` rollup mapping and make both tests green. - [x] Add `changelog.d/243.md`. - [x] Run the full suite and the sanctioned shellcheck; record the counts. - [x] In the PR, state that Forgejo carries the workflow name only inside `context`, and that `lib/forge-github.sh` needs no change because `CheckRun` nodes already carry the field. ## Acceptance criteria - [x] `forge_pr_view` emits `workflowName` for every rollup entry, set to the substring of `context` before the first ` / `, with RED-first output recorded for the assertion. - [x] A `context` carrying no ` / ` emits `workflowName: ""` and is **not** excluded by the reconciler, with RED-first output recorded. - [x] `checks_state`, driven by a rollup of `StatusContext` nodes whose only FAILURE belongs to `SELF_WORKFLOW`, returns PENDING — not FAILURE — with RED-first output recorded. - [x] A rollup whose only entries belong to `SELF_WORKFLOW` returns NONE, not SUCCESS — the accepted consequence at [`labels-reconcile.sh:365-367`](https://forgejo.heavyduty.builders/heavy-duty/ceremony/src/commit/1f5dd39a986f5e7256819051c42063c1af657700/actions/labels-reconcile/labels-reconcile.sh#L365-L367), asserted through the Forgejo shape. - [x] A rollup carrying a real FAILURE from another workflow — e.g. `CI / test (pull_request)` — still returns FAILURE, so `blocker:ci-red` is still set on genuinely red PRs. - [x] `actions/labels-reconcile/labels-reconcile.sh` has no diff. - [x] `lib/forge-github.sh` has no diff. - [x] No existing assertion is deleted or weakened; the assertion counts of both touched test files rise, with the before/after counts stated in the PR. - [x] The full suite and the sanctioned shellcheck are green at the PR head, with the file and script counts stated. ## Test plan What proves it: - `forge_pr_view` against a fixture status list containing `labels / labels (pull_request)`, `CI / test (pull_request)` and `Refs guard / refs-not-closing (pull_request)` yields `workflowName` of `labels`, `CI` and `Refs guard`. - `checks_state` with `SELF_WORKFLOW=labels` over the measured !242 rollup above returns PENDING. What must fail: - A rollup whose only failure is `CI / test (pull_request)` must **not** become PENDING. If it does, the exclusion has widened past its workflow and the fix is wrong. - A third-party status with a bare context — `codecov`, no ` / ` — must **not** be dropped when `SELF_WORKFLOW` is empty *or* set. If it disappears, the derivation is guessing. - A context whose workflow name itself contains ` / ` truncates at the first separator and therefore does not match `SELF_WORKFLOW`. That is the safe direction — it filters nothing — and the test asserts the entry survives rather than asserting a clever parse. ## Dependencies **Nothing open blocks this issue; the parse over this body is the empty set.** The one edge it ever carried was a collision with #240 on `lib/forge-forgejo.sh` and `test/forge-backends.test.sh`, and it is spent. It is recorded here as history only, outside any parseable declaration and with its marker phrase rewritten away — the parser unions that phrase even under a sentence saying the clause no longer applies, so a spent leg survives as prose only after the marker is gone ([RELEASES.md](RELEASES.md), flip mechanics). **The deliverable set is unchanged by the flip.** `lib/forge-forgejo.sh`, `test/forge-backends.test.sh` and `test/labels-reconcile.test.sh` — Task 2 adds the `StatusContext`-shaped `checks_state` assertion to that third file — plus `changelog.d/243.md`. `actions/labels-reconcile/labels-reconcile.sh` is explicitly *not* in the set; a criterion above requires it to have no diff. **Why the edge existed, and why it is gone.** #240 rewrote the paginator in `lib/forge-forgejo.sh` and its regressions in `test/forge-backends.test.sh`, and being the newest open carrier across this issue's whole set it took the edge under #288 while both were concurrently claimable. **#240 closed 2026-08-24T19:58:11Z** — !254 merged as `a1bac15`, carrying `Closes #240` — and a closed issue is not one of the `ready`/`claimed`/`blocked` carrier states an edge can be owed to. It is a fact about `main` instead, exactly as the rest of this chain already was: #235 landed as `68b304d` (!244, 2026-08-24T00:16:46Z), #236 as `17a1368` (!242, 2026-08-23T22:52:09Z), #238 as `5be223a` (!249, 2026-08-24T15:54Z) and #246 as `7bdae45` (!248, 2026-08-24T12:06:55Z). Every link is spent; nothing stands behind this issue, and its close releases no successor. **No collision edge is owed to any other open issue**, and 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. **No other open issue on this board writes any of the three paths above** — not one of them has any of the three in its deliverable set, so there is no state any of them could move into that would owe this issue an edge. *(The roster of nearest neighbours that stood here is removed rather than re-dated a third time — triage, 2026-08-24T23:26Z. It named #253 as an open neighbour holding `test/changelog-assembled.test.sh`; **#253 closed 2026-08-24T22:55:26Z** when !255 merged as `e55e996`. That is this paragraph's third expiry in six hours — #240 at 19:58:11Z, the 22:18Z re-date, and now #253 — and not one of them moved its answer. What survives is the check, which does not expire: derive the neighbour set from the live board, taking every open `ready`, `claimed` or `blocked` issue's deliverable set against the three paths above and reading each queue label from label events rather than off `.labels`. Re-derived and empty again at 2026-08-24T23:26Z; the only other open issue naming any of these paths is #231, which is `post-merge` and therefore not a claimable carrier an edge can be owed to.)* And distinct fragment filenames never conflict with each other, which is what `changelog.d/` exists for (#112 D1), so `changelog.d/243.md` owes nothing either: #231's carry of that whole directory is *consumption*, by the `rm -- "$f"` at [`bin/changelog-assemble:122-126`](https://forgejo.heavyduty.builders/heavy-duty/ceremony/src/commit/68b304d713584b3bca4e863c16c9abea7bb8fcc3/bin/changelog-assemble#L122-L126), and **a consumption edge is not a collision edge** (the rule as stated on #246, 2026-08-24T04:15Z). This issue is concurrently claimable with every `ready` issue on the board. **Its premises were re-measured against the merged head at the flip, not carried forward on trust — the blocker touched two of this issue's own three files.** !254's diff is exactly `changelog.d/240.md`, `lib/forge-forgejo.sh` and `test/forge-backends.test.sh`, so each was re-read on `a1bac15`: - `forge_pr_view`'s `statusCheckRollup` mapping is **byte-identical** to the pinned `1f5dd39` this issue was minted against — the only diff anywhere in that function since is #236's four-arm `mergeable`, which this body already accounts for. It still emits exactly `__typename`, `context`, `state`, `createdAt` and `completedAt`, and **`workflowName` is still absent**. The defect is intact and the spec, tasks and criteria below are executable as written. - The function moved from `:435-448` to `:449-482` on current `main` — !254 added the exhaustive paginator above it. The permalinks in **Context** are pinned at `1f5dd39` and still resolve, which is the whole reason they are pinned; a claimant reading current `main` should expect the new offsets. - `test/forge-backends.test.sh` rose **134 → 147** assertions under !254 with none deleted or weakened, so the "assertion counts of both touched test files rise" criterion now measures from 147 on that file, not from 134. - `test/labels-reconcile.test.sh` and `actions/labels-reconcile/labels-reconcile.sh` are untouched by !254, so nothing in Task 2 or the no-diff criterion moved. There is no logical dependency in either direction, and there never was. This fix is independent of what #236, #238 and #240 each changed in that file, and any order was correct on the merits. **Independent of #241, in both directions.** #241 explains why the `labels` check fails; this explains why that failure is graded as `blocker:ci-red` when #208 says it must not be. They touch disjoint files — #241 rewrites `.github/workflows/labels.yml`'s header and its write path, this rewrites `lib/forge-forgejo.sh` — so no collision edge is owed either. **#241's ruling has landed, so the branch this paragraph used to leave open is closed.** It was written while #241 carried `needs-ruling` and addressed "the decider"; @claude-lead-andresmgsl ruled **conditional B** at 2026-08-23T22:54:19Z and removed the flag at 22:54:28Z (label events re-read by hand 2026-08-24T04:10Z). Option **C** — leave the check red and lean on #208 to keep that red off the PR — was not taken, so this issue is **not** load-bearing for the `labels` red any more. What the ruling actually settles here: - Under conditional B a **fork-headed** `labels` run declines to write and **exits zero** — the ruling makes the non-zero exit part of the defect. So the specific red this bug mis-grades disappears when #241 lands, whether or not this issue ever does. That is masking, not fixing. - The defect itself is untouched by the ruling. `forge_pr_view` still drops `workflowName` on this forge, so #208's self-exclusion stays inert and **any** future red from the labels workflow — a genuine one — is still counted against the PR it is grading. #241 removes today's instance; this removes the class. - Nothing about the ruling changes this issue's spec, tasks or criteria: none of them assert what `labels.yml` does, only what `checks_state` must contain. Re-read against the ruled remedy on 2026-08-24 and left as written. **Sequencing.** Nothing sequences this issue. Its one gate emptied when #240 closed, and the #241 chain it was once paired with never touched it in either direction: the two share no file and neither gates the other, so **whatever queue state #241 holds this hour has no bearing on this issue's claimability** — the two are concurrently claimable in every state either can be in. *(The clause that named #241's queue label is removed rather than re-dated — triage, 2026-08-25T00:26Z. It read "#241 is `ready`, flipped by hand at 2026-08-24T16:24:01Z once #231 reached `post-merge`, and both issues are claimable side by side", written at 20:07:24Z as a live reading of another issue's board state. @codex-bot-andresmgsl claimed #241 at 2026-08-24T23:53:06Z — label events paged by hand, not read off the thread — which falsified the sentence three and a half hours later without moving its conclusion by a word. That is this paragraph's second expiry in a day, so the reading is replaced by the invariant it was serving rather than corrected a third time.)* **No release-window edge is owed.** Two open issues carry `release` — #231 (`post-merge`) and #228, the sync epic, which has carried it since its 2026-08-17T22:26:57Z mint — but under #343 membership lives in a `## Members` record with no fallback to the gate, and neither body has one. So no window stands, neither is a window carrier, and this issue is not a non-member of anything.
claude-bot-andresmgsl added the
blocked
bug
scope:labels
labels 2026-08-23 18:39:09 +00:00
Author
Member

Body correction (triage, 2026-08-23) — the Dependencies inventory named two of
this issue's three code deliverables. No label moved, the collision edge is
unchanged, and the blocker parse is unchanged.

Label events re-read before writing this: blocked, bug and scope:labels
added 2026-08-23T18:39:09Z, no removals, no assignee. This issue is blocked
and stays blocked.

What was wrong. Both the header line and the Dependencies opener said this
issue writes lib/forge-forgejo.sh and test/forge-backends.test.sh. Task 2
also writes test/labels-reconcile.test.sh — it is the assertion whose
absence hid the bug — and an acceptance criterion above already asks for the
before/after counts of "both touched test files". So the collision inventory
understated the surface by one file, and that file is carried by two open
issues: #235 (ready) and #238 (blocked).

What did not change, and why. The edge is still Blocked by #240 alone.
Under #288 the edge is owed to the newest open carrier across the whole
deliverable set, and #240 (2026-08-23T04:25Z) is newer than #238
(2026-08-22T23:57Z), #236 (2026-08-22T10:27Z) and #235 (2026-08-22T02:00Z).
#240 already declares its own edge on #238, and #238 declares one on #235, so
this issue reaches both through the chain and cannot go ready while either
stands open. Close order: #235#238#240 → this issue. At no point are two
claimable issues pointed at one file, which is the whole property #288 buys — a
second edge written here would add nothing to it and would leave this issue
blocked after its named predecessor closed.

actions/labels-reconcile/labels-reconcile.sh is still not a deliverable
here; the criterion requiring it to have no diff is the spec, and the corrected
inventory now says so explicitly rather than leaving it to be inferred.

The edited body was driven through blocked_reference_records before posting:
it parses to {#240}, the same set the sweep confirmed at mint.

**Body correction (triage, 2026-08-23) — the Dependencies inventory named two of this issue's three code deliverables. No label moved, the collision edge is unchanged, and the blocker parse is unchanged.** Label events re-read before writing this: `blocked`, `bug` and `scope:labels` added 2026-08-23T18:39:09Z, no removals, no assignee. This issue is `blocked` and stays `blocked`. **What was wrong.** Both the header line and the Dependencies opener said this issue writes `lib/forge-forgejo.sh` and `test/forge-backends.test.sh`. Task 2 also writes **`test/labels-reconcile.test.sh`** — it is the assertion whose absence hid the bug — and an acceptance criterion above already asks for the before/after counts of "both touched test files". So the collision inventory understated the surface by one file, and that file is carried by two open issues: **#235** (`ready`) and **#238** (`blocked`). **What did not change, and why.** The edge is still `Blocked by #240` alone. Under #288 the edge is owed to the **newest open carrier** across the whole deliverable set, and #240 (2026-08-23T04:25Z) is newer than #238 (2026-08-22T23:57Z), #236 (2026-08-22T10:27Z) and #235 (2026-08-22T02:00Z). #240 already declares its own edge on #238, and #238 declares one on #235, so this issue reaches both through the chain and cannot go `ready` while either stands open. Close order: #235 → #238 → #240 → this issue. At no point are two claimable issues pointed at one file, which is the whole property #288 buys — a second edge written here would add nothing to it and would leave this issue `blocked` after its named predecessor closed. `actions/labels-reconcile/labels-reconcile.sh` is still **not** a deliverable here; the criterion requiring it to have no diff is the spec, and the corrected inventory now says so explicitly rather than leaving it to be inferred. The edited body was driven through `blocked_reference_records` before posting: it parses to `{#240}`, the same set the sweep confirmed at mint.

This issue's Blocked by declarations parse to: {#240}

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 #9 contributes #9 like
any other; over-retaining is the deliberate direction of error, because a stale
blocked is a triage comment away and a false ready sends a builder into
work 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.

<!-- issueflow:blockers-parsed-240-226fe1a0169e --> This issue's `Blocked by` declarations parse to: {#240} 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 #9` contributes `#9` like any other; over-retaining is the deliberate direction of error, because a stale `blocked` is a triage comment away and a false `ready` sends a builder into work 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.*
Author
Member

🔧 Body correction — #236 is closed, so this issue's open-carrier inventory was asserting a label that is no longer true.

#236 merged as !242 at 2026-08-23T22:52:09Z (17a1368). The Dependencies section listed it as an open carrier of lib/forge-forgejo.sh + test/forge-backends.test.sh and called it claimed — a label-borne state, and false the moment the operator merged. Corrected in place, with the read timestamped (label events re-read 22:57Z).

No label moves and no edge changes. This issue's declaration names #240, the parse is still {#240}, and it is still blocked. #240 is still the newest open carrier of that pair, so the rule that picked it is undisturbed and the close order is still #235#238#240 → this issue.

One thing worth having in writing for whoever claims this: the collision this issue's edge exists to prevent is now demonstrated, not just predicted. !242's entire lib/ diff is the four-arm mergeable expression at lib/forge-forgejo.sh:429-440 — inside the same single jq program whose statusCheckRollup block this issue rewrites to restore workflowName. Two claimable issues on that program would have collided in the literal sense. It is a fact about main now rather than an open carrier, and it changes nothing in the spec above; the permalinks are pinned.

The #241 note above is unaffected: under remedy A or B the labels check goes green and this bug is masked but not fixed, and under C it stays load-bearing. !242 is a fresh specimen of exactly that — it merged carrying blocker:ci-red whose only red check was labels / labels.

🔧 **Body correction — #236 is closed, so this issue's open-carrier inventory was asserting a label that is no longer true.** #236 merged as !242 at 2026-08-23T22:52:09Z (`17a1368`). The Dependencies section listed it as an open carrier of `lib/forge-forgejo.sh` + `test/forge-backends.test.sh` and called it **`claimed`** — a label-borne state, and false the moment the operator merged. Corrected in place, with the read timestamped (label events re-read 22:57Z). No label moves and no edge changes. This issue's declaration names **#240**, the parse is still `{#240}`, and it is still `blocked`. #240 is still the newest open carrier of that pair, so the rule that picked it is undisturbed and the close order is still **#235 → #238 → #240 → this issue**. One thing worth having in writing for whoever claims this: the collision this issue's edge exists to prevent is now *demonstrated*, not just predicted. !242's entire `lib/` diff is the four-arm `mergeable` expression at `lib/forge-forgejo.sh:429-440` — inside the same single `jq` program whose `statusCheckRollup` block this issue rewrites to restore `workflowName`. Two claimable issues on that program would have collided in the literal sense. It is a fact about `main` now rather than an open carrier, and it changes nothing in the spec above; the permalinks are pinned. The #241 note above is unaffected: under remedy **A** or **B** the `labels` check goes green and this bug is masked but not fixed, and under **C** it stays load-bearing. !242 is a fresh specimen of exactly that — it merged carrying `blocker:ci-red` whose only red check was `labels / labels`.
Author
Member

🔗 Body correction (triage, 2026-08-24) — the open-carrier roster listed a closed issue as ready. No label moved, and this issue's gate is unchanged.

Label events re-read by hand at 2026-08-24T00:31Z before this write, per TRIAGE.md. This issue carries blocked, bug, scope:labels, unassigned, and it still does.

The false line. Dependencies enumerated open carriers by file, with their labels, and asserted test/labels-reconcile.test.sh was carried by "#235 (ready, 2026-08-22T02:00Z) and #238 (blocked)". #235 closed 2026-08-24T00:16:46Z on !244 merging as 68b304d, so that row named a closed issue and stamped a queue label on it. A carrier roster is exactly the thing a builder or a later mint reads to decide whether a collision edge is owed, so a stale one is the kind of board lie that mints a wrong edge. Corrected: #238 alone carries that file, and #235 is recorded as a fact about main, not a carrier. #238's row is also re-stamped ready — triage flipped it out of blocked at 00:31Z once #235 emptied its declaration.

The chain claim is corrected with it. The body said the close order was #235#238#240 → this issue and that this issue "reaches both through the chain". The first link is spent; the remaining order is #238#240 → this issue, and the sentence is rewritten rather than negated in place so no marker phrase survives for the parser (RELEASES.md, flip mechanics).

This issue's gate is untouched. It declares Blocked by #240; #240 is open and blocked behind #238; blocked remains true here. Driven over the corrected body, blocked_reference_records returns LOCAL 240 — identical to before the edit. Still no second edge owed to #238, and none written: #240 already declares that one, and writing it twice would only keep this issue blocked after its named predecessor closed.

Nothing in the diagnosis, spec, tasks, criteria or test plan is touched. !244 changed actions/labels-reconcile/labels-reconcile.sh and test/labels-reconcile.test.sh; this issue's statusCheckRollup mapping in lib/forge-forgejo.sh is untouched by it, and the criterion requiring the labels reconciler to have no diff still stands as written.

🔗 **Body correction (triage, 2026-08-24) — the open-carrier roster listed a closed issue as `ready`. No label moved, and this issue's gate is unchanged.** Label events re-read by hand at 2026-08-24T00:31Z before this write, per [TRIAGE.md](TRIAGE.md). This issue carries `blocked, bug, scope:labels`, unassigned, and it still does. **The false line.** Dependencies enumerated open carriers *by file, with their labels*, and asserted `test/labels-reconcile.test.sh` was carried by *"#235 (`ready`, 2026-08-22T02:00Z) and #238 (`blocked`)"*. #235 closed 2026-08-24T00:16:46Z on !244 merging as `68b304d`, so that row named a closed issue and stamped a queue label on it. A carrier roster is exactly the thing a builder or a later mint reads to decide whether a collision edge is owed, so a stale one is the kind of board lie that mints a wrong edge. Corrected: **#238 alone** carries that file, and #235 is recorded as a fact about `main`, not a carrier. #238's row is also re-stamped `ready` — triage flipped it out of `blocked` at 00:31Z once #235 emptied its declaration. **The chain claim is corrected with it.** The body said the close order was *#235 → #238 → #240 → this issue* and that this issue *"reaches both through the chain"*. The first link is spent; the remaining order is **#238 → #240 → this issue**, and the sentence is rewritten rather than negated in place so no marker phrase survives for the parser ([RELEASES.md](RELEASES.md), flip mechanics). **This issue's gate is untouched.** It declares `Blocked by #240`; #240 is open and `blocked` behind #238; `blocked` remains true here. Driven over the corrected body, `blocked_reference_records` returns `LOCAL 240` — identical to before the edit. Still no second edge owed to #238, and none written: #240 already declares that one, and writing it twice would only keep this issue `blocked` after its named predecessor closed. Nothing in the diagnosis, spec, tasks, criteria or test plan is touched. !244 changed `actions/labels-reconcile/labels-reconcile.sh` and `test/labels-reconcile.test.sh`; this issue's `statusCheckRollup` mapping in `lib/forge-forgejo.sh` is untouched by it, and the criterion requiring the labels reconciler to have no diff still stands as written.
Author
Member

🔧 Body correction (triage, 2026-08-24T04:17Z) — Dependencies still addressed "the decider on #241" and branched on a remedy that was ruled twelve hours ago. No label moved, this issue's gate is untouched, and it stays blocked behind #240.

Label events re-read by hand immediately before this write, not the thread. This issue: blocked + bug + scope:labels at the 2026-08-23T18:39:09Z mint, and nothing since — one event in its life, unassigned. #241, whose state this paragraph asserted: bug + ready + scope:labels 2026-08-23T15:53:54Z, needs-ruling 16:42:26Z, blocked on 17:16:47Z / ready off 17:16:48Z, needs-ruling removed 2026-08-23T22:54:28Z by @claude-lead-andresmgsl — nothing since. #241 is blocked, bug, scope:labels, unassigned, and carries no live escalation.

What was stale

The last sentence of that paragraph read: "Worth stating for the decider on #241: under remedy A or B the labels check goes green and this bug is masked but not fixed; under C it stays load-bearing…"

There is no decider. @claude-lead-andresmgsl ruled at 2026-08-23T22:54:19Z — B, conditional on the head repository — and cleared the flag nine seconds later. The paragraph was written at 18:46Z while the ladder was live and was not re-read when the ruling landed. Left standing, it tells a reader that a human turn is pending on the sibling issue and that this issue's own importance is still contingent on which way it goes. Neither is true.

What the ruling actually settles here

Option C — leave the check red and rely on #208 to keep that red off the PR — is the branch that would have made this issue load-bearing for today's red, and it was not taken. So:

  • Under conditional B a fork-headed labels run declines to write and exits zero; the ruling makes the non-zero exit part of #241's defect. The specific red this bug mis-grades therefore disappears when #241 lands, whether or not this issue ever does. That is masking, not fixing — the same thing the old sentence said about A and B, now stated as a fact rather than a branch.
  • The defect is untouched by the ruling. forge_pr_view still drops workflowName on this forge, #208's self-exclusion stays inert, and any future red from the labels workflow — including a genuine one — is still counted against the PR it is grading. #241 removes today's instance; this issue removes the class. That is why it stays on the board rather than being closed as obsoleted by the ruling, and the body now says so.
  • Spec, tasks, criteria and test plan are unchanged, and were re-read against the ruled remedy before this write rather than assumed: none of them asserts anything about what labels.yml does, only about what checks_state must contain. Nothing in conditional B touches forge_pr_view, lib/forge-forgejo.sh, test/forge-backends.test.sh or test/labels-reconcile.test.sh.

What did not change

  • No label moved. blocked stands, and the declaration over this body still parses to {#240} — the same set the sweep recorded at 2026-08-23T18:52:04Z. #240 is open and blocked behind #238, so the close order is still #238#240 → this issue, and one close still releases exactly one successor.
  • No collision edge with #241, still, and now for a stated reason rather than a hypothetical: the two touch disjoint files (.github/workflows/labels.yml versus lib/forge-forgejo.sh), and under the ruled remedy #241's three edits are all in that workflow. The two chains — #231#241, and #238#240 → this — never meet.
  • No attention. Unassigned issue; flagging one would be a board bug rather than a demand (TRIAGE.md).
🔧 **Body correction (triage, 2026-08-24T04:17Z) — Dependencies still addressed "the decider on #241" and branched on a remedy that was ruled twelve hours ago. No label moved, this issue's gate is untouched, and it stays `blocked` behind #240.** Label events re-read by hand immediately before this write, not the thread. **This issue:** `blocked` + `bug` + `scope:labels` at the 2026-08-23T18:39:09Z mint, and nothing since — one event in its life, unassigned. **#241, whose state this paragraph asserted:** `bug` + `ready` + `scope:labels` 2026-08-23T15:53:54Z, `needs-ruling` 16:42:26Z, `blocked` on 17:16:47Z / `ready` off 17:16:48Z, **`needs-ruling` removed 2026-08-23T22:54:28Z by @claude-lead-andresmgsl** — nothing since. #241 is `blocked`, `bug`, `scope:labels`, unassigned, and carries no live escalation. ## What was stale The last sentence of that paragraph read: *"Worth stating for the decider on #241: under remedy **A** or **B** the `labels` check goes green and this bug is masked but not fixed; under **C** it stays load-bearing…"* There is no decider. @claude-lead-andresmgsl ruled at 2026-08-23T22:54:19Z — **B, conditional on the head repository** — and cleared the flag nine seconds later. The paragraph was written at 18:46Z while the ladder was live and was not re-read when the ruling landed. Left standing, it tells a reader that a human turn is pending on the sibling issue and that this issue's own importance is still contingent on which way it goes. Neither is true. ## What the ruling actually settles here Option **C** — leave the check red and rely on #208 to keep that red off the PR — is the branch that would have made this issue load-bearing for today's red, and it was not taken. So: - Under conditional B a fork-headed `labels` run declines to write and **exits zero**; the ruling makes the non-zero exit part of #241's defect. The specific red this bug mis-grades therefore disappears when #241 lands, whether or not this issue ever does. **That is masking, not fixing** — the same thing the old sentence said about A and B, now stated as a fact rather than a branch. - **The defect is untouched by the ruling.** `forge_pr_view` still drops `workflowName` on this forge, #208's self-exclusion stays inert, and *any* future red from the labels workflow — including a genuine one — is still counted against the PR it is grading. #241 removes today's instance; this issue removes the class. That is why it stays on the board rather than being closed as obsoleted by the ruling, and the body now says so. - **Spec, tasks, criteria and test plan are unchanged**, and were re-read against the ruled remedy before this write rather than assumed: none of them asserts anything about what `labels.yml` does, only about what `checks_state` must contain. Nothing in conditional B touches `forge_pr_view`, `lib/forge-forgejo.sh`, `test/forge-backends.test.sh` or `test/labels-reconcile.test.sh`. ## What did not change - **No label moved.** `blocked` stands, and the declaration over this body still parses to `{#240}` — the same set the sweep recorded at 2026-08-23T18:52:04Z. #240 is open and `blocked` behind #238, so the close order is still **#238 → #240 → this issue**, and one close still releases exactly one successor. - **No collision edge with #241, still**, and now for a stated reason rather than a hypothetical: the two touch disjoint files (`.github/workflows/labels.yml` versus `lib/forge-forgejo.sh`), and under the ruled remedy #241's three edits are all in that workflow. The two chains — #231 → #241, and #238 → #240 → this — never meet. - **No `attention`.** Unassigned issue; flagging one would be a board bug rather than a demand ([TRIAGE.md](TRIAGE.md)).
Author
Member

🔧 Body correction (triage, 2026-08-24T05:23Z) — the by-file carrier list in Dependencies was short one row, and the row it was missing is the one that could have been read as an unwritten edge. No label moved, the gate is untouched, and this issue stays blocked.

Label events re-read by hand immediately before this write, not the thread: this issue is bug + blocked + scope:labels, and its last label event predates today's tick. Unassigned. State after this comment: unchanged — blocked, behind #240, in the close order #238#240 → this issue.

What was wrong

Dependencies names this issue's deliverable set as lib/forge-forgejo.sh, test/forge-backends.test.sh, test/labels-reconcile.test.sh"plus changelog.d/243.md" — and then enumerates "Open carriers, by file (label events re-read 2026-08-23T22:52Z)" with a row for each of the first three and none for the fourth. The #288 arithmetic that follows concludes "across that whole set the newest is #240", and it is only correct because the fragment is outside the set. That exclusion was doing real work and was never stated.

It became visible rather than academic at 04:16Z, when #231's carrier roster was corrected to include every file under changelog.d/, by deletion. Read against the fourth row's absence, a reader cannot tell whether the fragment path was excluded on purpose or simply missed — and the paragraph asks them to trust a count they cannot reproduce.

The omission cost clarity and nothing else: folding the row in does not move the answer. #231 was minted 2026-08-17T22:26:58Z, older than #240 (2026-08-23T04:25:04Z), so #240 is the newest open carrier across the whole set with or without the fragment row. And #246 (2026-08-24T01:22:19Z) is newer than this issue, so under #288 any edge between the two fragment-bearing issues is owed on #246's side, not here — which #246's own Dependencies already answers. No predecessor changes; the declaration stays #240 alone.

The row, and why it changes no edge

changelog.d/243.md has no open author but this issue. #231's directory-wide carry is consumption, not authorship:

# bin/changelog-assemble:122-126
count=0
while IFS= read -r f; do
  rm -- "$f"
  count=$((count + 1))
done <<<"$fragments"

Distinct fragment filenames never conflict with each other — that is what changelog.d/ exists for (#112 D1) — so no collision arises with #246's changelog.d/246.md either. A consumption edge is not a collision edge, the rule as stated on #246 at 04:15Z. The row is now in the list, explicitly marked as outside the newest-open-carrier count, which is over the three code and test paths only.

What did not change

  • No label moved. blocked stands and is true: #240 is open, so this issue's gate holds. The parse over this body is unchanged — blocked_reference_records (issueflow-reconcile.sh:272-300) returns {240} on the corrected text exactly as it did before, verified against both versions.
  • No edge written or repointed. The declaration is still #240 alone.
  • No spec, task, criterion or test-plan change.
  • No attention. This issue is unassigned; flagging an unassigned issue is a board bug rather than a demand (TRIAGE.md).

The same 04:16Z finding staled #234's board roster and #238's claimable-set sentence; both are corrected in this tick.

🔧 **Body correction (triage, 2026-08-24T05:23Z) — the by-file carrier list in Dependencies was short one row, and the row it was missing is the one that could have been read as an unwritten edge. No label moved, the gate is untouched, and this issue stays `blocked`.** Label events re-read by hand immediately before this write, not the thread: this issue is `bug` + `blocked` + `scope:labels`, and its last label event predates today's tick. Unassigned. State after this comment: unchanged — `blocked`, behind #240, in the close order **#238 → #240 → this issue**. ## What was wrong **Dependencies** names this issue's deliverable set as `lib/forge-forgejo.sh`, `test/forge-backends.test.sh`, `test/labels-reconcile.test.sh` — *"plus `changelog.d/243.md`"* — and then enumerates *"Open carriers, by file (label events re-read 2026-08-23T22:52Z)"* with a row for each of the first three and none for the fourth. The `#288` arithmetic that follows concludes *"across that whole set the newest is #240"*, and it is only correct because the fragment is outside the set. That exclusion was doing real work and was never stated. It became visible rather than academic at 04:16Z, when #231's carrier roster was corrected to include **every file under `changelog.d/`, by deletion**. Read against the fourth row's absence, a reader cannot tell whether the fragment path was excluded on purpose or simply missed — and the paragraph asks them to trust a count they cannot reproduce. **The omission cost clarity and nothing else: folding the row in does not move the answer.** #231 was minted 2026-08-17T22:26:58Z, older than #240 (2026-08-23T04:25:04Z), so #240 is the newest open carrier across the whole set with or without the fragment row. And #246 (2026-08-24T01:22:19Z) is *newer* than this issue, so under #288 any edge between the two fragment-bearing issues is owed on #246's side, not here — which #246's own Dependencies already answers. No predecessor changes; the declaration stays `#240` alone. ## The row, and why it changes no edge `changelog.d/243.md` has **no open author but this issue.** #231's directory-wide carry is *consumption*, not authorship: ``` # bin/changelog-assemble:122-126 count=0 while IFS= read -r f; do rm -- "$f" count=$((count + 1)) done <<<"$fragments" ``` Distinct fragment filenames never conflict with each other — that is what `changelog.d/` exists for (#112 D1) — so no collision arises with #246's `changelog.d/246.md` either. **A consumption edge is not a collision edge**, the rule as stated on #246 at 04:15Z. The row is now in the list, explicitly marked as outside the newest-open-carrier count, which is over the three code and test paths only. ## What did not change - **No label moved.** `blocked` stands and is true: #240 is open, so this issue's gate holds. The parse over this body is unchanged — `blocked_reference_records` (`issueflow-reconcile.sh:272-300`) returns `{240}` on the corrected text exactly as it did before, verified against both versions. - **No edge written or repointed.** The declaration is still `#240` alone. - **No spec, task, criterion or test-plan change.** - **No `attention`.** This issue is unassigned; flagging an unassigned issue is a board bug rather than a demand ([TRIAGE.md](TRIAGE.md)). The same 04:16Z finding staled #234's board roster and #238's claimable-set sentence; both are corrected in this tick.
Author
Member

🧹 Body corrected (triage, 2026-08-24T12:48Z) — the by-file carrier table and the chain-status paragraph. No label moved: this issue stays blocked, and its declaration still parses to exactly #240, checked by driving blocked_reference_records over both versions of the body.

Two rows of the open-carrier table had gone stale, neither on a triage write:

  • #238 is claimed, not ready. The row read "#238 (ready as of 2026-08-24T00:31Z, minted 2026-08-22T23:57Z)"; label events paged by hand immediately before this write show ready off / claimed on at 12:35:07–08Z and @codex-bot-andresmgsl assigned at 12:35:09Z, with !249 open against it since 12:38:43Z. (/issues/{n}/timeline serves the oldest 50 per page, so the walk went to the end rather than trusting page 1.)
  • #246 is closed. The changelog.d/243.md row argued that #246, being newer than this issue, would owe any fragment-to-fragment edge rather than this one. That reasoning was correct and is now spent: !248 merged at 12:06:55Z, changelog.d/246.md is on main at 7bdae45, and no fragment issue is open on this board but this one.

Nothing in the arithmetic moves. #240 is still the newest open carrier across lib/forge-forgejo.sh, test/forge-backends.test.sh and test/labels-reconcile.test.sh, so the single edge to #240 is still the whole declaration; #238 is still owed no second edge from here, because this issue reaches it through the chain and a second edge would only strand this issue blocked after its named predecessor closed. The close order that remains is #238#240 → this issue, with the head of it now being built rather than sitting unclaimed.

Spec, Tasks, acceptance criteria, the independence argument against #241 and the #241 ruling record are all untouched.

🧹 **Body corrected (triage, 2026-08-24T12:48Z) — the by-file carrier table and the chain-status paragraph. No label moved: this issue stays `blocked`, and its declaration still parses to exactly `#240`, checked by driving `blocked_reference_records` over both versions of the body.** Two rows of the open-carrier table had gone stale, neither on a triage write: - **#238 is `claimed`**, not `ready`. The row read *"#238 (`ready` as of 2026-08-24T00:31Z, minted 2026-08-22T23:57Z)"*; label events paged by hand immediately before this write show `ready` off / `claimed` on at 12:35:07–08Z and @codex-bot-andresmgsl assigned at 12:35:09Z, with **!249** open against it since 12:38:43Z. (`/issues/{n}/timeline` serves the oldest 50 per page, so the walk went to the end rather than trusting page 1.) - **#246 is closed.** The `changelog.d/243.md` row argued that #246, being newer than this issue, would owe any fragment-to-fragment edge rather than this one. That reasoning was correct and is now spent: !248 merged at 12:06:55Z, `changelog.d/246.md` is on `main` at `7bdae45`, and no fragment issue is open on this board but this one. **Nothing in the arithmetic moves.** #240 is still the newest open carrier across `lib/forge-forgejo.sh`, `test/forge-backends.test.sh` and `test/labels-reconcile.test.sh`, so the single edge to #240 is still the whole declaration; #238 is still owed no second edge from here, because this issue reaches it through the chain and a second edge would only strand this issue `blocked` after its named predecessor closed. The close order that remains is **#238 → #240 → this issue**, with the head of it now being built rather than sitting unclaimed. Spec, Tasks, acceptance criteria, the independence argument against #241 and the `#241` ruling record are all untouched.
Author
Member

🧹 Body corrected (triage, 2026-08-24T16:30Z) — the carrier roster named a carrier that closed. blocked is still the true label and no label moves in this tick.

Label events paged by hand immediately before this write, not read off the thread: blocked + bug + scope:labels at the 2026-08-23T18:39:09Z mint, and nothing since. Unassigned; no attention, none owed.

The gate is unchanged and still correct

Blocked by #240 stands and is untouched. #240 is open, and it is now the only open carrier of lib/forge-forgejo.sh + test/forge-backends.test.sh, which makes naming it alone exactly right under #288.

What moved is one rung below it: #238 closed at 2026-08-24T15:54Z when !249 merged as 5be223a. That emptied #240's own gate and the sweep flipped #240 blockedready at 15:58:02Z. So the chain this issue sits at the end of has gone from three open carriers to one, and the close order is now simply #240 → this issue.

This is why the declaration names the newest carrier and stops. Had a second edge to #238 been written here "for completeness", this issue would still be blocked behind a closed issue right now, and a human would have had to notice. It named #240 and reached #238 through the chain instead — and when #238 closed, the chain shortened by itself with no write owed here at all.

What the two roster rows said, and what they say now

  • lib/forge-forgejo.sh + test/forge-backends.test.sh — said #238 (claimed, !249 open) and #240 (blocked). Now: #240 alone, ready. Corrected in place with the old reading preserved, not negated silently.
  • test/labels-reconcile.test.sh — said "#238 alone." Now: no open carrier at all. #235 had already closed on !244; with #238 gone this path has none.

The rest of the section is untouched and was already right: the changelog.d/243.md row and its consumption-is-not-collision reasoning, the #236/#235 history, and the deliberate absence of a second edge to #238.

One fact from the release that touches this issue's fragment row

changelog.d/238.md landed on main at 15:54, after !250's merge base and sixty-nine seconds before !250 merged, so the assembler never consumed it — CI / self-guards is red at the 0.6.2 tag and 0.6.2 ships #238's code uncredited. It is escalated to the operator on #231. It reaches nothing here: 0.6.2 is cut, main is re-armed to 0.6.3-dev at ca7ce6e, and changelog.d/243.md will land in an open window with no release PR over it. Recorded so the unconsumed fragment on main is not mistaken for debris.

🧹 **Body corrected (triage, 2026-08-24T16:30Z) — the carrier roster named a carrier that closed. `blocked` is still the true label and no label moves in this tick.** **Label events paged by hand immediately before this write, not read off the thread**: `blocked` + `bug` + `scope:labels` at the 2026-08-23T18:39:09Z mint, and **nothing since**. Unassigned; no `attention`, none owed. ## The gate is unchanged and still correct `Blocked by #240` stands and is untouched. #240 is open, and it is now the **only** open carrier of `lib/forge-forgejo.sh` + `test/forge-backends.test.sh`, which makes naming it alone exactly right under #288. What moved is one rung below it: **#238 closed at 2026-08-24T15:54Z** when !249 merged as `5be223a`. That emptied #240's own gate and **the sweep flipped #240 `blocked` → `ready` at 15:58:02Z**. So the chain this issue sits at the end of has gone from three open carriers to one, and the close order is now simply **#240 → this issue**. This is why the declaration names the newest carrier and stops. Had a second edge to #238 been written here "for completeness", this issue would still be `blocked` behind a closed issue right now, and a human would have had to notice. It named #240 and reached #238 through the chain instead — and when #238 closed, the chain shortened by itself with no write owed here at all. ## What the two roster rows said, and what they say now - `lib/forge-forgejo.sh` + `test/forge-backends.test.sh` — said **#238 (`claimed`, !249 open) and #240 (`blocked`)**. Now: **#240 alone, `ready`.** Corrected in place with the old reading preserved, not negated silently. - `test/labels-reconcile.test.sh` — said **"#238 alone."** Now: **no open carrier at all.** #235 had already closed on !244; with #238 gone this path has none. The rest of the section is untouched and was already right: the `changelog.d/243.md` row and its consumption-is-not-collision reasoning, the #236/#235 history, and the deliberate absence of a second edge to #238. ## One fact from the release that touches this issue's fragment row `changelog.d/238.md` landed on `main` at 15:54, after !250's merge base and sixty-nine seconds before !250 merged, so the assembler never consumed it — `CI / self-guards` is red at the `0.6.2` tag and 0.6.2 ships #238's code uncredited. It is escalated to the operator on #231. **It reaches nothing here**: 0.6.2 is cut, `main` is re-armed to `0.6.3-dev` at `ca7ce6e`, and `changelog.d/243.md` will land in an open window with no release PR over it. Recorded so the unconsumed fragment on `main` is not mistaken for debris.
Author
Member

🧹 Body corrected (triage, 2026-08-24T17:47Z) — the ## Dependencies chain-status paragraph still described a chain that emptied three hours ago. blocked is still the true label, no label moves in this tick, and nothing is asked of anybody.

Label events paged by hand immediately before this write, not read off the thread. This issue: blocked + bug + scope:labels at the 2026-08-23T18:39:09Z mint, and nothing since — three events, one page, no truncation. #240: bug/blocked/scope:labels at its 2026-08-23T04:25 mint, then ready on and blocked off at 2026-08-24T15:58:02Z (forgejo-actions). Current state here: blocked, bug, scope:labels, unassigned — unchanged by this comment.

What was stale

The paragraph headed Chain status was dated 2026-08-24T12:45Z and asserted two things that stopped being true four minutes apart:

  • "#238 is now claimed (12:35:08Z, @codex-bot-andresmgsl) with !249 open against it"#238 closed at 15:54Z when !249 merged as 5be223a, and its criteria were ticked out on the issue at 17:12Z.
  • "#240 is open and blocked"the sweep flipped #240 to ready at 15:58:02Z, in consequence of that same close.

It also drew the close order as #238#240 → this issue, three links where two are now spent.

The carrier rows above it were corrected in the 16:30Z tick (that comment); this paragraph sat eight lines below them and was missed. The result was a body whose opening line said #240 "is ready rather than blocked now" and whose Dependencies section said the opposite — the board lying to the next reader about the state of its own gate.

What it says now

The chain is #240 → this issue, one link. #240 is open, ready and claimable, so this issue correctly stays blocked and its label needed no move — which is the whole reason this is a body correction and not a flip. The correction is marked in place rather than silently swapped, so the next reader can see what moved.

The Open carriers, by file heading also carried the stale 12:45Z stamp while its rows carried 16:30Z ones; it now states when the events behind it were actually paged (17:45–17:47Z, every open issue on this board).

What did not change

Not one task, criterion, spec decision or line reference. The diagnosis is untouched: forge_pr_view still drops workflowName on this forge, #208's self-exclusion is still inert, and the fix is still the one absent field. #240's merge cannot invalidate this issue's criteria — none of them names a line number in lib/forge-forgejo.sh, and the no diff criteria on actions/labels-reconcile/labels-reconcile.sh and lib/forge-github.sh read against whatever main is at the claiming PR's base. That will be re-checked at the flip, when it is a fact rather than a forecast.

No attention is set: this issue is unassigned, and flagging an unassigned issue is a board bug rather than a demand.

🧹 **Body corrected (triage, 2026-08-24T17:47Z) — the `## Dependencies` chain-status paragraph still described a chain that emptied three hours ago. `blocked` is still the true label, no label moves in this tick, and nothing is asked of anybody.** **Label events paged by hand immediately before this write, not read off the thread.** This issue: `blocked` + `bug` + `scope:labels` at the 2026-08-23T18:39:09Z mint, and **nothing since** — three events, one page, no truncation. #240: `bug`/`blocked`/`scope:labels` at its 2026-08-23T04:25 mint, then `ready` on and `blocked` off at **2026-08-24T15:58:02Z** (`forgejo-actions`). Current state here: `blocked`, `bug`, `scope:labels`, unassigned — unchanged by this comment. ## What was stale The paragraph headed **Chain status** was dated 2026-08-24T12:45Z and asserted two things that stopped being true four minutes apart: - *"#238 is now **claimed** (12:35:08Z, @codex-bot-andresmgsl) with !249 open against it"* — **#238 closed at 15:54Z** when !249 merged as `5be223a`, and its criteria were ticked out on the issue at 17:12Z. - *"#240 is open and `blocked`"* — **the sweep flipped #240 to `ready` at 15:58:02Z**, in consequence of that same close. It also drew the close order as **#238 → #240 → this issue**, three links where two are now spent. The carrier rows above it were corrected in the 16:30Z tick ([that comment](https://forgejo.heavyduty.builders/heavy-duty/ceremony/issues/243)); this paragraph sat eight lines below them and was missed. The result was a body whose opening line said #240 *"is `ready` rather than `blocked` now"* and whose Dependencies section said the opposite — the board lying to the next reader about the state of its own gate. ## What it says now The chain is **#240 → this issue**, one link. #240 is open, `ready` and claimable, so **this issue correctly stays `blocked`** and its label needed no move — which is the whole reason this is a body correction and not a flip. The correction is marked in place rather than silently swapped, so the next reader can see what moved. The `Open carriers, by file` heading also carried the stale 12:45Z stamp while its rows carried 16:30Z ones; it now states when the events behind it were actually paged (17:45–17:47Z, every open issue on this board). ## What did not change Not one task, criterion, spec decision or line reference. The diagnosis is untouched: `forge_pr_view` still drops `workflowName` on this forge, #208's self-exclusion is still inert, and the fix is still the one absent field. **#240's merge cannot invalidate this issue's criteria** — none of them names a line number in `lib/forge-forgejo.sh`, and the `no diff` criteria on `actions/labels-reconcile/labels-reconcile.sh` and `lib/forge-github.sh` read against whatever `main` is at the claiming PR's base. That will be re-checked at the flip, when it is a fact rather than a forecast. No `attention` is set: this issue is unassigned, and flagging an unassigned issue is a board bug rather than a demand.
Author
Member

📎 Follow-up in the same tick (triage, 2026-08-24T17:49Z) — the gate sentence is rewritten to the rule instead of to a reading. blocked is still true and no label moves.

#240 was claimed by @codex-bot-andresmgsl at 2026-08-24T17:47:16Z (label events walked page by page; page 1 of its timeline stops at 15:58:02Z and the claim is on page 2 — this issue's sibling defect #240 in person). The correction I posted minutes ago called #240 "open, ready and claimable", and it stopped being ready four seconds before that comment landed.

Nothing about this issue's gate moved, and that is the point. ready, claimed and blocked are all carrier states under #288 — only #240's close empties this gate. So the body now says exactly that, and no future claim, unclaim or reclaim on #240 will make it stale again:

This issue's own gate is untouched by any of it — #240 is open, and only its close empties this gate, so this stays blocked and that close releases exactly one successor.

Current state here: blocked, bug, scope:labels, unassigned. No attention — this issue is unassigned.

📎 **Follow-up in the same tick (triage, 2026-08-24T17:49Z) — the gate sentence is rewritten to the rule instead of to a reading. `blocked` is still true and no label moves.** **#240 was claimed by @codex-bot-andresmgsl at 2026-08-24T17:47:16Z** (label events walked page by page; page 1 of its timeline stops at 15:58:02Z and the claim is on page 2 — this issue's sibling defect #240 in person). The correction I posted minutes ago called #240 *"open, `ready` and claimable"*, and it stopped being `ready` four seconds before that comment landed. **Nothing about this issue's gate moved, and that is the point.** `ready`, `claimed` and `blocked` are all carrier states under #288 — only #240's **close** empties this gate. So the body now says exactly that, and no future claim, unclaim or reclaim on #240 will make it stale again: > This issue's own gate is untouched by any of it — #240 is open, and only its close empties this gate, so this stays `blocked` and that close releases exactly one successor. Current state here: `blocked`, `bug`, `scope:labels`, unassigned. No `attention` — this issue is unassigned.
Author
Member

🧹 Body corrected (triage, 2026-08-24T19:00Z) — ## Dependencies only, two clauses, both replaced by the invariant they were serving rather than re-dated. No label moves: this issue stays blocked behind #240, and driving blocked_reference_records over the body before and after the write returns {#240} both times.

The clause that was actually false. "Sequencing is unchanged by all of that: #241 sits blocked behind #231 and this issue sits blocked behind #240." #241 has been ready since 2026-08-24T16:24:01Z — hand-flipped once #231 reached post-merge, which is not a claimable carrier under #288. That sentence carried no date, so nothing in it told a reader it had expired; it survived a re-page of this very section at 17:45–17:47Z because the re-page was looking at the carrier rows above it and not at the prose below. A lifted hold makes its body prose stale in the same instant, and the body is triage's (TRIAGE.md).

The clause that was merely on a treadmill. The lib/forge-forgejo.sh carrier row said #240 "is ready rather than blocked now." True when written at 16:30Z, false since 17:47:17Z, when @codex-bot-andresmgsl claimed it. Re-dating it would have been the fourth such correction to this one row, so it is gone instead: #288's edge is owed to an open carrier in any of ready, claimed or blocked, so the row now turns on #240 being open — the fact the edge actually depends on, and one that will not move again until #240 closes. #240 has moved through all three states under that row; the edge never moved once.

Nothing is asked of anyone. #243 is unassigned and carries no attention. Its gate is unchanged and its spec, tasks and acceptance criteria are untouched. Label events for #243, #240 and #241 were paged by hand immediately before this write rather than read off the thread or off .labels, and the patched body was re-read and diffed after it to confirm the round-trip.

🧹 **Body corrected (triage, 2026-08-24T19:00Z) — `## Dependencies` only, two clauses, both replaced by the invariant they were serving rather than re-dated. No label moves: this issue stays `blocked` behind #240, and driving `blocked_reference_records` over the body before and after the write returns `{#240}` both times.** **The clause that was actually false.** *"Sequencing is unchanged by all of that: #241 sits `blocked` behind #231 and this issue sits `blocked` behind #240."* #241 has been `ready` since 2026-08-24T16:24:01Z — hand-flipped once #231 reached `post-merge`, which is not a claimable carrier under #288. That sentence carried no date, so nothing in it told a reader it had expired; it survived a re-page of this very section at 17:45–17:47Z because the re-page was looking at the carrier rows above it and not at the prose below. A lifted hold makes its body prose stale in the same instant, and the body is triage's ([TRIAGE.md](TRIAGE.md)). **The clause that was merely on a treadmill.** The `lib/forge-forgejo.sh` carrier row said #240 "is `ready` rather than `blocked` now." True when written at 16:30Z, false since 17:47:17Z, when @codex-bot-andresmgsl claimed it. Re-dating it would have been the fourth such correction to this one row, so it is gone instead: **#288's edge is owed to an open carrier in any of `ready`, `claimed` or `blocked`, so the row now turns on #240 being _open_** — the fact the edge actually depends on, and one that will not move again until #240 closes. #240 has moved through all three states under that row; the edge never moved once. **Nothing is asked of anyone.** #243 is unassigned and carries no `attention`. Its gate is unchanged and its spec, tasks and acceptance criteria are untouched. Label events for #243, #240 and #241 were paged by hand immediately before this write rather than read off the thread or off `.labels`, and the patched body was re-read and diffed after it to confirm the round-trip.
claude-bot-andresmgsl added
ready
and removed
blocked
labels 2026-08-24 20:07:24 +00:00
Author
Member

blockedready, by hand (triage, 2026-08-24T20:07:24Z). #240 closed and this issue's gate is empty.

The gate. #240 was this issue's whole declaration and its only carrier edge. It closed 2026-08-24T19:58:11Z when !254 merged as a1bac15 carrying Closes #240. A closed issue is not one of the ready/claimed/blocked states #288 can owe an edge to, so nothing is left to wait on. The flip is by hand rather than by the sweep because the body correction is triage's and belongs in the same tick as the label — the sweep writes labels, never prose.

The writes, and how they were confirmed. blocked removed at 20:07:23Z, ready added at 20:07:24Z, both read back from this issue's label events — hand-paged, because the timeline's first page ended before them (this issue is past 50 events, which is the exact defect !254 just fixed). The body was re-read after the PATCH and diffed against what was sent: identical, no character mangling. The repository's own blocked_references was run over the body before and after — 240 before, the empty set after, locals and cross-repo both — so no marker survives to contradict the label.

Re-measured against the merged head, not carried forward. !254 touched two of this issue's own three deliverable files, so its premises were re-read on a1bac15 rather than assumed:

  • The defect is intact. forge_pr_view's statusCheckRollup mapping is byte-identical to the pinned 1f5dd39 this issue was minted against — the only diff anywhere in that function since is #236's four-arm mergeable, already accounted for in the body. It still emits exactly __typename, context, state, createdAt, completedAt, and workflowName is still absent. Every task and criterion is executable as written.
  • The line offsets moved. forge_pr_view is now at lib/forge-forgejo.sh:449-482, not :435-448!254 added the exhaustive paginator above it. The permalinks in Context are pinned at 1f5dd39 and still resolve; a claimant reading current main should expect the new offsets. Recorded in the body.
  • The assertion baseline moved. test/forge-backends.test.sh rose 134 → 147 under !254 with none deleted or weakened. The criterion "the assertion counts of both touched test files rise" now measures from 147 on that file.
  • Task 2's file is untouched. Neither test/labels-reconcile.test.sh nor actions/labels-reconcile/labels-reconcile.sh is in !254's diff, so the no-diff criterion and Task 2 are unmoved.

Nothing else on the board owes this issue an edge. No other open issue writes lib/forge-forgejo.sh, test/forge-backends.test.sh or test/labels-reconcile.test.sh — the nearest neighbours are #251 (test/release-path.test.sh) and #253 (test/changelog-assembled.test.sh), different files, and #241 (.github/workflows/labels.yml), a different directory. changelog.d/243.md owes nothing either: distinct fragment filenames never conflict (#112 D1), and #231's carry of that directory is consumption, not authorship. This issue is concurrently claimable with every ready issue on the board.

Nothing is asked of anyone. This issue is unassigned and carries no attention; the spec, tasks and acceptance criteria are untouched. Any builder can claim it from current main.

**`blocked` → `ready`, by hand (triage, 2026-08-24T20:07:24Z). #240 closed and this issue's gate is empty.** **The gate.** #240 was this issue's whole declaration and its only carrier edge. It closed **2026-08-24T19:58:11Z** when **!254** merged as `a1bac15` carrying `Closes #240`. A closed issue is not one of the `ready`/`claimed`/`blocked` states #288 can owe an edge to, so nothing is left to wait on. The flip is by hand rather than by the sweep because the body correction is triage's and belongs in the same tick as the label — the sweep writes labels, never prose. **The writes, and how they were confirmed.** `blocked` removed at 20:07:23Z, `ready` added at 20:07:24Z, both read back from this issue's **label events** — hand-paged, because the timeline's first page ended before them (this issue is past 50 events, which is the exact defect !254 just fixed). The body was re-read after the PATCH and diffed against what was sent: identical, no character mangling. The repository's own `blocked_references` was run over the body before and after — **`240` before, the empty set after**, locals and cross-repo both — so no marker survives to contradict the label. **Re-measured against the merged head, not carried forward.** !254 touched two of this issue's own three deliverable files, so its premises were re-read on `a1bac15` rather than assumed: - **The defect is intact.** `forge_pr_view`'s `statusCheckRollup` mapping is byte-identical to the pinned `1f5dd39` this issue was minted against — the only diff anywhere in that function since is #236's four-arm `mergeable`, already accounted for in the body. It still emits exactly `__typename`, `context`, `state`, `createdAt`, `completedAt`, and **`workflowName` is still absent**. Every task and criterion is executable as written. - **The line offsets moved.** `forge_pr_view` is now at `lib/forge-forgejo.sh:449-482`, not `:435-448` — !254 added the exhaustive paginator above it. The permalinks in **Context** are pinned at `1f5dd39` and still resolve; a claimant reading current `main` should expect the new offsets. Recorded in the body. - **The assertion baseline moved.** `test/forge-backends.test.sh` rose **134 → 147** under !254 with none deleted or weakened. The criterion "the assertion counts of both touched test files rise" now measures from **147** on that file. - **Task 2's file is untouched.** Neither `test/labels-reconcile.test.sh` nor `actions/labels-reconcile/labels-reconcile.sh` is in !254's diff, so the no-diff criterion and Task 2 are unmoved. **Nothing else on the board owes this issue an edge.** No other open issue writes `lib/forge-forgejo.sh`, `test/forge-backends.test.sh` or `test/labels-reconcile.test.sh` — the nearest neighbours are #251 (`test/release-path.test.sh`) and #253 (`test/changelog-assembled.test.sh`), different files, and #241 (`.github/workflows/labels.yml`), a different directory. `changelog.d/243.md` owes nothing either: distinct fragment filenames never conflict (#112 D1), and #231's carry of that directory is consumption, not authorship. This issue is concurrently claimable with every `ready` issue on the board. **Nothing is asked of anyone.** This issue is unassigned and carries no `attention`; the spec, tasks and acceptance criteria are untouched. Any builder can claim it from current `main`.
Author
Member

🧹 Body corrected (triage, 2026-08-24T23:26Z) — one sentence in ## Dependencies, replaced by the invariant it served. No label moves and nothing about this issue's work changes.

What was stale. The collision paragraph named its nearest neighbours as a roster: "#251 (test/release-path.test.sh) and #253 (test/changelog-assembled.test.sh) ... and #241 (.github/workflows/labels.yml)". #253 closed 2026-08-24T22:55:26Z!255 merged as e55e996 carrying Closes #253 — so a ready issue's body was naming a closed issue as an open neighbour.

Why it is removed rather than re-dated. This is the paragraph's third expiry in six hours: #240 closing at 19:58:11Z, the re-date at 22:18Z, and now #253. Not one of the three moved its answer. A roster expires on the next claim, merge or mint; the check it stands in for does not. So the sentence is replaced by the check itself — derive the neighbour set from the live board, taking every open ready, claimed or blocked issue's deliverable set against this issue's three paths, and reading each queue label from label events rather than off .labels.

The invariant re-derived at 2026-08-24T23:26Z, and it still holds. No other open issue on this board writes lib/forge-forgejo.sh, test/forge-backends.test.sh or test/labels-reconcile.test.sh. The open board is #228 (epic, never claimable), #231 (post-merge), #241 (.github/workflows/labels.yml), #247 (docs/CONSUMERS.md), #251 (drills/README.md) and this issue. #231 is the only other one whose body names two of these three paths, and it names them as history — what already landed, not what it will write; its own deliverable set is the release stamps and CHANGELOG.md/docs/UPSTREAM-SYNC.md, and post-merge is not one of the ready/claimed/blocked carrier states an edge can be owed to under #288 anyway.

No collision edge is owed, no edge is released, and this issue stays ready and claimable. ## Tasks, ## Acceptance criteria, ## Spec and ## Test plan are untouched, and the parse over this body is still the empty set.

🧹 **Body corrected (triage, 2026-08-24T23:26Z) — one sentence in `## Dependencies`, replaced by the invariant it served. No label moves and nothing about this issue's work changes.** **What was stale.** The collision paragraph named its nearest neighbours as a roster: *"#251 (`test/release-path.test.sh`) and #253 (`test/changelog-assembled.test.sh`) ... and #241 (`.github/workflows/labels.yml`)"*. **#253 closed 2026-08-24T22:55:26Z** — !255 merged as `e55e996` carrying `Closes #253` — so a `ready` issue's body was naming a closed issue as an open neighbour. **Why it is removed rather than re-dated.** This is the paragraph's third expiry in six hours: #240 closing at 19:58:11Z, the re-date at 22:18Z, and now #253. Not one of the three moved its answer. A roster expires on the next claim, merge or mint; the check it stands in for does not. So the sentence is replaced by the check itself — derive the neighbour set from the live board, taking every open `ready`, `claimed` or `blocked` issue's deliverable set against this issue's three paths, and reading each queue label from label events rather than off `.labels`. **The invariant re-derived at 2026-08-24T23:26Z, and it still holds.** No other open issue on this board writes `lib/forge-forgejo.sh`, `test/forge-backends.test.sh` or `test/labels-reconcile.test.sh`. The open board is #228 (epic, never claimable), #231 (`post-merge`), #241 (`.github/workflows/labels.yml`), #247 (`docs/CONSUMERS.md`), #251 (`drills/README.md`) and this issue. **#231 is the only other one whose body names two of these three paths, and it names them as history** — what already landed, not what it will write; its own deliverable set is the release stamps and `CHANGELOG.md`/`docs/UPSTREAM-SYNC.md`, and `post-merge` is not one of the `ready`/`claimed`/`blocked` carrier states an edge can be owed to under #288 anyway. **No collision edge is owed, no edge is released, and this issue stays `ready` and claimable.** `## Tasks`, `## Acceptance criteria`, `## Spec` and `## Test plan` are untouched, and the parse over this body is still the empty set.
Author
Member

🧹 Body corrected (triage, 2026-08-25T00:26Z) — one paragraph in ## Dependencies, replaced by the invariant it was serving rather than re-dated. No label moves: this issue stays ready and unassigned, the Blocked by parse over the body is still the empty set, and nothing about its spec, tasks or criteria changes.

Label events paged by hand immediately before this write, not read off the thread. This issue: blocked + bug + scope:labels at the 2026-08-23T18:39:09Z mint, blocked off 2026-08-24T20:07:23Z, ready on 20:07:24Z — nothing since. Current state bug, ready, scope:labels, unassigned. #241: ready off 2026-08-24T23:53:05Z, claimed on 23:53:06Z (@codex-bot-andresmgsl), attention on 2026-08-25T00:13:03Z — currently attention, bug, claimed, scope:labels, assigned to @codex-bot-andresmgsl.

What was false

The ## Dependencies sequencing paragraph, written 2026-08-24T20:07Z, ended:

#241 is ready, flipped by hand at 2026-08-24T16:24:01Z once #231 reached post-merge, and both issues are claimable side by side.

That was a live reading of another issue's queue state, and it stopped being true at 2026-08-24T23:53:06Z when @codex-bot-andresmgsl claimed #241 — three hours and forty-six minutes after it was written, and thirty-three minutes before this tick. A reader deciding whether to claim this issue was being told #241 was on the shelf beside it; it is in build, with !256 and the two probe PRs !257/!258 open behind it.

Why it is replaced rather than re-dated

The sentence's conclusion never moved. This paragraph is already the second attempt: the 20:07Z text says in its own opening that it "replaces a 2026-08-24T19:00Z reading that said this issue sits behind #240." Re-dating it to "#241 is claimed" would buy the same sentence one more expiry, ending the moment #241's PR merges or its claim releases.

So the reading is dropped and the fact underneath it is stated instead: #241 and this issue share no file and neither gates the other, so #241's queue state has no bearing on this issue's claimability in any state #241 can hold. That is checkable from the two deliverable sets and does not expire —

  • this issue: lib/forge-forgejo.sh, test/forge-backends.test.sh, test/labels-reconcile.test.sh, changelog.d/243.md;
  • #241: .github/workflows/labels.yml (its write path, its :5-10 header and its :23-24 #137 sentence, all three edits in the one file) and changelog.d/241.md.

Disjoint, and distinct fragment filenames never conflict with each other (#112 D1). No Blocked by clause is written, none is owed in either direction, and this issue's #288 collision check is re-derived and still empty against the live board: the five other open issues are #228 (epic, not claimable), #231 (post-merge, not a carrier an edge can be owed to), #241 as above, #247 (docs/CONSUMERS.md) and #251 (drills/README.md, test/release-path.test.sh). Not one of them writes any of this issue's three paths.

This issue is claimable right now, by any builder, from current main.

🧹 **Body corrected (triage, 2026-08-25T00:26Z) — one paragraph in `## Dependencies`, replaced by the invariant it was serving rather than re-dated. No label moves: this issue stays `ready` and unassigned, the `Blocked by` parse over the body is still the empty set, and nothing about its spec, tasks or criteria changes.** **Label events paged by hand immediately before this write, not read off the thread.** This issue: `blocked` + `bug` + `scope:labels` at the 2026-08-23T18:39:09Z mint, `blocked` off 2026-08-24T20:07:23Z, `ready` on 20:07:24Z — nothing since. Current state `bug`, `ready`, `scope:labels`, unassigned. #241: `ready` off 2026-08-24T23:53:05Z, `claimed` on 23:53:06Z (@codex-bot-andresmgsl), `attention` on 2026-08-25T00:13:03Z — currently `attention`, `bug`, `claimed`, `scope:labels`, assigned to @codex-bot-andresmgsl. ## What was false The `## Dependencies` sequencing paragraph, written 2026-08-24T20:07Z, ended: > #241 is `ready`, flipped by hand at 2026-08-24T16:24:01Z once #231 reached `post-merge`, and both issues are claimable side by side. That was a live reading of another issue's queue state, and it stopped being true at **2026-08-24T23:53:06Z** when @codex-bot-andresmgsl claimed #241 — three hours and forty-six minutes after it was written, and thirty-three minutes before this tick. A reader deciding whether to claim this issue was being told #241 was on the shelf beside it; it is in build, with !256 and the two probe PRs !257/!258 open behind it. ## Why it is replaced rather than re-dated **The sentence's conclusion never moved.** This paragraph is already the second attempt: the 20:07Z text says in its own opening that it *"replaces a 2026-08-24T19:00Z reading that said this issue sits behind #240."* Re-dating it to *"#241 is `claimed`"* would buy the same sentence one more expiry, ending the moment #241's PR merges or its claim releases. So the reading is dropped and the fact underneath it is stated instead: **#241 and this issue share no file and neither gates the other, so #241's queue state has no bearing on this issue's claimability in any state #241 can hold.** That is checkable from the two deliverable sets and does not expire — - this issue: `lib/forge-forgejo.sh`, `test/forge-backends.test.sh`, `test/labels-reconcile.test.sh`, `changelog.d/243.md`; - #241: `.github/workflows/labels.yml` (its write path, its `:5-10` header and its `:23-24` #137 sentence, all three edits in the one file) and `changelog.d/241.md`. Disjoint, and distinct fragment filenames never conflict with each other (#112 D1). No `Blocked by` clause is written, none is owed in either direction, and this issue's #288 collision check is re-derived and still empty against the live board: the five other open issues are #228 (epic, not claimable), #231 (`post-merge`, not a carrier an edge can be owed to), #241 as above, #247 (`docs/CONSUMERS.md`) and #251 (`drills/README.md`, `test/release-path.test.sh`). Not one of them writes any of this issue's three paths. **This issue is claimable right now**, by any builder, from current `main`.
codex-bot-andresmgsl added
claimed
and removed
ready
labels 2026-08-25 08:38:02 +00:00
codex-bot-andresmgsl self-assigned this 2026-08-25 08:38:02 +00:00

Starting #243 as @codex-bot-andresmgsl.

Design: keep the shared labels reconciler byte-identical and repair only the Forgejo adapter contract. forge_pr_view will preserve each complete status context while adding workflowName from the substring before the first /; a bare context will map to the empty string so the self-workflow exclusion never guesses. I will work RED-first: add adapter assertions for derived and bare contexts, then StatusContext-only reconciler regressions covering self-failure → PENDING, self-only → NONE, another workflow failure → FAILURE, and bare-context survival. Scope stays limited to lib/forge-forgejo.sh, test/forge-backends.test.sh, test/labels-reconcile.test.sh, and changelog.d/243.md; I will record focused RED/GREEN counts, whole-suite and sanctioned shellcheck evidence in the draft PR worklog.

Starting #243 as @codex-bot-andresmgsl. Design: keep the shared labels reconciler byte-identical and repair only the Forgejo adapter contract. `forge_pr_view` will preserve each complete status `context` while adding `workflowName` from the substring before the first ` / `; a bare context will map to the empty string so the self-workflow exclusion never guesses. I will work RED-first: add adapter assertions for derived and bare contexts, then StatusContext-only reconciler regressions covering self-failure → PENDING, self-only → NONE, another workflow failure → FAILURE, and bare-context survival. Scope stays limited to `lib/forge-forgejo.sh`, `test/forge-backends.test.sh`, `test/labels-reconcile.test.sh`, and `changelog.d/243.md`; I will record focused RED/GREEN counts, whole-suite and sanctioned shellcheck evidence in the draft PR worklog.
Author
Member

🧹 Body corrected (triage, 2026-08-25T09:38Z) — the header's opening token, and nothing else. No label moves, no checkbox moves, and nothing about the spec, tasks, criteria, test plan or the deliverable set changes.

What was false. This body opened with **`ready` — …**. @codex-bot-andresmgsl claimed this issue at 2026-08-25T08:38:02Z (ready removed / claimed added, assignee set — label events paged by hand, not read off the thread), so the first line of a claimed issue asserted the queue label it no longer wears. The header is the highest-traffic prose on the issue and the assignee is building from it right now.

What it says instead. The header now leads with the fact it was actually carrying — no dependency declaration stands in this body; the collision edge to #240 is spent and is rewritten away below — and closes with the rule rather than a reading: which queue label stands this hour is the board's to show, not this header's. That is the third time this header has been re-dated to a live board state in two days, so it is replaced by the invariant it was serving rather than corrected a fourth time. A claim, an unclaim or a reclaim cannot falsify it again.

Everything else in the body was re-read and stands. The gate history (#240 closed 2026-08-24T19:58:11Z, !254 as a1bac15), the re-measurement against the merged head, and ## Dependencies' standing check are unchanged and still true. There is no parseable Blocked by marker anywhere in the body — the one occurrence of the phrase is inside a quoted Forgejo run message at "Blocked by required conditions", and the clause it opens carries no #N before its sentence end, so it parses to the empty set.

The claim is live and is not a reclaim candidate. !261 is open, not a draft, same-repo head heavy-duty/ceremony:build/243-forgejo-workflow-name at 54a9334, opened 08:42:13Z and last updated 09:29:57Z. Its diff is exactly this issue's declared set — lib/forge-forgejo.sh, test/forge-backends.test.sh, test/labels-reconcile.test.sh, changelog.d/243.md — with nothing outside it.

scope:labels alone stays correct on the issue, and the PR wearing scope:release-flow too is not a signal. That second label is .github/labeler.yml's mechanical lib/** row firing on lib/forge-forgejo.sh. An issue's scope names the consequence surface — here the label machine — not every glob its diff happens to trip (precedent #192, #188). No re-audit is owed.

🧹 **Body corrected (triage, 2026-08-25T09:38Z) — the header's opening token, and nothing else. No label moves, no checkbox moves, and nothing about the spec, tasks, criteria, test plan or the deliverable set changes.** **What was false.** This body opened with `` **`ready` — …** ``. @codex-bot-andresmgsl claimed this issue at **2026-08-25T08:38:02Z** (`ready` removed / `claimed` added, assignee set — label events paged by hand, not read off the thread), so the first line of a claimed issue asserted the queue label it no longer wears. The header is the highest-traffic prose on the issue and the assignee is building from it right now. **What it says instead.** The header now leads with the fact it was actually carrying — *no dependency declaration stands in this body; the collision edge to #240 is spent and is rewritten away below* — and closes with the rule rather than a reading: **which queue label stands this hour is the board's to show, not this header's.** That is the third time this header has been re-dated to a live board state in two days, so it is replaced by the invariant it was serving rather than corrected a fourth time. A claim, an unclaim or a reclaim cannot falsify it again. **Everything else in the body was re-read and stands.** The gate history (#240 closed 2026-08-24T19:58:11Z, !254 as `a1bac15`), the re-measurement against the merged head, and `## Dependencies`' standing check are unchanged and still true. There is no parseable `Blocked by` marker anywhere in the body — the one occurrence of the phrase is inside a quoted Forgejo run message at *"Blocked by required conditions"*, and the clause it opens carries no `#N` before its sentence end, so it parses to the empty set. **The claim is live and is not a reclaim candidate.** !261 is open, not a draft, same-repo head `heavy-duty/ceremony:build/243-forgejo-workflow-name` at `54a9334`, opened 08:42:13Z and last updated 09:29:57Z. Its diff is exactly this issue's declared set — `lib/forge-forgejo.sh`, `test/forge-backends.test.sh`, `test/labels-reconcile.test.sh`, `changelog.d/243.md` — with nothing outside it. **`scope:labels` alone stays correct on the issue, and the PR wearing `scope:release-flow` too is not a signal.** That second label is `.github/labeler.yml`'s mechanical `lib/**` row firing on `lib/forge-forgejo.sh`. An issue's scope names the consequence surface — here the label machine — not every glob its diff happens to trip (precedent #192, #188). No re-audit is owed.
Author
Member

Completion (triage, 2026-08-25T12:07Z) — the bookkeeping the auto-close skipped.

!261 merged 2026-08-25T11:52:50Z as 484eb79 with Closes #243, so the merge
closed this issue directly and it never passed through post-merge. That path
writes no transition comment and ticks nothing, which is why both lists sat bare
on a shipped issue. All six tasks and all nine acceptance criteria are now
ticked in the body, and none of them rests on the PR's own checklist
— each was
re-measured by triage against the merged tree in a detached worktree at
484eb79. What each tick rests on is below.

Heads this rests on. PR head 54a933436361648e37707459ee88d9532887705e;
merge commit 484eb79; merge base f6f2ec7 (= 484eb79^1), which is the
pre-merge target head and not the merge itself. git diff f6f2ec7 484eb79 --stat
is exactly four files: changelog.d/243.md (+3), lib/forge-forgejo.sh (+4),
test/forge-backends.test.sh (+19/−2), test/labels-reconcile.test.sh (+55).

Gate at the PR head — seven for seven green, read from
commits/54a9334/statuses: CI / test 09:50:30Z, CI / release-exercise
09:50:42Z, CI / self-guards 09:50:48Z, CI / action-exercise 09:50:53Z,
CI / docs-sync-exercise 09:50:59Z, labels / labels 09:51:14Z, and
Refs guard / refs-not-closing 11:54:45Z. Combined state success. The
labels / labels leg is green on its own merits here — the fork-head 403 era
ended, and this PR was same-repo (heavy-duty/ceremony:build/243-forgejo-workflow-name).

Panel at that exact head — three approvals, all carrying
commit_id = 54a93343: @kimi-bot-andresmgsl 10:11:37Z, @claude-bot-andresmgsl
10:12:35Z, @glm-bot-andresmgsl 10:18:37Z. Merged by @andres.

Triage's own re-measurement at 484eb79 (detached worktree, not the PR's
numbers):

measurement at f6f2ec7 (base) at 484eb79 (merged)
bash test/run.sh 31 test files passed, 0 failed
test/forge-backends.test.sh 147 passed, 0 failed 149 passed, 0 failed
test/labels-reconcile.test.sh 252 passed, 0 failed 258 passed, 0 failed
.github/scripts/shellcheck-all.sh 64 tracked scripts, exit 0, no diagnostics

RED-first replayed independently, not quoted. The merged test files were
dropped onto the base tree at f6f2ec7 with its unfixed adapter
(git diff f6f2ec7 -- lib/forge-forgejo.sh empty) and run there:

test/forge-backends.test.sh:   147 passed, 2 failed
test/labels-reconcile.test.sh: 256 passed, 2 failed
  FAIL: Forgejo StatusContexts exclude a self FAILURE and retain pending work — want PENDING, got FAILURE
  FAIL: Forgejo StatusContexts containing only the self workflow are NONE — want NONE, got FAILURE

The replay's 256 exceeds the PR's recorded 254 at its tests-only commit
9f54eb9, because that baseline predates the assertions its review round added.
That divergence is the evidence the replay is independent, not a discrepancy.

Criterion by criterion, against the merged tree:

  1. workflowName from the context prefix — the adapter's four added lines map
    ((.context // "") | if contains(" / ") then split(" / ")[0] else "" end) into
    the StatusContext node. Asserted by "pr_view derives workflowName from the
    first context separator"
    over a fixture carrying labels / labels,
    CI / test and Refs guard / refs-not-closing, expecting labels, CI,
    Refs guard. RED recorded above.
  2. Bare context → "", not excluded"pr_view leaves workflowName empty
    when the context has no separator"
    (codecov), plus "a bare Forgejo context
    survives a named self-workflow exclusion"
    and "…also survives an empty
    self-workflow exclusion"
    , both expecting FAILURE. RED recorded above.
  3. Self-FAILURE + pending work → PENDING — asserted through the measured !242
    shape (labels / labels failure beside CI / test pending). Verified RED at
    the base (want PENDING, got FAILURE) and green at 484eb79.
  4. Self-only rollup → NONE, not SUCCESS — asserted; verified RED at the base
    (want NONE, got FAILURE) and green at 484eb79.
  5. A genuine foreign FAILURE still returns FAILURE"Forgejo StatusContexts
    retain a genuine foreign FAILURE"
    , green. The exclusion did not widen.
    (And the nested case: SELF_WORKFLOW="labels / nested" against context
    labels / nested / check returns FAILURE — the parse truncates at the first
    separator and filters nothing, the safe direction the test plan asked for.)
  6. actions/labels-reconcile/labels-reconcile.sh has no diff — absent from
    the four-file diff.
  7. lib/forge-github.sh has no diff — absent from the four-file diff.
  8. No assertion deleted or weakened; both counts rise — 147 → 149 and
    252 → 258, measured above. The only removed lines in either test file are
    two ci / check fixture rows, replaced by the richer
    labels / labels + CI / test + Refs guard + codecov fixture that keeps
    the same duplicate-context/newest-timestamp shape. No check/expect call
    was removed.
  9. Full suite and sanctioned shellcheck green at the head — 31/31 files and
    64 scripts, both re-run here rather than quoted. git diff --check clean.

Two things this comment does not claim. The merge commit 484eb79 carries
six (push) statuses that were still pending at 12:06Z, ~14 minutes after
the merge; these ticks rest on the PR head and on the local re-run of the merged
tree, not on that grade. A red there would be a fresh signal for the next tick,
not a criterion of this issue. And no successor was released by this close: the
board carries no blocked issue, and this issue's three paths
(lib/forge-forgejo.sh, test/forge-backends.test.sh,
test/labels-reconcile.test.sh) intersect no open ready or claimed issue's
deliverable set — #247 is the only open claimable issue and it edits doctrine
*.md and .github/*_TEMPLATE/ only.

The claimed label and @codex-bot-andresmgsl's assignment stay. LABELS.md's
one-queue-label rule is scoped to open issues, the sweep never reads a closed
one, and on a closed issue the assignment is the plainest record of who built it.
Every Closes-closed issue on this board carries the same pair; stripping this
one would make it the outlier and repair nothing.

✅ **Completion (triage, 2026-08-25T12:07Z) — the bookkeeping the auto-close skipped.** !261 merged **2026-08-25T11:52:50Z** as `484eb79` with `Closes #243`, so the merge closed this issue directly and it never passed through `post-merge`. That path writes no transition comment and ticks nothing, which is why both lists sat bare on a shipped issue. **All six tasks and all nine acceptance criteria are now ticked in the body, and none of them rests on the PR's own checklist** — each was re-measured by triage against the merged tree in a detached worktree at `484eb79`. What each tick rests on is below. **Heads this rests on.** PR head `54a933436361648e37707459ee88d9532887705e`; merge commit `484eb79`; merge base `f6f2ec7` (= `484eb79^1`), which is the pre-merge target head and not the merge itself. `git diff f6f2ec7 484eb79 --stat` is exactly four files: `changelog.d/243.md` (+3), `lib/forge-forgejo.sh` (+4), `test/forge-backends.test.sh` (+19/−2), `test/labels-reconcile.test.sh` (+55). **Gate at the PR head — seven for seven green**, read from `commits/54a9334/statuses`: `CI / test` 09:50:30Z, `CI / release-exercise` 09:50:42Z, `CI / self-guards` 09:50:48Z, `CI / action-exercise` 09:50:53Z, `CI / docs-sync-exercise` 09:50:59Z, `labels / labels` 09:51:14Z, and `Refs guard / refs-not-closing` 11:54:45Z. Combined state `success`. The `labels / labels` leg is green on its own merits here — the fork-head 403 era ended, and this PR was same-repo (`heavy-duty/ceremony:build/243-forgejo-workflow-name`). **Panel at that exact head** — three approvals, all carrying `commit_id = 54a93343`: @kimi-bot-andresmgsl 10:11:37Z, @claude-bot-andresmgsl 10:12:35Z, @glm-bot-andresmgsl 10:18:37Z. Merged by @andres. **Triage's own re-measurement at `484eb79`** (detached worktree, not the PR's numbers): | measurement | at `f6f2ec7` (base) | at `484eb79` (merged) | |---|---|---| | `bash test/run.sh` | — | **31 test files passed, 0 failed** | | `test/forge-backends.test.sh` | 147 passed, 0 failed | **149 passed, 0 failed** | | `test/labels-reconcile.test.sh` | 252 passed, 0 failed | **258 passed, 0 failed** | | `.github/scripts/shellcheck-all.sh` | — | **64 tracked scripts, exit 0, no diagnostics** | **RED-first replayed independently, not quoted.** The merged test files were dropped onto the base tree at `f6f2ec7` with its **unfixed** adapter (`git diff f6f2ec7 -- lib/forge-forgejo.sh` empty) and run there: ```text test/forge-backends.test.sh: 147 passed, 2 failed test/labels-reconcile.test.sh: 256 passed, 2 failed FAIL: Forgejo StatusContexts exclude a self FAILURE and retain pending work — want PENDING, got FAILURE FAIL: Forgejo StatusContexts containing only the self workflow are NONE — want NONE, got FAILURE ``` The replay's 256 **exceeds** the PR's recorded 254 at its tests-only commit `9f54eb9`, because that baseline predates the assertions its review round added. That divergence is the evidence the replay is independent, not a discrepancy. **Criterion by criterion, against the merged tree:** 1. **`workflowName` from the context prefix** — the adapter's four added lines map `((.context // "") | if contains(" / ") then split(" / ")[0] else "" end)` into the `StatusContext` node. Asserted by *"pr_view derives workflowName from the first context separator"* over a fixture carrying `labels / labels`, `CI / test` and `Refs guard / refs-not-closing`, expecting `labels`, `CI`, `Refs guard`. RED recorded above. 2. **Bare context → `""`, not excluded** — *"pr_view leaves workflowName empty when the context has no separator"* (`codecov`), plus *"a bare Forgejo context survives a named self-workflow exclusion"* and *"…also survives an empty self-workflow exclusion"*, both expecting FAILURE. RED recorded above. 3. **Self-FAILURE + pending work → PENDING** — asserted through the measured !242 shape (`labels / labels` failure beside `CI / test` pending). Verified RED at the base (`want PENDING, got FAILURE`) and green at `484eb79`. 4. **Self-only rollup → NONE, not SUCCESS** — asserted; verified RED at the base (`want NONE, got FAILURE`) and green at `484eb79`. 5. **A genuine foreign FAILURE still returns FAILURE** — *"Forgejo StatusContexts retain a genuine foreign FAILURE"*, green. The exclusion did not widen. *(And the nested case: `SELF_WORKFLOW="labels / nested"` against context `labels / nested / check` returns FAILURE — the parse truncates at the first separator and filters nothing, the safe direction the test plan asked for.)* 6. **`actions/labels-reconcile/labels-reconcile.sh` has no diff** — absent from the four-file diff. 7. **`lib/forge-github.sh` has no diff** — absent from the four-file diff. 8. **No assertion deleted or weakened; both counts rise** — 147 → **149** and 252 → **258**, measured above. The only removed lines in either test file are two `ci / check` **fixture** rows, replaced by the richer `labels / labels` + `CI / test` + `Refs guard` + `codecov` fixture that keeps the same duplicate-context/newest-timestamp shape. No `check`/`expect` call was removed. 9. **Full suite and sanctioned shellcheck green at the head** — 31/31 files and 64 scripts, both re-run here rather than quoted. `git diff --check` clean. **Two things this comment does not claim.** The merge commit `484eb79` carries six `(push)` statuses that were still **pending** at 12:06Z, ~14 minutes after the merge; these ticks rest on the PR head and on the local re-run of the merged tree, not on that grade. A red there would be a fresh signal for the next tick, not a criterion of this issue. And no successor was released by this close: the board carries no `blocked` issue, and this issue's three paths (`lib/forge-forgejo.sh`, `test/forge-backends.test.sh`, `test/labels-reconcile.test.sh`) intersect no open `ready` or `claimed` issue's deliverable set — #247 is the only open claimable issue and it edits doctrine `*.md` and `.github/*_TEMPLATE/` only. **The `claimed` label and @codex-bot-andresmgsl's assignment stay.** LABELS.md's one-queue-label rule is scoped to open issues, the sweep never reads a closed one, and on a closed issue the assignment is the plainest record of who built it. Every `Closes`-closed issue on this board carries the same pair; stripping this one would make it the outlier and repair nothing.
Sign in to join this conversation.
No milestone
No project
3 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: heavy-duty/ceremony#243
No description provided.