Skip to content

Questions across replay and reconnect on the phone and paired desktops (CF-6) - #389

Merged
BunsDev merged 10 commits into
mainfrom
cf6-replay
Oct 23, 2026
Merged

BunsDev merged 10 commits into
mainfrom
cf6-replay

Conversation

@BunsDev

@BunsDev BunsDev commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Part of CF-6 (#279), under tracker #273. It builds on CF-3's phone answers (#373) and Codex's native plan mode (#385).

Most of CF-6 has already landed: #285, #308, #309, #318, #329, #330, #335 and #336. This PR covers what #373 and #385 made possible. A structured question must survive replay and reconnect on the phone and on paired desktops, and Codex's plan message must converge on every path.

Fixes (each had a failing test first)

  • An older phone was told the wrong place. A phone that doesn't draw question cards was told a question answered on the phone was "Answered on your Mac." It now says "Answered on your phone." (companion.ts, phoneItem)
  • The phone couldn't see which answer was Codex's plan. It now receives an assistant message's nativePhase, so the plan (nativePhase: "plan") is marked. Older phones ignore the field.

New tests

  • test/question-replay.test.ts. Uses a real Codex app-server fixture, journal, companion over TLS and paired desktop.

    • While the phone was away: a question is answered on the desktop, withdrawn by Codex (serverRequest/resolved), or stopped.
      • The journal delta closes the card, both for a phone that draws questions and, as a notice, for one that doesn't.
      • Taps queued from before the sleep get a 409. That covers both the card's answer and the approval route.
      • Codex hears exactly one reply.
      • The secret is in no delta, journal row or event.
    • Cursors that must reload: one from another journal, one ahead of the log, a pruned one, and no journal at all. Each gets the whole transcript with the card closed.
    • Paired desktop: one that slept while the phone answered resumes from its cursor to the card answered on the phone. Its stale answer is refused.
    • Crash inside the 250 ms batch window, after a phone answer: the rows are still unwritten. The journal reopens with a new id, the phone's old cursor reloads, the card says the phone answered, and nothing runs or is sent again.
    • Answer still sending at a crash: it reads as uncertain on both kinds of phone and is refused if sent again.
    • Older paired desktop: it can only approve or deny, so its answer to a question is invalid and Codex hears nothing.
  • test/journal-resume.test.ts. One typed turn runs through these steps:

    • a commentary message;
    • a question with a secret, answered on the phone while the paired desktop slept, with a late desktop answer refused;
    • Codex's plan, streamed and then failed;
    • a retry that reuses the plan's native id while the first attempt's sink sends a late delta.

    Every path agrees: the live projection, the saved files, the event stream, the live journal and a fully coalesced one (each replayed twice), the paired desktop, both kinds of phone (full snapshot against mid-run snapshot plus delta), and a restarted runner. Codex heard the phone's answer once.

  • test/conversation-migration.test.ts. Loads questions and a plan as v0.3.0 saved them.

    • The open question closes as ended.
    • Older answers gain no invented sender or delivery.
    • The plan keeps its phases.
    • The draft file is byte-identical, and nothing runs.
  • iOS QuestionCardTests. A delta that closes one card drops its draft and keeps another card's draft. A withdrawn card sends nothing. A reload replaces the transcript instead of merging into it. An uncertain answer stays closed.

  • iOS UI (CompanionFlowTests). The paired fixture now keeps an event journal, so every UI test polls with a cursor. Two question cards are closed on the Mac (one answered, one stopped) while open on the phone with a choice made. The next poll closes each one ("Answered on your Mac" and "Not answered — the turn was stopped") and removes its controls.

The iOS tests passed on their first run. They pin behaviour the iPhone model already had; they are not red-then-green repairs.

Results

Final head fa4b7cb, on main at 2eba641, with every commit signed. Environment: macOS, Node v24.22.0, pnpm 10.34.5, the iOS 27.0 simulator (a dedicated iPhone 18 Pro).

Command Result
pnpm build && pnpm typecheck && pnpm test Build and typecheck passed. Desktop unit: 1,844 tests, 1,828 passed, 16 skipped, 0 failed. Other suites: 117, 16, 21, 10 and 16, all passed
pnpm test:e2e 389 passed, 6 skipped, 0 failed; site 7/7
xcodebuild … -only-testing:ModexCompanionTests test 60/60, 0 skipped
apps/ios/ModexCompanion/Scripts/test-e2e.sh (MODEX_UI_TIMEOUT_SCALE=2) 43/43

No test was flaky, hung or retried. The 6 e2e skips and 16 unit skips are the existing opt-in, live-provider and platform-specific cases.

Gates beyond the tests

The CF-6 gate review records each gate:

  • Live Codex, journal on (codex-cli 0.163.0): passed.
    • A Plan turn asked a question. The phone answered it over HTTPS, and its late tap got a 409.
    • The live projection, saved files, event stream, journal (replayed once and twice), the phone's snapshot plus delta, and a restarted runner were identical.
    • The phone saw the plan as final/plan.
  • Live Claude Code, journal on (2.1.309, haiku, chat mode): passed. The phone approved touch, its late deny got a 409, and every view agreed.
  • 8 MB slow-stream fallback: passed. It is now an automated test through the real host and TLS.
  • Power loss: proxy only. A process dying inside the batch window is tested; real power loss is not.
  • Physical iPhone, Windows and Linux: waiting. Exact steps and pass/fail checks are on that page.

Live evidence and its sha256 hashes are outside the tree, at ~/.claude/task-evidence/cf6-2026-10-22/.

Docs

  • human-requests.md: a "question closed while a client was away" section, the older phone's new wording, and the new tests.
  • event-journal.md: the phone's nativePhase, the typed convergence test, and the journal-on iPhone fixture.
  • status.md: a new unreleased entry.
  • docs/reviews/2026-10-22-cf6-gates.md: the gate review above.

Still open for #279

These gates need Val or hardware; this PR doesn't close them:

  • a physical iPhone reconnecting after sleep and a network change;
  • Windows and Linux;
  • real power loss.

The TestFlight upload of a Companion build with the card is Val's.

🤖 Generated with Claude Code

BunsDev and others added 8 commits October 22, 2026 21:34
…desktops (CF-6)

Red: a legacy phone is told a question answered on the phone was answered on the Mac.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…F-6)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… (CF-6)

