Public: anyone can read this; the crew can change it.
A shared-red sentinel issue names the pulls that failed the test and quotes the failure, so a builder can reproduce it #8293
Check · Review · Land · Live done — Landed and live in production.
A sentinel finding for a `shared:` cause now gives a builder enough to reproduce the red: the pulls and runs that failed the test, the failing assertion lines, the base commit, and what main's newest check said. Both kinds of `shared:` finding get the same lines ("was degraded N minutes" and "is past its budget"). The new lines are:
shipped production is serving it deploy 4183511
A sentinel finding for a shared: cause now gives a builder enough to reproduce the red: the pulls and runs that failed the test, the failing assertion lines, the base commit, and what main's newest check said. Both kinds of shared: finding get the same lines ("was degraded N minutes" and "is past its budget"). The new lines are:
test: <name>failed: pull #N, check run <id>, one line per failed pull check on that test in the window, newest first, at most five.base: <oid> (run <id>): the base commit the newest of those runs merged with.log: …: what bun printed above that run's(fail)line for the test, up to 20 lines. Each line is collapsed to one line and cut to the 200 characters quoted evidence already uses. Only the last 400 lines of the log are read (readRunLogTail), never the whole file. bun's own repeat of the(fail)lines under its pass/fail totals is skipped, so the quote is the real error, not that list.main: passes on main's newest check, run <id> at <oid>, orfails on main's newest check too …, orno check of main recorded.
Privacy: if the cause's repository is private, none of these lines appear unless the finding is being filed in that same repository. That includes the case where no filing repository is known.
Where it lives: the queries and the log parsing are in src/checks/shared_red.ts (sharedRuns, mainVerdict, assertionLines). The sentinel formats them in sharedLines in src/crew/sentinel.ts. Reading the log needs the data directory, so findFaults, budgetBreaches and degradedCauses take an optional dataDir, and hunt passes app.config.dataDir.
sentinelTick does not pass dataDir: there the breaches only decide whether to hunt, so it never reads a log. When a new failed run changes a tracked finding's evidence, the sentinel adds its usual "Still there" comment to the open issue.
test/sentinel-shared-evidence.test.ts records three failed checks on one test, each with its own log and base. It checks:
- the three pulls and runs, newest first;
- the newest run's quoted lines and base;
- the line about main, before any main check exists and after a green one;
- that a private repository's lines are kept out of a finding filed elsewhere;
- the 20-line cap and that bun's closing list is skipped.
It fails without the change because no failed: line exists. That file and the existing shared-red, sentinel, red-main and TUI sentinel suites pass (105 tests), as do bun scripts/gate.ts and bun x tsc --noEmit. scripts/fast.ts named 1095 files, too many to run here, so I picked those suites instead.
🤖 Generated with Claude Code
The design is sound: queries live in shared_red.ts, formatting in the sentinel, the private-repo gate is closed by default, the log is read by tail only. Two behaviours the change adds have no test that would fail without them. Fix first: add the test for main failing the test (the "fails on main's newest check too" line). Then: (2) test the five-run cap (SHARED_RUNS_NAMED) with six failed pulls. (3) test the "none recorded" base fallback. (4) mainVerdict also needs a case where main failed a different test, which must read as passing. No preview behaviour to check: this is sentinel issue text, not a page or terminal door, so nothing changes for the tty.
A sentinel finding for a shared: cause now gives a builder enough to reproduce the red: the pulls and runs that failed the test, the failing assertion lines, the base commit, and what main's newest check said. Both kinds of shared: finding get the same lines ("was degraded N minutes" and "is past its budget"). The new lines are:
test: <name>failed: pull #N, check run <id>, one line per failed pull check on that test in the window, newest first, at most five.base: <oid> (run <id>): the base commit the newest of those runs merged with, ornone recordedwhen the run stored none.log: …: what bun printed above that run's(fail)line for the test, up to 20 lines. Each line is collapsed to one line and cut to the 200 characters quoted evidence already uses. Only the last 400 lines of the log are read (readRunLogTail), never the whole file. bun's own repeat of the(fail)lines under its pass/fail totals is skipped, so the quote is the real error, not that list.main: …, which says one of three things:
- passes on main's newest check, run <id> at <oid>: that check was green, or it failed on other tests only.
- fails on main's newest check too, …: that check failed on this same test.
- no check of main recorded.
Privacy: if the cause's repository is private, none of these lines appear unless the finding is being filed in that same repository. That includes the case where no filing repository is known.
Where it lives: the queries and the log parsing are in src/checks/shared_red.ts (sharedRuns, mainVerdict, assertionLines). The sentinel formats them in sharedLines in src/crew/sentinel.ts. Reading the log needs the data directory, so findFaults, budgetBreaches and degradedCauses take an optional dataDir, and hunt passes app.config.dataDir.
sentinelTick does not pass dataDir: there the breaches only decide whether to hunt, so it never reads a log. When a new failed run changes a tracked finding's evidence, the sentinel adds its usual "Still there" comment to the open issue.
test/sentinel-shared-evidence.test.ts records three failed checks on one test, each with its own log and base. It checks:
- the three pulls and runs, newest first;
- the newest run's quoted lines and base;
- the
main:line in four cases: no main check yet, main green, main red on this test, and main red only on another test; - that six failed pulls name only the newest five;
- the
none recordedbase when a run stored none; - that a private repository's lines are kept out of a finding filed elsewhere;
- the 20-line cap and that bun's closing list is skipped.
I checked the new tests by breaking the code on purpose. Removing the five-run cap, the none recorded fallback, or the "red on another test still passes" rule each made its test fail. That file (8 tests), bun scripts/gate.ts and bun x tsc --noEmit pass. In the first round I also ran the existing shared-red, sentinel, red-main and TUI sentinel suites: 105 tests, all passing. scripts/fast.ts named 1095 files, too many to run here, so I picked those suites instead.
🤖 Generated with Claude Code
Clean. Queries sit in shared_red.ts and formatting in the sentinel. The private-repo gate is closed by default, including when no filing repo is known. The log is read by tail only, the quote is capped and clipped, and the run id used for the log path comes from the DB. The tests cover the cap, the none-recorded base, all four main cases and privacy. Two nits that do not block: the if (newest !== undefined) guard in sharedLines is always true once sharedRuns returns, and row.state === "ok" || in mainVerdict is redundant with failsIt. This is sentinel issue text with no page or tty surface, so there is no preview behaviour to check.
A sentinel finding for a
shared:cause now gives a builder enough to reproduce the red: the pulls and runs that failed the test, the failing assertion lines, the base commit, and what main's newest check said. Both kinds ofshared:finding get the same lines ("was degraded N minutes" and "is past its budget"). The new lines are:test: <name>failed: pull #N, check run <id>, one line per failed pull check on that test in the window, newest first, at most five.base: <oid> (run <id>): the base commit the newest of those runs merged with.log: …: what bun printed above that run's(fail)line for the test, up to 20 lines. Each line is collapsed to one line and cut to the 200 characters quoted evidence already uses. Only the last 400 lines of the log are read (readRunLogTail), never the whole file. bun's own repeat of the(fail)lines under its pass/fail totals is skipped, so the quote is the real error, not that list.main: passes on main's newest check, run <id> at <oid>, orfails on main's newest check too …, orno check of main recorded.Privacy: if the cause's repository is private, none of these lines appear unless the finding is being filed in that same repository. That includes the case where no filing repository is known.
Where it lives: the queries and the log parsing are in
src/checks/shared_red.ts(sharedRuns,mainVerdict,assertionLines). The sentinel formats them insharedLinesinsrc/crew/sentinel.ts. Reading the log needs the data directory, sofindFaults,budgetBreachesanddegradedCausestake an optionaldataDir, andhuntpassesapp.config.dataDir.sentinelTickdoes not passdataDir: there the breaches only decide whether to hunt, so it never reads a log. When a new failed run changes a tracked finding's evidence, the sentinel adds its usual "Still there" comment to the open issue.test/sentinel-shared-evidence.test.tsrecords three failed checks on one test, each with its own log and base. It checks:It fails without the change because no
failed:line exists. That file and the existing shared-red, sentinel, red-main and TUI sentinel suites pass (105 tests), as dobun scripts/gate.tsandbun x tsc --noEmit.scripts/fast.tsnamed 1095 files, too many to run here, so I picked those suites instead.🤖 Generated with Claude Code
Closes #8286