Part of #5930, the flow alarm: nothing approved sits still. Today the steward's probe computes this outside the forge; the forge must own it.
Done:
[ ] src/flow/alarm_stuck.ts exports one function taking the app (or db) and now, answering { fired: boolean, since?: string, says: string }. It fires when an approved, open pull has had no event (run, check, review, push, landing) for 30 min. says is one plain sentence, e.g. "#3885 has sat approved for 5 days; its last event was a red check at 10:12." It names the oldest such pull and its last event.
[ ] A test covers fired and not fired: an approved pull with an event 31 min ago fires and names it; one with an event 29 min ago does not; a held or unapproved pull never counts. It fails without the file.
[ ] Nothing draws it yet; the banner is a separate issue. Keep to this one file and its test, so the four alarms land in parallel without touching each other.
Files: src/flow/alarm_stuck.ts, a test under test/.
Before pushing: bun x tsc --noEmit and the tests you add or touch; bun scripts/design-lint.ts if src/web/ changes. Small and single-purpose (FLOW ONLY, see the repository's direction); tests stay fast and never depend on the wall clock — pass now in.
Part of #5930, the flow alarm: nothing approved sits still. Today the steward's probe computes this outside the forge; the forge must own it.
Done:
now, answering{ fired: boolean, since?: string, says: string }. It fires when an approved, open pull has had no event (run, check, review, push, landing) for 30 min.saysis one plain sentence, e.g. "#3885 has sat approved for 5 days; its last event was a red check at 10:12." It names the oldest such pull and its last event.Files: src/flow/alarm_stuck.ts, a test under test/.
Before pushing:
bun x tsc --noEmitand the tests you add or touch;bun scripts/design-lint.tsif src/web/ changes. Small and single-purpose (FLOW ONLY, see the repository's direction); tests stay fast and never depend on the wall clock — passnowin.