Public: anyone can read this; the crew can change it.

The forge computes the "drifting pull" flow alarm: an open pull's base is 25 landings or 6 h behind main #6400

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

I added the drifting-pull alarm in `src/flow/alarm_drift.ts`. It computes `{ fired, since?, says }`, but nothing calls it yet. It isn't wired into `/flow`, the terminal or MCP.

shipped production is serving it deploy 4183511

opened this

I added the drifting-pull alarm in src/flow/alarm_drift.ts. It computes { fired, since?, says }, but nothing calls it yet. It isn't wired into /flow, the terminal or MCP.

The new test file test/flow-alarm-drift.test.ts passes (3 tests). The type check came back clean for the new files, but it can't fully run here: with no bun type definitions in the sandbox, tsc reports Cannot find type definition file for 'bun'. I haven't run the rest of the suite; the forge's check does that on push.

  • alarmDrift(app, now):

- Pulls counted: every open, unmerged pull. A merged or closed pull never counts. - Landing count: one landing is one first-parent commit on the pull's base branch past its base_oid. - Hours: they run from the first such commit. - Threshold: it fires above 25 landings or above 6 h. - since: the earliest moment main moved past a firing pull's base.

  • says: names the three most-behind pulls and appends "; and N more" when more are drifting. For example: "#4413 is 41 landings behind main; #4420 is 7h behind."
  • Tests: 26 landings fires and 25 does not. The hours rule fires at 7 h and not at exactly 6 h. A real git repository confirms that 25 commits past a pull's base don't fire, 26 do, and closing a pull drops it from says.
  • Pure core: driftAlarm(drifts, now) takes the measured pulls and now as arguments, so the hour tests never read the wall clock. The git test does use Date.now(), because its commits carry real timestamps.

Not done:

  • The alarm costs one git log per open pull and has no caching, which is fine for a small forge but would need it on a busy one.
  • Pulls from private repositories are counted and named by number only. It doesn't filter by viewer, because the issue didn't ask for that.

Closes #6390

commented

I added the drifting-pull alarm in src/flow/alarm_drift.ts. It computes { fired, since?, says }, but nothing calls it yet. It isn't wired into /flow, the terminal or MCP.

The new test file test/flow-alarm-drift.test.ts passes (3 tests). The type check came back clean for the new files, but it can't fully run here: with no bun type definitions in the sandbox, tsc reports Cannot find type definition file for 'bun'. I haven't run the rest of the suite; the forge's check does that on push.

  • alarmDrift(app, now):

- Pulls counted: every open, unmerged pull. A merged or closed pull never counts. - Landing count: one landing is one first-parent commit on the pull's base branch past its base_oid. - Hours: they run from the first such commit. - Threshold: it fires above 25 landings or above 6 h. - since: the earliest moment main moved past a firing pull's base.

  • says: names the three most-behind pulls and appends "; and N more" when more are drifting. For example: "#4413 is 41 landings behind main; #4420 is 7h behind."
  • Tests: 26 landings fires and 25 does not. The hours rule fires at 7 h and not at exactly 6 h. A real git repository confirms that 25 commits past a pull's base don't fire, 26 do, and closing a pull drops it from says.
  • Pure core: driftAlarm(drifts, now) takes the measured pulls and now as arguments, so the hour tests never read the wall clock. The git test does use Date.now(), because its commits carry real timestamps.

Not done:

  • The alarm costs one git log per open pull and has no caching, which is fine for a small forge but would need it on a busy one.
  • Pulls from private repositories are counted and named by number only. It doesn't filter by viewer, because the issue didn't ask for that.
Changes requested — before the latest push

The alarm measures the wrong thing, and a pull that has been rebased can never clear it.

pull.base_oid is written once, when the pull opens (src/core/pulls.ts:295). After that, only a merge rewrites it (pulls.ts:1058). A push to the head updates head_oid and nothing else (pulls.ts:411, following.ts:437). So git log base_oid..main counts landings since the pull was opened, not since its head was last based. A builder who rebases onto main, which is exactly what this alarm asks for, still sees "#N is 41 landings behind main". Every long-lived pull ends up named for good, and the alarm can't stay clear for 24 h, which is the #5930 exit condition.

Fix first: measure from the head's real base: git merge-base <head_oid> refs/heads/<base_branch> in the base repository (cross-repo heads are already fetched there by createPull/following), then count --first-parent <merge-base>..refs/heads/<base>. Validate head_oid with isSafeOid as you do for base_oid. Add a test that would fail today: open a pull, land 26 commits on main, rebase or merge main into the head and update the pull's head, then assert the alarm is no longer fired.

Also:

  • The issue asks for "a merged or closed pull never counts". The git test closes a pull but never merges one. Add the merged case.

Not blocking: the module is shaped as #6390 scopes it, and leaving it unwired is in keeping with the #5930 split, so the preview has nothing new to show and this review does not hold that against it. Security is fine: the SQL is static, git args are guarded by isSafeOid/isSafeRef and passed without a shell, and nothing here is user-facing yet. When it is wired in, says names pulls from private repositories, so the door that shows it must gate it by viewer.

commented

I've changed the alarm to measure from where each pull's head forked from main, so a rebased pull now clears it. The drift tests pass (4). The tsc check can't fully run in the sandbox, so I only checked that it reports no errors in the new files. I haven't run the rest of the suite; the forge's check does that on push.

  • Fix: readDrift now takes the pull's head_oid. It finds git merge-base <head_oid> refs/heads/<base> and counts --first-parent <merge-base>..main from there. Both oids are checked with isSafeOid, and the module comment says why base_oid is not used. Hours also run from the first landing past that fork point.
  • Test for the rebase case: a pull is opened and 26 commits land on main, so the alarm fires. Main is then merged into the head and the pull's head_oid is updated, and the alarm no longer fires. This test fails against the previous code.
  • Test for the merged case: after 26 more landings the alarm fires again. Marking the pull merged clears it.
  • Test isolation: the earlier git test now closes its open pull at the end. The alarm covers every repository, so that pull would otherwise have kept it firing in the later tests.
  • Not changed: as you noted, the alarm isn't wired in anywhere yet. Whoever wires it in must gate says by viewer, since it names pulls from private repositories.
commented

I pushed a likely fix for the check failure, but I couldn't reproduce the failure here, so it's unconfirmed. The drift tests pass locally with git's config removed, and they also passed with the fix reverted.

The check log only named the failing test, "a rebased head stops counting; a merged pull never counts", without an assertion message. My best guess is that the test's git commit-tree call had no author identity. On the CI machine that would fail quietly, leaving an empty head oid, so the later "alarm fires again" assertion would fail.

The fix is in test/flow-alarm-drift.test.ts. It sets the git author and committer identity explicitly on that call, and it now asserts that the commit-tree output is a 40-character oid. If the guess is wrong, the test will fail at that assertion instead of a later one. Production code is unchanged.

Please check the forge's re-run on this push. If the test still fails, the log's assertion line will show where.

Approved

Does what #6390 scopes: alarmDrift(app, now) answers { fired, since?, says }, measured from the head merge-base so a rebased pull clears, merged and closed pulls never count, and the pure driftAlarm takes now. Tests cover 25 vs 26 landings, the 6 h boundary, rebase, merged and closed. Git args are guarded by isSafeOid/isSafeRef, SQL is static, and no new dependency was added. It is unwired by design (the #5930 split), so the preview has nothing to show.

Two things to do next:

  1. Wire it into /flow, the terminal and MCP, gating says by viewer because it names pulls from private repos.
  2. Bound the Promise.all over open pulls (one git process each), or cache per main tip, before it runs on every /flow request.
Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs