After-runtime evidence kit for issue 23860

Scope: scratch files only; no tracked application changes. Lead owns the one integrated server. This kit has syntax/schema checks, but the after runtime has NOT been run.

Lead startup (from /home/ubuntu/wt/issue-23860): retain the existing isolated worktree .t3 and original dev launch configuration. Restart the lead-owned server so it imports the finalized preload. Required environment additions:

export PATH="/tmp/issue-23860-research/bin:$PATH"
export NODE_OPTIONS="${NODE_OPTIONS:+$NODE_OPTIONS }--import=/tmp/issue-23860-research/preload.mjs"
export GH_TOKEN=fixture-no-real-credential
export T3CODE_DEV_AUTH_TOKEN=issue23860-disposable-dev-token-at-least32
vp run dev

Do not set VITE_HTTP_URL or VITE_WS_URL. Read actual server port from dev-runner output. No browser is needed. The existing synthetic project research-project-d1e54b8e-5347-4543-b318-62775d720508 must exist in the isolated database and point at /tmp/issue-23860-research/repo. Each invocation creates a new thread in that project. Do not run two drivers concurrently.

Exact after command (replace 14725 with actual server port if different; same disposable token as server):

set -o pipefail
T3CODE_DEV_AUTH_TOKEN=issue23860-disposable-dev-token-at-least32 REPRO_WS_URL='ws://127.0.0.1:14725/ws?orchestrationProtocol=2' node /tmp/issue-23860-research/driver.mjs | tee /tmp/issue-23860-research/after-transcript.txt

Expected stdout: PROJECT, PERSISTED_BASELINE, FIXTURE_READ for UNKNOWN and CONFLICTING and UNKNOWN/title, PERSISTED_TITLE_WITH_KNOWN_RETAINED, assertion of zero no-op writes, FIXTURE_READ for MERGEABLE/title, PERSISTED_DIFFERENT_KNOWN, assertion of exactly three total snapshot writes, DONE. Driver exits nonzero on failed assertions or a 90-second failure deadline. The deadline is only a failure guard, never a success condition.

Milestones and absence proof:
1. Persist baseline CONFLICTING/title A; await actual thread.pull-request-link-updated payload.snapshot.
2. Refresh UNKNOWN/title A; await a revision-specific fixture receipt through fs.watch.
3. Refresh CONFLICTING/title A; await receipt. This next fetch is inside a later sweep of the reactor's serial drainable worker, so the preceding UNKNOWN sweep has finished. The fixture captures each response before emitting its receipt; changing the fixture cannot alter an in-flight response.
4. Refresh UNKNOWN/title B; await receipt and persisted title B event with conflicting retained. The third read proves the second no-op sweep completed. Assert only two snapshot events exist so far: baseline and title B. The baseline -> unknown -> same known sequence therefore caused zero snapshot writes, including no syncedAt-only write. Assert every other snapshot field except title/syncedAt matches baseline.
5. Refresh MERGEABLE/title B; await persisted mergeable event, assert exactly three total snapshot events, and assert only mergeability/syncedAt changed from title B. Finally getThreadProjection must equal that last snapshot.

Receipts prove fetch boundary reads, not persistence on their own. Persistence is proved by subscribed stored events and final projection. The worker ordering plus later title event supplies the no-op completion barrier. No sleeps, interval polling, app instrumentation, or private service calls. Receipt revisions include a run UUID to reject stale receipts across reruns. fixture.json and receipt.json use atomic renames. The preload fixture supports title and mergeability together; legacy mergeability file fallback remains for offline checks.

Verification performed without starting server/browser:
node --check /tmp/issue-23860-research/driver.mjs
node --check /tmp/issue-23860-research/preload.mjs
node /tmp/issue-23860-research/check-fixture.mjs

Offline schema check validates captured _0_repository, _1_repository and unaliased GraphQL responses with the actual generated result and normalized snapshot schemas. This is not after-runtime proof. The checker appends synthetic fixture reads to http.ndjson and writes a legacy receipt; do not treat all http.ndjson entries as server activity. Driver events.ndjson is append-only; after-transcript.txt is the clean per-run evidence. Before transcript is preserved separately.

FRICTION
- The old driver waited for a persisted unknown event, which the fix correctly suppresses. It cannot be reused as after proof.
- invalidate RPC completion only queues refresh; it does not drain the reactor. A fixture receipt also only marks response creation. Serial next-read barriers plus the persisted title event close that gap.
- Initial link is thread.pull-request-synced payload.pullRequests, with null snapshot; actual refresh is thread.pull-request-link-updated payload.snapshot. Only non-null update events are counted.
- orchestrationProtocol=2 is mandatory. Driver adds it automatically.
- A preload change requires a lead-owned server restart; editing it cannot change an already imported fetch closure.
- Existing project is reused, fresh thread each time. No automatic project recreation if the lead resets the database.
- Fixture handles GraphQL aliases and author.__typename, blocks unhandled GitHub fetch routes, and uses stack=null. PATH gh stub never delegates to a real forge CLI. No real forge reads or real credentials.
- Driver resolves undici from the main checkout dependency installation. No browser, server launch, forge operation, or tracked edit was performed by this test sub-agent.
