# fix\(server\): wake PR watches when remaining CI checks finish · PR \#23904 · pingdotgg/t3code

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

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

Visibility: public

State: merged

Source repository: pingdotgg/t3code

Source branch: fix/issue-23712

Target branch: main

Head commit: 93fe829bdb5a0947f87ce38bb8b31c381748956f

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

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

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

## Description

---

## Problem

A PR watch could announce that its required checks passed while other CI jobs were still pending, then never wake the thread when those jobs finished on the same head. The watch stayed active, leaving unattended follow-up stalled.

## Change

Record the non-required checks still pending when the early required-check notification fires, in an optional `owedChecks` watch field. Once all checks finish successfully, send one final all-checks notification and clear that obligation. Later advisory jobs do not repeatedly wake the agent. Keep already-reported required gates separate so a pending check that becomes required still produces the required-check notification. Persist the owed names across sweeps and re-watching; old watch records continue to decode.

## Scope and approval

Fixes #23712
Fixes https://git.cafe/pingdotgg/t3code/issues/23712

Triaged bug via the automated issue flow. The change covers the server watch evaluator, its persistence and re-watch initialization, one backward-compatible optional contract field, and the regression test. Client rendering is unchanged.

## Verification

- Baseline regression: 1 failed, 16 passed; final completion returned no change. [Failing test output](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/bb2da8ca30769442/issue-23712-red.log).
- After merging current main: 22 watch tests passed; 14 runtime watch tests passed; legacy watch-contract test passed.
- Targeted lint and server typecheck passed.
- Two independent reviewers returned MERGE on the final logic, including the required-policy transition fix.
- The new policy-transition assertion failed before the fix and passes afterward; the final notification has count 2 and required true, followed by no duplicate.
- Existing watches without recorded owed checks retain their prior behavior for a transition already underway at upgrade.

Server-only evidence: the real development server and web client ran against isolated state. A temporary check-data fixture supplied required-success/other-pending, followed by all-success on the same PR head. The watch evaluator and notification path ran normally; sweeps were accelerated and reads logged. This verifies the runtime transition, rather than uncontrolled live GitHub CI timing. Temporary instrumentation was removed before submission.

| Observation | Baseline | Fixed |
| --- | --- | --- |
| Required succeeds while another check is pending | One required-check notification | One required-check notification |
| Remaining check succeeds on the same head | Completed snapshot read; no notification | One “All 2 checks passed” notification |
| Completed snapshot read again | No notification | No duplicate notification |

[Baseline runtime transcript](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/a10925b8a2866342/issue-23712-evidence.md) · [Raw runtime evidence and persisted notifications](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/8207e40624925e1d/issue-23712-evidence.json).

