# fix\(server\): wake PR watches when no checks report · PR \#23863 · pingdotgg/t3code

[View on GitCafe](https://git.cafe/pingdotgg/t3code/pull/23863)

Repository: [pingdotgg/t3code](https://git.cafe/pingdotgg/t3code)

Visibility: public

State: merged

Source repository: pingdotgg/t3code

Source branch: fix/issue-23701

Target branch: main

Head commit: 048e855e9e76bd8b4562cf2be92842187a022995

[Discussion and reviews](https://git.cafe/pingdotgg/t3code/pull/23863)

[Commits](https://git.cafe/pingdotgg/t3code/pull/23863/commits?format=markdown)

[Files changed](https://git.cafe/pingdotgg/t3code/pull/23863/diffs?format=markdown)

## Description

---

## Problem

A watched GitHub pull request whose workflow skips its changes can wait forever without waking the agent. A docs-only pull request with `pull_request.paths-ignore: ["docs/**"]` reproduces this: no checks appear, and the watch reports no update.

## Change

Report once when a GitHub head has remained without checks beyond a grace period. Keep the existing read cadence, preserve suppression after checks have been observed, and retain notification state across restarts and rewatching. Older watch records are handled conservatively. Other forge providers retain their existing behavior.

## Scope and approval

Fixes #23701

Triaged bug via the automated issue flow; [GitCafe issue #23701](https://git.cafe/pingdotgg/t3code/issues/23701).

## Verification

- The regression fails on main with `expected +0 to equal 1` and passes with this fix. It verifies one notice, no CI-success claim, and suppression after pending checks were observed. Added regressions cover legacy saved watches and providers that do not report checks.
- Focused server suites, scoped lint and server/contracts typechecks passed during implementation. After merging current main, all three watch suites passed: 105 tests in `runtimeLayer.test.ts`, `pullRequestWatch.test.ts`, and `pullRequestWatch.forgejoComments.test.ts`.
- [Runtime evidence report](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/09391e35bfd0378d/evidence-report.md) and [sanitized transcript](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/ab65a1debafa0e78/runtime-transcript.json): the real dev server read a docs-only GitHub head with zero checks and emitted exactly one `updated` notice after 18 minutes. The notice made no passing-check claim. Its persisted clock cleared; rewatching and restarting retained that state without another notice.
- The eligibility floor is 15 minutes; unchanged GitHub fingerprints normally defer the next detail read to about 30 minutes. A dev reload preceded the captured notice, so this run does not establish uninterrupted quiet-read timing.
- Server-only change with no changed render path: [visual fixture matrix is empty](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/ff4b1ad2394d6aba/fixture-matrix.json). No screenshots are applicable. No platform-specific code changed.

Implemented with Claude Opus 5.5 in the Claude Agent harness; triage, review, and proof with GPT-6.1-Sol in the Codex harness.

<details><summary>Review and audit log</summary>

Read-only audit of [1fbb142](https://github.com/pingdotgg/t3code/commit/1fbb1426134e533d482bd930e865ff3058528f66) against refreshed merge-base [2c8bdf6](https://github.com/pingdotgg/t3code/commit/2c8bdf6177159d8fcd12a897d7c9311f6eb93237). Two fresh parallel reviewers inspected the complete diff and relevant callers/contracts. No code changes, installation, server/browser launch, or suite reruns in this round.

- **Reviewer A — Codex / gpt-6.1-sol — VERDICT: MERGE.** No blocking defect. Read full diff and traced persistence, compatibility, check evaluation, provider gating, and delivery. Reports passing in-memory diagnostics for threshold boundaries, late success/failure, serialized state, schema compatibility and cadence; these are reviewer diagnostics, not a committed regression or the supplied runtime transcript. Suggested coverage: late checks and no-checks-specific restart/rewatch.
- **Reviewer B — Claude Agent / claude-opus-5-5 — VERDICT: MERGE.** No blocking defect. Independently traced full seven-file diff; no tests run. Optional nullable contract field preserves older records/servers; rewatch copies both timestamp and cleared null state. Legacy seen heads intentionally stay silent until the next push. Other providers do not enter the new GitHub-only branch.
- **Timing/product decision:** 15 minutes is an eligibility floor from the first empty head read, not a delivery guarantee. Unchanged GitHub fingerprints normally defer detail rereads to about 30 minutes. Both reviewers consider this conservative informational behavior mergeable; the notice reports actual elapsed minutes. The runtime test exercises the null-fingerprint/10-minute reread path, rather than uninterrupted GitHub fingerprint timing.
- **Late checks:** Code inspection indicates a second real success/failure update: no-checks clears only the clock, leaves `passed` false, and keeps watching. The existing check-evaluation path remains active. The notice says “No checks have reported” and uses `updated`, never claims CI passed.
- **All-green case:** This PR does **not** fix a separate report of checks existing and all turning green without waking. The passing-check decision and wake-dispatch path are unchanged; normal pending-to-green still produces `checks-passed`. That report requires a separate reproduction.
- **Test quality:** New runtime coverage uses `TestClock.adjust`; pure evaluator tests receive explicit time. No wall-clock waits. Existing GitCafe head status reports 35 successful checks; the author's 105-test result was not independently rerun here.

**Proof audit** — [runtime transcript](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/ab65a1debafa0e78/runtime-transcript.json) and [evidence report](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/09391e35bfd0378d/evidence-report.md) fetched with curl. Line references below refer to the original pretty-printed JSON transcript.

| Required row | Evidence | Status |
| --- | --- | --- |
| Zero-checks head notifies once | Transcript lines 60–128: persisted clock, cleared clock, one `updated` notice without pass wording; lines 2–3 record one unique notice. [runtimeLayer.test.ts](https://github.com/pingdotgg/t3code/blob/1fbb1426134e533d482bd930e865ff3058528f66/apps/server/src/orchestration-v2/runtimeLayer.test.ts#L2750), test **“wakes once when no checks report on the head, without saying they passed”**. | Proven by transcript and test |
| Checks observed then none → no notice | Same named runtime test, lines 2834–2842: another head observed pending, then empty across an hour of TestClock sweeps; total notice count stays one. | Proven by test |
| Late checks after the notice | No supplied transcript or committed test takes the already-notified **same head** through late pending checks to success/failure and asserts the second wake. Reviewer A reports passing in-memory diagnostics and both reviewers traced the correct path, but no reproducible proof artifact was supplied for that sequence. | Unproven by transcript or named committed test |
| Restart keeps state | Transcript lines 133–168 retain null on ended watch and rewatch; lines 183–205 record restart, healthy sweep, restored null clock and notice count one. Proves startup/sweep survival; does not separately instrument a successful post-restart host reread. | Proven by transcript within stated limit |
| Legacy watch record | [pullRequestWatch.test.ts](https://github.com/pingdotgg/t3code/blob/1fbb1426134e533d482bd930e865ff3058528f66/apps/server/src/orchestration-v2/pullRequestWatch.test.ts#L280), test **“does not tell a watch saved before the no-checks clock that its seen head has none”**: seen head remains silent, pushed head notifies once. | Proven by test |
| Non-GitHub forge unchanged | [provider exclusion test](https://github.com/pingdotgg/t3code/blob/1fbb1426134e533d482bd930e865ff3058528f66/apps/server/src/orchestration-v2/pullRequestWatch.test.ts#L288), test **“never reports missing checks on a host whose checks it cannot read”**: Azure stays silent over an hour; existing Forgejo boundary test remains, and runtime logic is explicitly GitHub-only. | Proven by test |

**SHA provenance:** Transcript lines 170–171 and 183–184 identify implementation [171fec9](https://github.com/pingdotgg/t3code/commit/171fec973b8029810b4a4a56491314f82a6cddbc), rather than current head. Compared all added/removed diff lines from evidence base [ef92441](https://github.com/pingdotgg/t3code/commit/ef92441313) to that implementation against the current PR's merge-base-to-head diff: the only difference is `export const PULL_REQUEST_WATCH_NO_CHECKS_MS` becoming private `const`; no runtime semantic drift in the patch. Transcript's `6074dd4…` is the watched scratch PR head, not the implementation SHA. The reload immediately before the captured notice prevents any claim of uninterrupted quiet-fingerprint delivery timing; the red result is supplied evidence, not independently rerun.

**Audit VERDICT: INSUFFICIENT — late checks after the notice.** This is a proof gap, not a confirmed implementation defect.

Models/harnesses: reviewer A gpt-6.1-sol / Codex; reviewer B claude-opus-5-5 / Claude Agent; proof audit and reconciliation gpt-6.1-sol / Codex. No reviewer fallback required.

</details>

## Round 2

Added **“wakes for checks that complete after the no-checks notice on the same head”** in `runtimeLayer.test.ts`. It passes: the same head emits one no-checks notice after TestClock advances, then the normal checks-passed update when late checks complete, with no duplicate no-checks notice. This closes the audit’s missing late-checks row. All three watch suites pass (106 tests); scoped lint passes.

Test addition: GPT-6.1-Sol / Codex; read-only review: Claude Opus 5.5 / Claude Agent.

