Skip to content

release-train: develop -> staging - #400

Merged
tracebloc-release-train[bot] merged 2 commits into
stagingfrom
release-train/to-staging
Sep 1, 2026
Merged

release-train: develop -> staging#400
tracebloc-release-train[bot] merged 2 commits into
stagingfrom
release-train/to-staging

Conversation

@LukasWodka

@LukasWodka LukasWodka commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Automated promotion by the release train (RFC-0008 D14). Head is the train-managed release-train/to-staging branch (a mirror of develop), so it never collides with a human PR. Merged only when the fr-gate is green.


Note

Medium Risk
Changes affect daily kanban CI gates and branch-ownership tooling used for stale-branch workflows; misbehavior could cause false reds or wrong attribution, but there is no auth or data-path change.

Overview
Kanban archive no longer fails baseline recall on checkout-less jobs: the recall step sets GH_REPO so gh run download can resolve the repo, captures download stderr into prev.stderr (not prev.error, which would false-fail asserts), and surfaces gh’s error text when a live board-baseline artifact cannot be fetched.

branch_owner.py replaces the capped gh pr list --limit 1000 path with paginated GraphQL (gh api graphql --paginate --slurp), validates completeness via rows == totalCount (plus duplicate-page / inconsistent-count checks), and exits non-zero without branch rows when the list is incomplete so output is not mistaken for “everyone unattributable.” It also fixes gh repo view for --repo runs by passing the repository positionally (the --repo flag was never valid). Selftests and mutation harnesses are updated for these behaviors.

Reviewed by Cursor Bugbot for commit a8c0546. Bugbot is set up for automated code reviews on this repo. Configure here.