[Final-logic runtime transcript](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/5e37950504fff560/issue-23712-finalhead-evidence.md) · [Final-logic runtime rows and persisted notifications](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/79a781063e425df2/issue-23712-finalhead-evidence.json) · [Policy-transition failing test](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/d639292b2fa53c7a/issue-23712-bot-red.log). Runtime source was [75aef71418](https://github.com/pingdotgg/t3code/commit/75aef714186dc24e893a63545caf054dbb549eb7); the final merge from main changes no watch code. Both original completion and required-policy transition produced exactly two persisted notifications and no repeated notification.

No render paths changed; the visual fixture matrix is empty. Web, desktop, and mobile use this shared server behavior. No platform-specific surface requires separate verification.

Models: GPT-6.1 Sol and Claude Opus 5.5, through the Codex and Claude harnesses in T3 Code.


<details><summary>Proof audit</summary>

Read-only proof audit of [72b8789](https://github.com/pingdotgg/t3code/commit/72b87892b3585ff3c234679e6006043f0254c5a8). No code changes, installs, test reruns, reviews, or new runtime sessions. Named tests below were inspected at this head; passing execution is supplied evidence, not independently rerun. [Runtime transcript](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/5e37950504fff560/issue-23712-finalhead-evidence.md) and [raw rows and persisted notifications](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/79a781063e425df2/issue-23712-finalhead-evidence.json) were fetched with curl.

| Row | Status | Evidence and limit |
| --- | --- | --- |
| Required checks pass while others pending → one required notice, then one final notice | Proven by test and runtime transcript | **“wakes again when the other checks finish after the required ones passed”**, [pullRequestWatch.test.ts:138](https://github.com/pingdotgg/t3code/blob/72b87892b3585ff3c234679e6006043f0254c5a8/apps/server/src/orchestration-v2/pullRequestWatch.test.ts#L138), assertions at lines 146–157. Transcript `triage-23712-final-original-v2`, raw JSONL rows 7–10: one required notice, one “All 2 checks passed” notice, repeated read silent; persisted counts 1 → 2 → 2. |
| Later advisory jobs → no further wake | Proven by test for pending → success advisory churn | Same named test, [pullRequestWatch.test.ts:158](https://github.com/pingdotgg/t3code/blob/72b87892b3585ff3c234679e6006043f0254c5a8/apps/server/src/orchestration-v2/pullRequestWatch.test.ts#L158), lines 158–163. Does not establish suppression of a newly failing advisory job; failures remain reportable. |
| Pending check becomes required → required notice | Proven by test and runtime transcript | Same named test, [pullRequestWatch.test.ts:165](https://github.com/pingdotgg/t3code/blob/72b87892b3585ff3c234679e6006043f0254c5a8/apps/server/src/orchestration-v2/pullRequestWatch.test.ts#L165), lines 165–171: count 2, required true, no duplicate. Transcript `triage-23712-final-policy`, JSONL rows 11–15 and persisted counts 1 → 2 → 2. |
| Persistence across sweeps and restart/re-watch | Partly proven; nonempty obligation across restart/re-watch UNPROVEN | Raw `reactorRows` entries 7 and 11–12 (zero-based) retain `['remaining']` across quiet pending sweeps before completion. Restart rows 9 and 14 preserve **already cleared** owed state and suppress duplicates. No supplied named test/transcript restarts or re-watches while owed names are nonempty. Re-watch copying in [Orchestrator.ts:3403](https://github.com/pingdotgg/t3code/blob/72b87892b3585ff3c234679e6006043f0254c5a8/apps/server/src/orchestration-v2/Orchestrator.ts#L3403) and equality in [PullRequestWatchReactor.ts:174](https://github.com/pingdotgg/t3code/blob/72b87892b3585ff3c234679e6006043f0254c5a8/apps/server/src/orchestration-v2/PullRequestWatchReactor.ts#L174) support the implementation, but are not proof of that missing sequence. |
| Legacy watch record without field decodes | Proven by test | **“decodes a watch saved before passed checks were recorded”**, [threadPullRequest.test.ts:9](https://github.com/pingdotgg/t3code/blob/72b87892b3585ff3c234679e6006043f0254c5a8/packages/contracts/src/threadPullRequest.test.ts#L9): decoded watch at lines 21–30 omits `owedChecks`; decode succeeds and default passed-check state is asserted at line 34. |
| Non-required check fails after required notice: does agent wake? | Exact sequence UNPROVEN | Code inspection: yes, a newly failing non-required check emits `checks-failed` immediately ([pullRequestWatch.ts:56](https://github.com/pingdotgg/t3code/blob/72b87892b3585ff3c234679e6006043f0254c5a8/apps/server/src/orchestration-v2/pullRequestWatch.ts#L56)); no all-checks-success notice while failure remains (lines 77–89). Owed name remains while required gate stays passed; recovery can then emit final success. **“reports each failure at once, even while another check never finishes”**, [pullRequestWatch.test.ts:71](https://github.com/pingdotgg/t3code/blob/72b87892b3585ff3c234679e6006043f0254c5a8/apps/server/src/orchestration-v2/pullRequestWatch.test.ts#L71), covers generic failure deduplication, not failure after an early required notice. No supplied runtime transcript exercises that sequence. |
| New head push clears owed checks | UNPROVEN by named test or runtime transcript | Reset is explicit at [pullRequestWatch.ts:54](https://github.com/pingdotgg/t3code/blob/72b87892b3585ff3c234679e6006043f0254c5a8/apps/server/src/orchestration-v2/pullRequestWatch.ts#L54); the same read may legitimately record new-head pending checks. No test/transcript starts with nonempty owed state and changes head. Generic head-change failure coverage at test line 85 does not prove this field reset. |

**SHA provenance and claim corrections.** The red log was fetched with curl: it reports 1 failed / 16 passed at the final-completion assertion, but contains no implementation SHA, so baseline identity is unverified. Final runtime source is [75aef71](https://github.com/pingdotgg/t3code/commit/75aef714186dc24e893a63545caf054dbb549eb7), distinct from the watched metadata head [c762335](https://github.com/pingdotgg/t3code/commit/c762335dc05f81604242cb063d96b13c06884d58). Compared every added/removed patch line from each source's merge-base with fetched `origin/main`: evidence-source and audited-head patches are identical (70 added/removed lines each); direct comparison of all five changed files is empty. Thus the body’s “final merge from main changes no watch code” claim is supported. The runtime evidence proves persisted notification delivery/run creation with controlled check data and accelerated sweeps; all four provider runs failed on an intentionally invalid model, so it does not prove successful agent follow-up or live CI timing. Persistence claims exceed supplied proof specifically for nonempty owed state across restart/re-watch. No reproduction was added during this audit.

**Composition with [#23863](https://git.cafe/pingdotgg/t3code/pulls/23863).** It is already merged, rather than merely ready, and its merge commit is an ancestor of this head. The integrated evaluator composes the behaviors: any nonempty check read clears the no-checks clock (lines 100–109); owed completion requires nonempty checks (lines 55–94). They cannot both notify on the same read. A zero-checks notice followed by late required success and then remaining-check success can legitimately produce three different notices. Required-gate promotion and owed completion are mutually exclusive via `if` / `else if`, preventing two completion notices on that read. No combined zero-checks → early-required → owed-completion test/transcript is supplied; composition is established by source inspection, not that integrated runtime sequence.

Model: GPT-6.1 Sol through the Codex harness in T3 Code.

VERDICT: INSUFFICIENT — nonempty owed-check persistence across restart/re-watch; non-required failure after required notice; new-head owed-check reset.

</details>


## Round 2

Added test-only coverage in [93fe829](https://github.com/pingdotgg/t3code/commit/93fe829bdb5a0947f87ce38bb8b31c381748956f):
- `finishes owed checks once after pending sweeps, a restart, and a re-watch`: nonempty persisted obligation survives quiet sweeps, a fresh reactor, and stop/re-watch; completion sends exactly one final notice.
- `reports an owed non-required failure once after the required notice`: one failure notice immediately, no duplicate on the same failure, no success notice while failed, and one final success notice after recovery. The agent is woken about the failure.
- `clears old-head owed checks when a new head is pushed`: an empty new-head check read clears the old obligation; later advisory success produces no stale final notice.

All three requested watch suites passed (110 tests); lint on both changed files and the apps/server typecheck passed. TestClock and effect-worker drains are used; no sleeps or polling. No production changes. Main was already included, so no merge was needed. One read-only Claude Opus 5.5 review returned `VERDICT: MERGE`.

Model/harness: GPT-6.1 Sol through Codex in T3 Code; review by Claude Opus 5.5 through Claude Agent.

