# PR #22947 evidence: working-timer anchor across agent steers and child updates

**Environment:** host `fs-28`, Linux 6.1.102 x86_64, node v26.12.1 (driver and servers), provider `claudeAgent`, model `claude-opus-5-5` (via CLIProxyAPI hub). BEFORE server `8d3b061121fe65669d5d43d011b48cd3027a4108`, AFTER server `5d9fd21d7c0c31334693bab72474410ef31a55c5`. Driver `apps/server/scripts/verify-work-timer-start.ts` sha256 `2e4d6f9ca3fc3486a279b36c3f21c6a505b65b962a286242dffcba2132042314` (byte-identical on both sides; BEFORE `2e4d6f9ca3fc3486a279b36c3f21c6a505b65b962a286242dffcba2132042314`). Each server ran with its own `--base-dir` under `/tmp/ev-22947/home-<side>`. Both sides ran in parallel, 2026-10-22 11:25:27–11:37 UTC (pass 2).

Times are UTC `HH:MM:SS.mmm` on 2026-10-22. **ws** = persisted `payload_json.$.workStartedAt` (read-only sqlite), **st** = persisted `startedAt`, **req** = `requested_at`, **anchor** = `ws ?? st ?? req`. **shell** = `activityRunStartedAt` from the `orchestration.subscribeShell` watcher at the milestone (with the receive time of the last change), **thr ws** = `workStartedAt` on the run from `orchestration.subscribeThread`. Sender: C = coordinator thread, K/K2 = app-owned child of T (verified against `projection.subagents` with `origin:"app_owned"`).

## Row verdicts