LukasWodka and others added 2 commits September 1, 2026 16:33
…ame itself (backend#2802) (#397)

* fix(kanban): give the baseline download a repo, and let its failure name itself (backend#2802)

kanban-archive.yml has been red on every scheduled run while ARCHIVING PERFECTLY --
today: 72 archived, 0 failed, then a non-zero exit. The failure is one missing env
var, and the reason it took a reproduction to find is a second defect in the same
step.

THE CAUSE. `gh run download` takes no `--repo`, and this job has NO checkout (its
steps are all API calls; it needs no source). So gh resolved the repository from the
git remote, found no `.git`, and died BEFORE making any API call. That is exactly why
the artifacts LISTING in the same step succeeded -- it puts the repo in the URL path --
while the fetch failed.

Reproduced outside Actions, gh 2.98.0:

    $ cd /tmp/empty && gh run download 33393320354 -n board-baseline
    failed to run git: fatal: not a git repository (or any of the parent directories)
    $ GH_REPO=tracebloc/.github gh run download 33393320354 -n board-baseline -D prev
    $ cat prev/board.total
    574

574 is the baseline the job could not read. `gh` does NOT read
`GITHUB_REPOSITORY` -- tested -- so being on a runner does not supply it. `GH_REPO`
is the variable it does read, and it is narrower than adding a checkout to a job that
needs no source.

RULED OUT RATHER THAN GUESSED PAST: not retention (`retention-days: 30`; the
artifact is 142 bytes, created 2026-08-31, `expired: false`); not permissions
(`actions: read` is set and the same token succeeded on the listing in the same
step); not the download-artifact v4 cross-run restriction (there is no
`actions/download-artifact` in this file at all).

AND THE SECOND DEFECT, which is why five red runs were undiagnosable from their own
logs: `>/dev/null 2>&1` on the download threw away the only line that said why, and
the refusal below then CLOBBERED `prev.error` with a generic summary. Now stderr is
captured to `prev.error` and the summary APPENDS the tool's own words rather than
overwriting them. A refusal that cannot say what it saw sends the next reader to
reproduce it by hand, which is what happened here.

NOT A DUPLICATE: backend#2802 and backend#2903 are both closed, and #2903 is a
different defect (a `per_page=100` listing treated as complete -- the class tracked
by the open #2944). Neither describes this cause. The download path itself landed
2026-08-30 in .github#383.

GUARDED. `archive-baseline-selftest.py` now asserts the recall step sets `GH_REPO`
from `github.repository` and captures the download's stderr. Both mutation-proved
against the live file: removing `GH_REPO` -> "does not set \`GH_REPO\`";
restoring `2>&1` -> "not capture stderr". Full selftest green either side --
10 cross-run + 10 identity + 6 completeness cases + the detector anchors.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* test(kanban): re-anchor the stale download-failure mutation (backend#2802)

The fix in the previous commit rewrote the download-failed branch to capture gh
stderr and append to prev.error, so archive-baseline-mutations.py own anchor for
that branch matched 0 times.

THE HARNESS CAUGHT IT ITSELF and refused, rather than reporting 0 uncaught about a
premise nobody typed -- which is why this is a re-anchor and not a discovery. An
inert mutation is indistinguishable from good coverage in a log, and that guard is
the reason this commit exists.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* test(kanban): anchor the whole printf, not its first line (backend#2802)

The re-anchor in the previous commit applied but changed nothing: it replaced only
the printf FIRST line, leaving the continuation ("$rid" ... > prev.error) standing
as its own command -- so prev.error was still written and the mutation went
UNCAUGHT rather than inert. Two different failures with the same symptom in a log,
and the harness distinguished them: "anchor matched 0 times" first, then "the suite
passed with this broken".

Anchoring both lines makes the mutation actually remove the write.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(kanban): capture the download stderr WITHOUT dirtying the unreadable sentinel (backend#2802)

Bugbot High on my own fix, and it is right about the coupling. The previous commit
sent `gh run download` stderr straight into `prev.error` -- but the assert step
treats ANY non-empty `prev.error` as an unreadable baseline (`if [ -s prev.error ]`
at :724). So a SUCCESSFUL download that printed anything at all would mark the
baseline unreadable and keep the job red after a good archive: the exact failure
this PR exists to fix, arriving from the other side.

MEASURED, AND THE FINDING AS DESCRIBED DOES NOT REPRODUCE: `gh run download` writes
0 bytes to stderr on success on a non-TTY (tested, gh 2.98.0; a runner is also
non-TTY). So no run would have gone red today.

FIXED ANYWAY, because the coupling is the defect rather than the current byte count.
One deprecation notice on stderr in a future gh is all it takes, and this file is
full of exactly that shape -- a sentinel that means "could not read" quietly
acquiring a second meaning. stderr now goes to `prev.stderr` and only the FAILURE
branch promotes it into `prev.error`, so the sentinel has one writer and one
meaning.

The stale comment claiming stderr "lands in prev.error" is corrected in the same
commit -- it would otherwise describe the shape this change just removed.

Selftest extended both ways: the download must capture into `prev.stderr`, and it
must NOT write into `prev.error`. Two mutations, both reddening -- restore the
straight-into-sentinel write; drop the capture entirely.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…e out loud (backend#2972) (#398)

`branch_owner.py` asked `gh pr list --limit 1000`. `tracebloc/backend` has 1418
pull requests, so the seam failed closed on the org's LARGEST repo -- the one
carrying 62 of the org's 99 stale branches -- and `main` printed
`108 branch(es): 0 attributed, 108 unattributable` and exited 0. That line is
character-for-character what a genuine "nobody can be named here" answer looks
like, so a reader who trusted the tool concluded every branch in the repo was
unowned.

A bigger number would only move the date this happens again, so the read now
PAGES TO THE END (`gh api graphql --paginate --slurp`, cursor variable named
`$endCursor` because gh injects into that name and no other) and asks the
repository for its own `totalCount` on the same connection. "Did the read
finish?" is then two measured numbers compared, not a threshold anybody picked:
rows == totalCount, or a refusal naming both counts and the shortfall.

The fail-closed backstop survives and gets sharper. A short read, a page that
came back twice (what a broken cursor looks like), a count that disagrees with
itself between pages, and a GraphQL `errors[]` at HTTP 200 are all refusals --
carrying a marker `main` matches on, so it prints no rows and exits 2 rather
than tallying a refusal into the shape of data. The other PR-list problems still
print their rows, with the one reason carried into the summary line.

Also, found while verifying: `gh repo view` has NO `--repo` flag -- it takes the
repository positionally, and the flag form exits 1 with `unknown flag: --repo`.
`default_branch()` built the flag form, so the authoritative lookup was
unreachable on every `--repo` run, the answer silently came from `origin/HEAD`
(a clone-time cache), and the first-commit signal was withheld for every branch
while the message blamed a remote that was never asked. Measured on
`tracebloc/backend`: 88 of 108 attributed with the paging fix alone, 102 of 108
with both.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
@LukasWodka

Copy link
Copy Markdown
Contributor Author

bugbot run

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit a8c0546. Configure here.

@tracebloc-release-train tracebloc-release-train Bot added gate-nudge Toggled by the release train to (re-)fire the fr-gate and removed gate-nudge Toggled by the release train to (re-)fire the fr-gate labels Sep 1, 2026
@tracebloc-release-train
tracebloc-release-train Bot merged commit 885df27 into staging Sep 1, 2026
31 of 39 checks passed
@tracebloc-release-train
tracebloc-release-train Bot deleted the release-train/to-staging branch September 1, 2026 16:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant