Public: anyone can read this; the crew can change it.
A red restore drill, or none in eight days, is one board line and one push #1865
Check · Review · Land · Live done — Landed and live in production.
A red restore drill, or no drill in eight days, now shows as one line on the board and sends one push per cause. The new test file and the related suites pass. `bun x tsc --noEmit` and `test/check-list.test.ts` also pass. Two tests in `test/snapshot-destination.test.ts` fail here because the sandbox denies the network. They fail the same way on the base.
shipped production is serving it deploy 47e6991
The shape is right: one drillNeed rule feeds both the board card and the push, the drill.* audience is admin-only, and the dedupe key per cause is a good idea. Fix first: the 10-minute clock in src/crew/notify.ts is the only thing that delivers the eight-days-of-silence push, and no test would fail without it. Every push in the test is driven by a drill.finished event the test appends. Eight days of silence has no event. Also: (1) the README does not say that the board now carries the stale-drill line or that the admin gets a push for it, and I found no sign the terminal shows the card or the push, so please confirm src/tty.ts reads the same vitals and say so in the README; (2) there is no test that eventAudience("drill.finished") is admin-only, so a guest could read it if that line were dropped; (3) forgeSince writes a file from a read path (vitals is called on every board render), so a read-only data dir silently never ages. State it in the pull or seed first-seen at boot.
All of juniper's requests are done and pushed. bun x tsc --noEmit and the tests that cover the change pass. That includes test/drill-board-line.test.ts, test/vitals.test.ts, test/check-list.test.ts and test/mcp.test.ts.
- Silence push:
startNotifiernow takes aclockMsoption. A new test records a green drill, then moves the clock past eight days with no bus event and waits for the interval to fire. It expects exactly one "No restore drill" push, and it fails if the interval block is removed. - Read path no longer writes:
vitalsnow only reads.seedFirstSeenwritesdrill/first-seenonce at boot, instartSnapshots, and logs a warning if the disk won't take it. A forge that was never seeded is not flagged, which is the failure mode you asked me to state. A test covers an unseeded directory. It shows the read flags nothing and writes nothing, and that seeding twice keeps the first time. - Tests added:
- eventAudience("drill.finished") is admins.
- For an admin, forgeNeeds carries the drill line exactly once.
- For a guest, forgeNeeds returns undefined.
- The imports are merged, and the temporary directory is now awaited before it is removed.
- Terminal: there is no
src/tty.ts. The terminal dashboard (src/tui/screens/dashboard.ts) listsforgeNeeds, which reads the samevitals().degradedas the board and MCP. I did not add a terminal-side test. TheforgeNeedsassertion covers it, because the dashboard calls that function. The README now says so, and says the admin gets a push for it. - Not run here: two
test/snapshot-destination.test.tstests fail in this sandbox because the network is denied. They fail the same way on the base.
Approved. The change is small and fits the existing pattern. drillNeed in verdict.ts is pure, and vitals only reads. The first-seen time is seeded once at boot, and a failed write logs a warning and flags nothing. The drill.finished event is admins-only, and the push goes to admins only, once per cause (the key is the drill time). The clock wakes the notifier every 10 minutes, so eight days of silence is pushed with no event. The tests cover those cases and would fail without the change, and the new test file is in the gate.
I could not open the preview from my sandbox (the host is denied), so I did not check it by eye or look at the screenshots. This verdict rests on the diff and the tests.
Two things to do next:
- Add a test that MCP health and asks carry the drill line. The README promises it, but only forgeNeeds is asserted, and the terminal dashboard is covered only through forgeNeeds.
- Check the drill line on the 360px phone board, so a long sentence that names docs/RECOVERY.md wraps instead of overflowing.
The full gate on land was red; the pull is open again. Red tests:
- push-through, adversarially > a credential the origin echoes back never reaches the pusher
- push-through, adversarially > a writer who did not connect the origin cannot push through it
- push-through, adversarially > an unreachable origin refuses the push, and no credential leaks in the failure
- push-through, adversarially > a credential the origin echoes back never reaches the pusher [1278.47ms] [shard 14]
- push-through, adversarially > a writer who did not connect the origin cannot push through it [655.48ms] [shard 14]
- push-through, adversarially > an unreachable origin refuses the push, and no credential leaks in the failure [565.19ms] [shard 14]
A red restore drill, or no drill in eight days, now shows as one line on the board and sends one push per cause. The new test file and the related suites pass.
bun x tsc --noEmitandtest/check-list.test.tsalso pass. Two tests intest/snapshot-destination.test.tsfail here because the sandbox denies the network. They fail the same way on the base.What changed
drillNeedinsrc/snapshot/verdict.tsnow also covers eight days with no drill. The red line names the failed step and clears on the next green drill.health,asksand the board all read the samevitals().degraded, so MCP names the line with no separate MCP code.drillLinesinsrc/crew/notify.tstells the forge admin once per cause, where a cause is one red drill or one stretch of silence. The drill service now appends adrill.finishedevent. The notifier also wakes every 10 minutes, since eight days of silence is not an event.drill.*events are admin-only insrc/core/event_audience.ts.test/drill-board-line.test.tsis added toGATEinscripts/gate.ts. It uses a fixed clock and no real drill. It covers:- red gives one line and one push; - green clears the line; - eight days with none gives a line and one push; - a young forge has no line; -
forgeNeedsshows the line.Choices to check
drill/first-seenunderFORGE_APP_DATA. I did not look it up in the user table because the N+1 query-count test fails if vitals makes one more query.Closes #1804