| Row | Trigger | Matrix expectation | BEFORE | AFTER | Verdict |
|---|---|---|---|---|---|
| R0 | User turn 1 on T (`sleep 110`) | absent → anchor = own startedAt (S1), both | run #1 completed; ws **—**; st 11:26:33.375; req 11:26:33.020; anchor **11:26:33.375**; thr ws —; shell 11:26:33.375 (rx 11:26:33.884); msg user/web/sender none | run #1 completed; ws **—**; st 11:26:36.220; req 11:26:35.267; anchor **11:26:36.220**; thr ws —; shell 11:26:36.220 (rx 11:26:36.511); msg user/web/sender none | PASS |
| R1 | C `t3_thread_send` mode `steer` into running T | no new run; anchor stays S1, both | msg run_id = run 1 (absorbed), own runs 0, runCount 1→1; S1 11:26:33.375; shell 11:26:33.375; sender C (agent/mcp) | msg run_id = run 1 (absorbed), own runs 0, runCount 1→1; S1 11:26:36.220; shell 11:26:36.220; sender C (agent/mcp) | PASS |
| R2 | C `t3_thread_send` mode `queue` behind running T, drains when run 1 ends | BEFORE absent (reset to own start); AFTER = S1 | run #2 completed; ws **—**; st 11:28:46.456; req 11:27:09.213; anchor **11:28:46.456**; thr ws —; shell 11:28:46.456 (rx 11:28:46.764); msg agent/mcp/sender C | run #2 completed; ws **11:26:36.220**; st 11:28:47.836; req 11:27:13.856; anchor **11:26:36.220**; thr ws 11:26:36.220; shell 11:26:36.220 (rx 11:26:36.511); msg agent/mcp/sender C | PASS |
| R3 | App-owned child K → T mode `queue` while T runs (drains after delegated-completion run) | BEFORE absent (reset); AFTER = S1 | run #5 completed; ws **—**; st 11:30:30.249; req 11:29:23.378; anchor **11:30:30.249**; thr ws —; shell 11:30:30.249 (rx 11:30:30.370); msg agent/mcp/sender K(app_owned child of T) | run #5 completed; ws **11:26:36.220**; st 11:30:33.163; req 11:29:23.817; anchor **11:26:36.220**; thr ws 11:26:36.220; shell 11:26:36.220 (rx 11:26:36.511); msg agent/mcp/sender K(app_owned child of T) | PASS |
| R10a | Delegated completion wake (K / K2 finished) | continues previous anchor, both | run #4 completed; ws **11:28:46.456**; st 11:30:17.948; req 11:29:27.555; anchor **11:28:46.456**; thr ws —; shell — (rx —); msg agent/server/sender none | run #4 completed; ws **11:26:36.220**; st 11:30:22.246; req 11:29:27.222; anchor **11:26:36.220**; thr ws —; shell — (rx —); msg agent/server/sender none | PASS |
| R10b | Delegated completion wake (K / K2 finished) | continues previous anchor, both | run #8 completed; ws **11:31:50.511**; st 11:32:04.014; req 11:31:54.445; anchor **11:31:50.511**; thr ws —; shell — (rx —); msg agent/server/sender none | run #8 completed; ws **11:30:59.122**; st 11:32:08.220; req 11:31:57.738; anchor **11:30:59.122**; thr ws —; shell — (rx —); msg agent/server/sender none | PASS |
| R9 | New user turn on idle T (delegates K2, ends) | absent → own startedAt, both | run #6 completed; ws **—**; st 11:30:56.349; req 11:30:56.161; anchor **11:30:56.349**; thr ws —; shell 11:30:56.349 (rx 11:30:56.522); msg user/web/sender none | run #6 completed; ws **—**; st 11:30:59.122; req 11:30:58.973; anchor **11:30:59.122**; thr ws —; shell 11:30:59.122 (rx 11:30:59.505); msg user/web/sender none | PASS |
| R4 | K2 → idle T (`start_immediately`) | BEFORE absent (reset); AFTER = prior anchor (R9 start) | run #7 completed; ws **—**; st 11:31:50.511; req 11:31:49.956; anchor **11:31:50.511**; thr ws —; shell 11:31:50.511 (rx 11:31:50.862); msg agent/mcp/sender K(app_owned child of T) | run #7 completed; ws **11:30:59.122**; st 11:31:51.464; req 11:31:51.205; anchor **11:30:59.122**; thr ws 11:30:59.122; shell 11:30:59.122 (rx 11:31:51.651); msg agent/mcp/sender K(app_owned child of T) | PASS |
| R5 | C → idle T (immediate) | absent, both | run #9 completed; ws **—**; st 11:32:41.661; req 11:32:41.407; anchor **11:32:41.661**; thr ws —; shell 11:32:41.661 (rx 11:32:42.248); msg agent/mcp/sender C | run #9 completed; ws **—**; st 11:32:39.825; req 11:32:39.625; anchor **11:32:39.825**; thr ws —; shell 11:32:39.825 (rx 11:32:40.938); msg agent/mcp/sender C | PASS |
| R8 | CLI `t3 thread send` while T runs (automatic-queue) | absent, both (no senderThreadId) | run #10 completed; ws **—**; st 11:33:40.525; req 11:33:10.351; anchor **11:33:40.525**; thr ws —; shell 11:33:40.525 (rx 11:33:40.812); msg agent/mcp/sender none | run #10 completed; ws **—**; st 11:33:38.034; req 11:33:10.269; anchor **11:33:38.034**; thr ws —; shell 11:33:38.034 (rx 11:33:38.629); msg agent/mcp/sender none | PASS |
| R6 | C queued behind user hold run → `thread.stop` → `queue.resume` | absent, both (explicit-queue via queue.resume) | run #12 completed; ws **—**; st 11:34:30.635; req 11:34:23.634; anchor **11:34:30.635**; thr ws —; shell 11:34:30.635 (rx 11:34:30.865); msg agent/mcp/sender C | run #12 completed; ws **—**; st 11:34:29.549; req 11:34:21.756; anchor **11:34:29.549**; thr ws —; shell 11:34:29.549 (rx 11:34:29.636); msg agent/mcp/sender C | PASS |
| R7 | C queued behind user hold run → `thread.stop` → `queued-run.start` | absent, both (explicit-queue via queued-run.start) | run #14 completed; ws **—**; st 11:35:24.214; req 11:35:13.653; anchor **11:35:24.214**; thr ws —; shell 11:35:24.214 (rx 11:35:24.452); msg agent/mcp/sender C | run #14 completed; ws **—**; st 11:35:24.152; req 11:35:10.351; anchor **11:35:24.152**; thr ws —; shell 11:35:24.152 (rx 11:35:24.429); msg agent/mcp/sender C | PASS |