Red: the phone does not receive nativePhase, so it cannot tell Codex's plan from an ordinary answer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…lan is marked (CF-6)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…n every client (CF-6)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e or deny (CF-6)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ugh the journal (CF-6)

The paired fixture now keeps an event journal, so every UI test polls with a
cursor. Two more question threads are closed on the Mac (answered, stopped)
while open on the phone with a draft; the next poll closes each card.
QuestionCardTests adds delta, withdrawal, reload and uncertain cases. All
passed on first run: they pin behaviour the model already had.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vercel

vercel Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
modex Ready Ready Preview Oct 23, 2026 3:15am UTC

Request Review

BunsDev and others added 2 commits October 22, 2026 21:43
…ered question (CF-6)

The host's 8 MB stream backlog had no test. A real paired desktop over TLS
stops reading; the host drops its stream past 8 MB; its reconnect from the
old cursor would replay more than 4 MB, so it reloads, and thread:items
shows the card answered. Passed first run: it pins existing behaviour.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-code-quality

github-code-quality Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Code coverage overview

Languages: TypeScript

TypeScript / code-coverage-agent

main 2eba641 cf6-replay fa4b7cb +/-
65% 65% 0%
View coverage of the 5 most impacted files.
File main 2eba641 cf6-replay fa4b7cb +/-
apps/desktop/sr...ne/companion.ts 98% 98% 0%
apps/desktop/sr...esktop-items.ts 98% 98% 0%
apps/desktop/sr...ngine/runner.ts 98% 98% 0%
apps/desktop/sr...man-requests.ts 100% 100% 0%
apps/desktop/sr...desktop-host.ts 98% 99% +1%

ℹ️ Calculations represent line coverage. Results may change as calculations are updated.


Updated October 23, 2026 04:04 UTC

@BunsDev

BunsDev commented Oct 23, 2026

Copy link
Copy Markdown
Contributor Author

Val chose (2026-10-23) to land #389 first as the CF-6 base. #388 (cf6-acceptance) will be rebased onto it and keep only what #389 lacks: the Windows/Linux CI journal suites with the Windows signal fix, the in-app live journal check, the Electron mid-reply kill, the older-Companion notice and foreign-cursor tests, three event-journal.md corrections, and Val's G4/G6 deferrals, merged into this gate plan. Auto-merge is on for #389. Please don't force-push it in the meantime.

BunsDev added a commit that referenced this pull request Oct 23, 2026
… live journal check, a mid-reply kill (CF-6)

Rebased onto #389 (cf6-replay) at Val's choice, keeping only what it does not already cover:

- The Windows and Linux platform jobs run the journal, replay and request suites, now including
  question-replay. journal-resume's crash test accepts Windows, where a self-SIGKILL ends the child with
  an exit code rather than a signal.
- The live smoke checks the journal in the real app with MODEX_EVENT_JOURNAL=1, and a Chat-mode
  scenario replays an approved card exactly as saved.
- An e2e kills the app in the middle of a streaming reply: a new journal id, the reply unknown at its
  saved length, nothing run again. It found that the journal can be ahead of the thread files at a
  crash.
- event-journal.md: an 8 MB drop reloads in practice, the heartbeat also drops a stalled stream, and the
  crash finding. The gate review records these and Val's deferrals of the Windows and Linux app
  sessions and a real power cut.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@BunsDev
BunsDev added this pull request to the merge queue Oct 23, 2026
@BunsDev

BunsDev commented Oct 23, 2026

Copy link
Copy Markdown
Contributor Author

Correction to my earlier comment: auto-merge didn't take then (rate limit). #389 is now in the merge queue (queued 04:54Z, position 1), per Val's choice to land it first. #388 is rebased onto cf6-replay at 9f8ae8f and retargeted here, with the duplicate tests dropped.

Merged via the queue into main with commit 4f44304 Oct 23, 2026
13 checks passed
@BunsDev
BunsDev deleted the cf6-replay branch October 23, 2026 05:16
BunsDev added a commit that referenced this pull request Oct 23, 2026
… live journal check, a mid-reply kill (CF-6)

Rebased onto #389 (cf6-replay) at Val's choice, keeping only what it does not already cover:

- The Windows and Linux platform jobs run the journal, replay and request suites, now including
  question-replay. journal-resume's crash test accepts Windows, where a self-SIGKILL ends the child with
  an exit code rather than a signal.
- The live smoke checks the journal in the real app with MODEX_EVENT_JOURNAL=1, and a Chat-mode
  scenario replays an approved card exactly as saved.
- An e2e kills the app in the middle of a streaming reply: a new journal id, the reply unknown at its
  saved length, nothing run again. It found that the journal can be ahead of the thread files at a
  crash.
- event-journal.md: an 8 MB drop reloads in practice, the heartbeat also drops a stalled stream, and the
  crash finding. The gate review records these and Val's deferrals of the Windows and Linux app
  sessions and a real power cut.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch was successfully deployed

1 active deployment
Preview — fa4b7cb8 Deployed Oct 23, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant