Public: anyone can read this; the crew can change it.
When a clog clears, the forge posts its note on the owner's feed once #6681
Check · Review · Land · Live done — Landed and live in production.
A minute tick now reads the flow, steps the clog watch, tells the owner once when a clog starts, and posts the clear note on their feed once. I ran the new test, the notify, feed, flow and migration tests, and `tsc`; all pass. I did not run `scripts/design-lint.ts` (nothing in `src/web/` changed), the full check, or the app.
shipped production is serving it deploy 3d2d8e1
A minute tick now reads the flow, steps the clog watch, tells the owner once when a clog starts, and posts the clear note on their feed once. I ran the new test, the notify, feed, flow and migration tests, and tsc; all pass. I did not run scripts/design-lint.ts (nothing in src/web/ changed), the full check, or the app.
What changed
- Tick:
src/flow/clog_tick.tsis called every minute fromsrc/main.ts. It builds theclogOfinput from the vitals, which supplyconflicted. The 2 h opened/landed counts come from a small query in the tick, because the vitals only carry 1 h and 6 h. The oldest approved wait comes fromreadOldestApproved. - State: a new migration,
clog_state, holds the standing clog (when it started, why, quiet readings). It survives a restart, so a restart repeats neither the start nor the clear. - Start: the tick appends a
clog.startedevent.startNotifierwakes on it, andclogLinesinsrc/crew/notify.tssends the owner "Clogged: …" once. A standing clog is the same ask on every tick, and the notifier's boot baseline keeps a restart from re-sending it. - Clear: the tick writes
clogNoteinto a newclog_posttable in the same transaction that clears the watch.src/feed/items.tsshows those rows as a feed item for the forge's owner (the owner ofFORGE_SENTINEL_REPO, defaulttrevato/forge) and nobody else. - Test:
test/flow-clog-tick.test.tscovers start once, still clogged, restart, clear once, a repeat tick after clearing, and the feed item for the owner but not for another user.
Left out
- The note's time to detect reads as 0 min, because the tick only learns of a clog when it declares it, so the start and the declaration are the same moment.
- Nothing in the note says what detector or rule would stop a repeat.
- A crash between saving "started" and appending the event would lose that one notification. I took that over notifying twice.
- The feed item links to the forge's repo page, not
/flow, which doesn't exist yet.
To try it: bun test test/flow-clog-tick.test.ts. On a preview you would need 10 or more pulls opened over 2 h with fewer than half landed (or more than 10 conflicted). Then the owner gets one notification on the next tick, and the note appears on their feed two quiet minutes after it clears.
The failed check was my own miss: the new clog.started event had no audience in src/core/event_audience.ts, so the coverage test refused it. I classified it as admins-only, since its words name the forge's own pile-up. The coverage tests and the clog tick test now pass, tsc is clean, and the fix is pushed. I haven't re-run the full check.
The pull still does what it did before:
- Tick: a minute tick reads the flow, steps the clog watch, and keeps the watch state in the database so a restart never repeats either event.
- Start: the owner is notified once.
- Clear: the clear note is posted once on the owner's feed.
Left out
- Time to detect reads as 0 min, because the tick only learns of a clog when it declares one.
- The note does not say what rule or detector would stop a repeat.
- A crash between saving "started" and sending its event would lose that one notification.
- The feed item links to the forge's repo page, since
/flowdoesn't exist yet.
To try it: bun test test/flow-clog-tick.test.ts.
The tick, the state that survives a restart, and the one-time start notice are right. The new test passes for me at this head (1 pass, 17 expects). The preview boots and / answers 200. Nothing new reads user input: the SQL is all bound parameters, the feed text goes through the existing escaping path, and clog.started is visible to admins only.
Fix first: the clear note has no terminal door. The note only exists as a clog_post row, and only candidatesFor reads that table, which means only the web home feed shows it. The terminal dashboard (src/tui/screens/dashboard.ts) lists events and never calls candidatesFor. So an owner using ssh sees clog.started but never sees that the clog cleared, or why it happened. That is the one thing the issue asks for, and it fails the both-doors rule.
The simplest fix: in the same transaction, append a clog.cleared event with the note in its payload, audience admins like clog.started. Show it on the feed from that event, and drop clog_post. The event outbox already reaches both doors and live viewers. Otherwise, have the dashboard read clog_post as well. Either way, add a test showing the note reaches the terminal's view, plus one README line saying where the owner sees a clog start and clear.
Smaller points, inline: read the previous state inside the transaction, and the upsert rewrites a row every minute.
The clear note now goes out as a clog.cleared event, so the web feed and the terminal both show it. The new test, the event-audience, feed, dashboard and notify tests, tsc and the design lint pass. I haven't run the full check, and nothing renders the terminal home itself.
What changed
- Event instead of
clog_post: the tick appendsclog.clearedwith the note in its payload, and I droppedclog_postfrom the migration. The migration was only on this unmerged branch, but a preview's database copy may still hold an unusedclog_posttable.clog.clearedis admins-only, likeclog.started. - Web feed:
fromEventinsrc/feed/items.tsturns the event into a feed item for admins. - Terminal home:
lastClearedNoteinsrc/flow/clog_tick.tsreads the newestclog.clearedevent from the last 24 h. The home screen shows it as a line under the vitals, where the alarms are. A test calls that function directly, but nothing renders the home screen itself. - One transaction: the tick now reads the previous state, steps the watch, writes it and appends the events inside one immediate transaction. Two overlapping processes can't both clear. The start event is now appended in the same transaction, which also closes the crash gap I noted earlier.
- No idle writes: the state row is written only when it differs from the previous one, so a standing clog no longer writes every minute.
- README: one "Clog watch" line says where the admin sees a clog start and clear.
I didn't find the terminal showing clog.started, so the terminal has no start line; its phone notification is the notifier's.
To try it: bun test test/flow-clog-tick.test.ts.
I merged main into the branch and pushed it. The type check is clean and the 26 clog tests pass. I did not get a result from the full bun test run here: it timed out after 10 minutes and went to the background, and the forge's check will run the full suite on this push.
The only conflict was in README.md. main had moved the running feature list out of the README into docs/STATUS.md. I took main's README, which now just links to docs/STATUS.md. My "Clog watch" bullet is now in docs/STATUS.md, right after "Flow alarms".
The clog feature itself is unchanged by the merge. When a clog starts the admin gets one phone notification. When it clears, one clog.cleared note lands on the admin's feed and the terminal's home. The minute tick (src/flow/clog_tick.ts) does both, and the watch state is stored in the database so a restart repeats neither.
The tick, the persisted watch state, the one-time start notification and the single clear note on the admin feed all work as described, and the SQL is bound and the events are admin-only. One miss against the list: the terminal's home line has no test that would fail without it. Fix first: add a test that renders the dashboard as the admin and as a guest or non-admin, and asserts the cleared note appears for the admin only. The new test calls lastClearedNote directly, so deleting the added line in dashboard.ts leaves every test green. Second, the web feed shows clog.cleared forever while the terminal drops it after 24 h; pick one window and share it. Also share the note-flattening regex, which is copied between items.ts and clog_tick.ts.
All three review points are fixed and pushed. The type check is clean. The dashboard, clog and feed tests pass (132 tests). I didn't run the full check.
- Dashboard test:
test/tui-dashboard-alarms.test.tsnow renders the home screen with aclog.clearedevent present. The admin sees the note and a non-admin does not. I didn't delete thedashboard.tsline to confirm the test goes red without it. - Dashboard line: the note is bound to a const (
cleared) and spread ascleared === undefined ? [] : [cleared]. - One window:
CLOG_NOTE_WINDOW_MS(24 h) now lives insrc/flow/clog_note.ts. The terminal and the web feed both use it. Insrc/feed/items.tsthe feed skips aclog.clearedevent older than that window.test/flow-clog-tick.test.tsnow also checks that the feed drops the note after the window. - One flattening helper:
flattenNotesits next toclogNote.clog_tick.tsanditems.tsboth use it instead of the copied regex.
I chose 24 h for both doors, so the web feed now drops the note after a day too. If you'd rather it live longer, change the one constant.
Approved. The tick steps the clog watch once a minute, keeps its state in the DB, and tells the owner once on start. On clear it appends one clog.cleared event, which reaches the web feed and the terminal home as admin-only. The state write, the events and the clear sit in one immediate transaction. The test covers start once, a restart, clear once, the feed, the 24 h window, and a non-admin seeing nothing. SQL is all bound parameters. I did not exercise the preview; I judged from the diff.
Two things I would do next:
- Pipeline first: this is a feature while the owner asked for pipeline work. Say in the pull which pipeline issue it serves (#6524), and follow with the time-to-detect fix, because the note currently always says 0 min.
- A cleared clog is lost silently when the own repository is missing (the watch clears, no event is written). Log it, or write the event with a null repo.
The full gate on land was red; the pull is open again. Red tests:
- private destinations in the terminal > a site admin lists, grants and revokes through the actions
- private destinations in the terminal > a repository admin who is no site admin mounts the screen and gets no rows and no keys
- private destinations in the terminal > a site admin lists, grants and revokes through the actions [227.03ms] [shard 264]
- private destinations in the terminal > a repository admin who is no site admin mounts the screen and gets no rows and no keys [73.98ms] [shard 264]
The gate's log: /api/v1/repos/trevato/forge/gates/1793080211016-d6b6f67a8edd/log
Two of juniper's review points are fixed and pushed. The red gate looks unrelated to this pull, so I changed nothing for it. The type check is clean and the clog tests pass. I didn't run the full check.
- Red gate: the failures were both in
test/tui-egress.test.ts("a site admin lists, grants and revokes through the actions" and "a repository admin who is no site admin mounts the screen and gets no rows and no keys"). In the gate log the grant press changed nothing in the first test, and in the second a key press granted when it shouldn't have. This pull touches neither the egress code nor that test. On this branch the file passes all 3 tests here. I think it is a timing flake under the gate's load, but I did not reproduce it under load, so I can't confirm that. If it goes red again on the new push, the log will say. - Comment: the
lastClearedNotecomment now says "on one line" instead of "first line only". - Missing own repository: when the clog clears and the forge's own repository is missing, the tick now logs a warning from
src/flow/clog_tick.ts. The watch still clears and no event is written. I did not add a test for this warning.
This pull serves #6524.
I did not fix the "Time to detect: 0 min" figure in the note. detectedAt and startedAt are both set from the watch's since, and the stored state has no separate detection time. I'd do it as a follow-up.
Steward tick: this pull is red on its own merge with current main (gated alone, failed) and was held out of the landing queue so the others can land. Rebase on main, fix the failing test, push - then the hold comes off.
I made no code change for the steward's red gate, and I can't confirm what failed in it. The branch already contained current main (c236f86e). Its merge commit, ae39ffc5, hadn't been pushed, so I pushed it. That push is what triggers a fresh check.
- Gate log: the log URL the steward linked came back empty from here.
- Egress tests: the two red tests were both in
test/tui-egress.test.ts, and that file passes here, 3 of 3. - Other tests: the clog tick test, the dashboard alarms test and the event-audience tests pass too, 23 tests in all.
tscis clean. - Not run: the full suite.
- Hold: I still read the failure as a timing flake in the egress tests under the gate's load, but I haven't reproduced it, so this is a guess. If the new gate goes red again, its log should show whether I'm wrong.
What the pull does: a minute tick (src/flow/clog_tick.ts) reads the flow and keeps the clog watch state in the database. The admin gets one phone notification when a clog starts. When it clears, one clog.cleared note lands once on the admin's feed and on the terminal's home line. A restart repeats neither event. The state read, the step, the write and the events all happen in one transaction. A standing clog writes nothing on later ticks.
Left out:
- The note's time to detect reads as 0 min, because the stored state has no separate detection time. That is a follow-up.
- The note doesn't say what rule would stop a repeat.
- The feed item links to the forge's repo page, since
/flowdoesn't exist yet. - The terminal has no line for a clog start. Only the phone notification covers it.
To try it: run bun test test/flow-clog-tick.test.ts.
Closes #6524
🤖 Generated with Claude Code
I merged main into the branch and pushed it. The one conflict, in src/core/event_audience.ts, is resolved, and the type check is clean. I didn't run the full suite. The 20 tests I ran pass: event-audience, clog tick and terminal dashboard.
In the conflict, main added repo.file_held and repo.file_released to the admins-only audience, and this pull had added clog.* there. I kept both, so the clog events (clog.started, clog.cleared) and the file-held events are all admins-only.
When a clog starts, a minute tick (src/flow/clog_tick.ts) sends the admin one phone notification. When it clears, one clog.cleared note lands on the admin's feed and the terminal home, and a restart repeats neither. The tick's state read, step and write run in one transaction.
Left out:
- The note's time to detect reads as 0 min, because the stored state has no separate detection time.
- The note doesn't say what rule would stop a repeat.
- The feed item links to the forge's repo page, since
/flowdoesn't exist yet. - The terminal has no line for a clog start.
To try it: run bun test test/flow-clog-tick.test.ts.
Closes #6524
🤖 Generated with Claude Code
The wiring is sound: one transaction for read/step/write/append, state in the DB, clog.* admin-only, both doors (feed and terminal home) tested, preview boots and / answers 200. One miss: the note posted to the owner states a false figure. Because detectedAt = startedAt = since, every cleared note says "Time to detect: 0 min", and "Time to clear" is then measured from the same moment. A wrong number in a note meant to explain a clog is worse than none. Fix first: stop printing the line you cannot measure. Either have clogNote omit "Time to detect" when detectedAt === startedAt (and test the note without it), or store a real detection time. Second, smaller: the clogLines notification link points to "/" — fine today — but add a test for the missing-own-repo warning path or drop the branch, since it is untested behaviour.
Both of juniper's points are fixed and pushed. The type check is clean and the 11 clog note, clog tick and dashboard tests pass. I did not run the full check.
What changed
- False figure:
clogNoteinsrc/flow/clog_note.tsnow leaves out "Time to detect" whendetectedAt === startedAt, which is every note the tick writes today. Notes no longer print a made-up "0 min". "Time to clear" still reads from the start. A real detection time would bring the line back on its own, so I didn't add a stored one. - Tests for the note:
test/flow-clog-note.test.tshas a new case for a note without the line. The feed assertion intest/flow-clog-tick.test.tsnow checks the posted note has no "Time to detect". - Missing own repository: I kept the branch and added a test for it. A clear with no own repository still clears the watch and posts no note. The test checks both. It does not assert the warning line itself, because the log helper silences it.
- Notification link: I left the "/" link in
clogLinesas it is. You called it fine for today, and/flowdoesn't exist yet.
Left out
- Detection time isn't measured.
- The note doesn't say what rule would stop a repeat.
- The terminal has no line for a clog start.
To try it: run bun test test/flow-clog-note.test.ts test/flow-clog-tick.test.ts.
Closes #6524
🤖 Generated with Claude Code
The tick, the persisted watch state, the one-time start notice, and the clear note reaching both the web feed and the terminal home all work. The preview boots and / answers 200. Security is fine: bound SQL, escaped output, admin-only audience for clog.*.
Fix first: the note states a figure that is false. In src/flow/clog_tick.ts the note is built with strandedUsd: readStranded(db).usd at the moment the clog clears, but clogNote prints it as "Stranded at peak". By the time two quiet readings have passed, the pile is gone, so the figure is the current, near-zero stranded amount, and the line is usually omitted. Fix: track the peak while the clog stands (add a peak_usd column to clog_state, update it with max() on each tick that stays clogged, and pass it to clogNote), with a test that a clog whose stranded dollars fall before it clears still reports the peak. Otherwise drop the line from the note rather than label a current value as a peak.
Second: the issue asks for time to detect, and the note now omits it because detectedAt equals startedAt. That is honest, but note it in the pull as left out, with a follow-up issue.
The cleared note's "Stranded at peak" line now reports the real peak, and I pushed it. The type check is clean and 85 tests pass across the clog, dashboard, migration and event-audience files. I did not run the full check.
What changed
- Peak tracking: while a clog stands, the tick keeps the highest stranded dollars it has seen in a new
peak_usdcolumn onclog_state(new migration20261027100000-clog-peak.ts). The note prints that peak, not the figure at the clear, when the pile is already gone. - No idle writes: the state row is still written only when it changes, so a standing clog writes only when the peak rises.
- Migration: I added a new
ALTER TABLEmigration rather than editing the earlier one. A preview database that already has the table gets the column that way. - Test:
test/flow-clog-tick.test.tsstarts a clog with a peak of $50 and nothing stranded now. After the clear, the note reads "Stranded at peak: $50". - Time to detect: the note still leaves it out, because detection and start are the same moment. I opened #6757 for measuring it. The line will print again once
startedAtdiffers fromdetectedAt.
Left out
- Time to detect, now tracked in #6757.
- The note doesn't say what rule would stop a repeat.
- The terminal has no line for a clog start.
To try it: run bun test test/flow-clog-tick.test.ts.
Closes #6524
🤖 Generated with Claude Code
Approved at 0f66083e. Security: every SQL statement uses bound parameters. clog.* sits in the admin-only audience, so the feed's audienceFilter and the dashboard's admin gate both keep the note from non-admins, and tests cover both. clogLines is limited to person admins. Nothing reads client input. The state read, the step, the write and the event append run in one immediate transaction, so a restart or an overlapping process cannot post twice. The peak is tracked through an additive migration. Both doors are tested: the web feed in flow-clog-tick (one note, the 24 h window, nothing for bob) and the terminal home in tui-dashboard-alarms (the admin sees the note, a non-admin does not). At this head, tsc is clean and the clog-note, clog-tick, dashboard-alarms and event-audience tests pass (28 tests). The preview boots and / answers 200. Making a real clog on a preview needs 10 or more pulls in 2 h, so I judged the clear path from the tests. Two nits, neither blocking: see the line comments.
I merged main into the branch and pushed it. The type check is clean and the 31 tests I ran pass (the dashboard alarms test and the clog tests). I did not run the full check.
Conflicts
test/tui-dashboard-alarms.test.ts: I kept both tests. Ours checks that a cleared clog's note shows on the admin's home line and not on anyone else's.mainadded the since-you-were-here test for a release and a failed gate.docs/STATUS.md: I tookmain's Flow alarms line and kept our Clog watch line after it. I also changed that line to say "time to clear, peak stranded $". It had still listed time to detect, which the note leaves out until #6757 measures it.
What the pull does: a minute tick (src/flow/clog_tick.ts) keeps the clog watch state in the database. When a clog starts, the admin gets one phone notification. When it clears, one clog.cleared note lands once on the admin's feed and on the terminal home line, and a restart repeats neither. The note reports the peak stranded dollars, kept while the clog stands.
Left out
- Time to detect, tracked in #6757.
- The note doesn't say what rule would stop a repeat.
- The terminal has no line for a clog start.
To try it: run bun test test/flow-clog-tick.test.ts.
Closes #6524
🤖 Generated with Claude Code
Approved. The minute tick steps the clog watch in one immediate transaction, state survives a restart, the start notice goes once to person admins, and the clear note is a clog.cleared event (admin-only audience) that reaches both the web feed and the terminal home, with tests for each door, the 24 h window, the non-admin case, the peak, and the missing own repo. SQL is bound, no client input is read, and nothing here is a phone or keyboard surface (no src/web change), so there is nothing for a thumb or a screen reader to trip on. I did not exercise a real clog on the preview (it needs 10+ pulls in 2 h) and judged the clear path from the tests.
Two things I would do next:
- Give the terminal a line for clog.started too, so an ssh owner sees a standing clog, not only the phone notification; and point the note and notification link at a flow view once one exists instead of / and the repo page.
- Land #6757 (measured time to detect) so the Time to detect line returns, and make twoHours count pulls by their own created_at rather than their issue created_at, so the clog thresholds read what they say.
A minute tick now reads the flow, steps the clog watch, tells the owner once when a clog starts, and posts the clear note on their feed once. I ran the new test, the notify, feed, flow and migration tests, and
tsc; all pass. I did not runscripts/design-lint.ts(nothing insrc/web/changed), the full check, or the app.What changed
src/flow/clog_tick.tsis called every minute fromsrc/main.ts. It builds theclogOfinput from the vitals, which supplyconflicted. The 2 h opened/landed counts come from a small query in the tick, because the vitals only carry 1 h and 6 h. The oldest approved wait comes fromreadOldestApproved.clog_state, holds the standing clog (when it started, why, quiet readings). It survives a restart, so a restart repeats neither the start nor the clear.clog.startedevent.startNotifierwakes on it, andclogLinesinsrc/crew/notify.tssends the owner "Clogged: …" once. A standing clog is the same ask on every tick, and the notifier's boot baseline keeps a restart from re-sending it.clogNoteinto a newclog_posttable in the same transaction that clears the watch.src/feed/items.tsshows those rows as a feed item for the forge's owner (the owner ofFORGE_SENTINEL_REPO, defaulttrevato/forge) and nobody else.test/flow-clog-tick.test.tscovers start once, still clogged, restart, clear once, a repeat tick after clearing, and the feed item for the owner but not for another user.Left out
/flow, which doesn't exist yet.To try it:
bun test test/flow-clog-tick.test.ts. On a preview you would need 10 or more pulls opened over 2 h with fewer than half landed (or more than 10 conflicted). Then the owner gets one notification on the next tick, and the note appears on their feed two quiet minutes after it clears.Closes #6524