R10a is the completion of K (run #4, it jumped ahead of K's queued R3 message); R10b is the completion of K2 (run #8).

## Gaps (reset vs inherit is unambiguous)

- BEFORE: R2 start − S1 = 133s; R3 start − S1 = 237s; R4 start − R9 start = 54s (T idle between R9 end and R4).
- AFTER: R2 start − S1 = 132s; R3 start − S1 = 237s; R4 start − R9 start = 52s (T idle between R9 end and R4).

## What else changed between sides

- **Expected diffs only.** BEFORE and AFTER differ only in R2, R3, R4, and, as a downstream effect, the R10 values:
  - **R2** (run #2): AFTER persists `workStartedAt = S1`. BEFORE has none, so its anchor resets to run #2's own start.
  - **R3** (run #5): the same split. AFTER inherits S1 through run #4.
  - **R4** (run #7): AFTER persists `workStartedAt` = R9's start. BEFORE resets to its own start.
- **R10 differs in value, not behavior.** On both sides the delegated-completion runs continue the previous run's anchor. The value differs only because the previous anchor differs: BEFORE R10a = R2's reset start, AFTER R10a = S1. Same rule, PASS on both.
- **Client-received anchor agrees with the persisted value on every row and side.**
  - The `subscribeShell` `activityRunStartedAt` and the `subscribeThread` run `workStartedAt` always matched the persisted anchor.
  - In AFTER, the shell value stayed at S1 from 11:26:36.511 through R2, R3 and R10a with no change event. The queued runs drained back to back, so there was no idle `null` between them.
  - In BEFORE, the shell moved to each new run's own start (R2 rx 11:28:46.764, R3 rx 11:30:30.370), so the visible timer reset.
  - R4 AFTER: the shell went from `null` (T idle) back to R9's start, 52 s in the past, at rx 11:31:51.651. The matrix calls this out: the timer jumps back by the idle gap.
- **Identical structure on both sides.**
  - T: 13 runs (11 completed, 2 interrupted by the R6/R7 `thread.stop`), 14 user messages.
  - C: 6 runs, all completed. Both ran 2 app-owned children (K, K2).
  - Neither side had driver errors or "Provider turn failed" log lines.
  - Run ordinal 3 is skipped on **both** sides. K's queued R3 message carries `run_id …:ordinal:3`, but its run row is ordinal 5, because the delegated-completion run #4 jumped ahead of it. This is pre-existing queue/ordinal behavior and does not differ between SHAs.
- **Timing noise.** The two servers ran concurrently, and per-row start times differ by 0–4 s between sides (model latency). All reset/inherit decisions sit on gaps of ≥52 s (see Gaps), so the noise cannot flip a verdict.
- **Model behavior.**
  - Every agent row's persisted message carries the expected `createdBy=agent`, `creationSource=mcp` and `senderThreadId` (C, K or K2). R8 (CLI) correctly has `agent/mcp` with **no** `senderThreadId`.
  - No row needed a re-run because the model improvised.
  - The R1 steer was absorbed into run #1 (message `run_id` = run 1) on both sides.
  - On both sides the delegated-completion wake (run #4) jumped ahead of K's queued message (run #5). The drain is therefore "after run #4 ended", which is still `automatic-queue`.

## Unverified

- **R11** (restart continuation via `t3_thread_send mode:"restart"`) was not run. It is optional in the matrix and outside the PR's changed branches; the unit test covers it. Everything else (R0–R10) was captured.

## History (superseded runs, not uploaded)

- **pass 0** (driver `380e…`/`5ae4…`/`46ce…`) failed to start:
  - First failure: I awaited `withResolvers()` instead of `.promise`.
  - Second failure: the ambient `T3CODE_HOME=/home/ubuntu/.t3` won over the bootstrap `t3Home`. The server's ownership guard refused to start against the live install ("No server was stopped"). The fix was to pass `--base-dir` and override `T3CODE_HOME` in the child env.
  - Third failure: readiness needs `--log-level debug`.
- **pass 0 (driver `1071…`)** and **pass 1 (`56c9…`)** produced the same R0–R5/R9/R10 results as pass 2. Two problems:
  - Pass 0's in-log R3/R4 milestones picked the parent run, because the parent prompt quoted the marker. Fixed by matching with `startsWith`.
  - Pass 1's R7 failed: `queued-run.start` requires a stopped thread. Pass 1's R8 failed on a bad CLI flag.
- Those logs are kept locally in `/tmp/ev-22947/capture/pass0`, `pass1`.

## Rebuild one row

All rows come from one driver run per SHA (about 12 min). From a checkout at the SHA under test, with `claude` reachable:

```
node apps/server/scripts/verify-work-timer-start.ts --home-dir /tmp/ev-22947/home-after --label after --out /tmp/ev-22947/capture/after.log
python3 -I /tmp/ev-22947/tools/rows.py /tmp/ev-22947/capture   # per-row JSON; e.g. grep '"row": "R2"'
```
