Skip to content

fix(web): keep file autosaves active after effect replay - #9878

Merged
juliusmarminge merged 2 commits into
mainfrom
fix/audit-file-save-strictmode
Sep 5, 2026
Merged

fix(web): keep file autosaves active after effect replay#9878
juliusmarminge merged 2 commits into
mainfrom
fix/audit-file-save-strictmode

Conversation

@juliusmarminge

@juliusmarminge juliusmarminge commented Sep 5, 2026

Copy link
Copy Markdown
Member

Problem

React StrictMode replays effect setup in development. The file preview memoized a FileSaveCoordinator but disposed that same instance during the replay cleanup. Subsequent editor changes and rendered Markdown task changes never reached the write command.

This was discovered while auditing #7907. It is a separate save-lifecycle bug, not a demonstrated fix for that report's word-wrap focus jumps or "Line doesn't exist" error. That issue should remain open.

Fix

Move the shared hook into its own module and create the coordinator in effect setup. The file-specific callback stays stable across effect replay, while cleanup detaches and disposes the old coordinator. A retired file callback cannot write into the next file or a later mount.

Both the source editor and rendered Markdown task list use the hook. The existing coordinator class, disposed guard, 500 ms debounce, write command, cache confirmation and close-flush behavior are unchanged. No dependency, server, contract, mobile or documentation changes.

Verification

Base 363cde411, committed September 4, 2026 at 20:12:04 PDT.

  • With the original main-branch hook moved unchanged into the testable module, all eight actual React StrictMode lifecycle tests fail. The first editor change produces no pending notification; the Markdown task update produces zero write calls.
  • The candidate passes all 25 focused tests. Coverage includes source-model changes, the real Markdown task transform, unchanged-file debounce, pending flush on close, path/cwd/environment switches, and ignored retired callbacks after unmount/remount.
  • Web typecheck and three-file formatting pass. Targeted lint has no errors and one pre-existing effect-dependency warning in untouched FilePreviewPanel code.
  • The three-file cumulative diff and both callers have been reviewed locally. The coordinator class is byte-identical to main.
cd apps/web
vp test run src/components/files/useFileSaveCoordinator.test.tsx src/components/files/fileSaveCoordinator.test.ts src/components/files/FilePreviewPanel.test.ts --project unit --maxWorkers 2
../../node_modules/.bin/tsgo --noEmit

The tests mount the real hook and coordinator under React StrictMode and assert the write/cache boundary. They do not simulate Pierre's native input handling or prove an actual disk write.

Integrated client verification

On September 4, 2026 (PDT), the orchestrator tested Chromium 152 on Linux against the base above, then the exact production changes from 31c544f. Both runs used the same disposable local server, synthetic thread and owned Markdown file. Other independently reviewed UI patches were held constant between runs.

  • Before: native pointer/keyboard input appended AUDIT-SAVE and Enter in the real editor. Closing and reopening lost both edits; the actual file on disk was unchanged.
  • After: the same input reached disk and survived closing/reopening. A rendered Markdown task toggle wrote [x], remained checked after reopen, and unchecked successfully.
  • A wrapped-line control sent 60 separate native Enter keypresses and a trailing marker. The file gained exactly 60 newline characters (6 → 66), retained the marker after reopen, and produced no captured window errors or unhandled rejections.
  • These are actual editor/RPC/disk checks, not a projection-only fixture. No provider turn was sent. The reported CachyOS/Electron focus-jump/error path remains unverified; #7907 remains open.

Before — reopened file has lost the typed marker:

Before: reopened Markdown file loses the native edit

After — reopened file retains the typed marker:

After: reopened Markdown file retains the native edit

Before recording:

https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/de6de44b76f7a5c9/9878-before-save.webm

After recording:

https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/6369243b73a7020c/9878-after-save.webm

Final audit

The orchestrator reviewed the whole three-file change and both callers. The coordinator class, debounce, command, cache and disposal guards remain unchanged. All current-head CI jobs that ran, Correctness, Approvability, UI Consistency, Effect Service Conventions and Bugbot pass; skipped jobs are not counted as executed tests. No unresolved review threads or active change requests remain.

This is a narrow lifecycle correctness repair within the requested simple-fix merge authority. No issue closure is implied.

Prepared by GPT 6 Astra via Codex in T3 Code.


Note

Medium Risk
Touches the file-edit persistence path (debounced writes and query cache confirmation), but scope is limited to hook lifecycle wiring with regression tests; the coordinator implementation itself is unchanged.

Overview
Fixes file autosave stopping in development when React StrictMode replays effect cleanup and disposed the memoized FileSaveCoordinator while the editor still called into that dead instance.

The inline useFileSaveCoordinator hook is extracted to useFileSaveCoordinator.ts. Coordinator creation moves into useEffect setup with a ref-backed change callback, so replay gets a new coordinator instead of reusing a disposed one. Retired callbacks are cleared on cleanup and cannot write after unmount, file switches, or remount. FilePreviewPanel now imports the shared hook; debounce, write command, and cache confirmation behavior on the coordinator class are unchanged.

Adds StrictMode-focused unit tests for editor saves, rendered Markdown task toggles, debounce across rerenders, unmount flush, and file identity changes.

Reviewed by Cursor Bugbot for commit 31c544f. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Fix file autosave after effect replay in FilePreviewPanel

  • Extracts the save-coordinator hook from FilePreviewPanel.tsx into useFileSaveCoordinator.ts, with a typed options contract and memoized session
  • useFileSaveCoordinator routes changes only to the active coordinator; effect replay and file-identity transitions create a fresh coordinator while stale callbacks from retired sessions become inert
  • Retains the existing 500 ms debounce, file-write persistence, pending-state reporting, and confirmed-query updates; cleanup clears the active coordinator reference before disposal
  • Adds StrictMode lifecycle tests in useFileSaveCoordinator.test.tsx covering editor change persistence, Markdown task writes, rerender debounce, unmount/retired-callback, file-identity transitions, and same-file remount
  • Risk: callbacks retained after a session retires are silently dropped instead of writing; verify useFileSaveCoordinator's session swap logic on relative path, working directory, and environment identity changes

Macroscope summarized 31c544f.

@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Sep 5, 2026
@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

Provider Metric Main baseline This PR Impact PR ceiling
Codex Total thread wire 13.5 KiB 13.5 KiB 0 B (0.0%) 15.1 KiB
Codex Thread snapshot wire 7.0 KiB 7.0 KiB −10 B (−0.1%) 7.3 KiB
Codex Live turn WebSocket wire 6.5 KiB 6.5 KiB +10 B (+0.2%) 7.8 KiB
Codex Live turn WebSocket decoded 57.0 KiB 57.0 KiB 0 B (0.0%) 66.4 KiB
Codex Live turn messages 8 8 0 (0.0%) 21
Claude Total thread wire 13.5 KiB 13.6 KiB +38 B (+0.3%) 15.1 KiB
Claude Thread snapshot wire 7.0 KiB 7.0 KiB +1 B (+0.0%) 7.3 KiB
Claude Live turn WebSocket wire 6.5 KiB 6.5 KiB +37 B (+0.6%) 7.8 KiB
Claude Live turn WebSocket decoded 57.8 KiB 57.8 KiB +44 B (+0.1%) 66.4 KiB
Claude Live turn messages 8 9 +1 (+12.5%) 21

Baseline: 363cde4 · PR result: 31c544f · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 113.8 KiB
  • Claude decoded thread snapshot: 114.5 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

@macroscopeapp

macroscopeapp Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at 31c544f

Macroscope's review found this PR approvable — This is a focused autosave lifecycle fix that preserves the existing debounce, file-write command, and query confirmation behavior while handling React effect replay safely. Targeted tests cover StrictMode, rerenders, stale callbacks, unmount flushing, and Markdown edits.

You can add or adjust custom eligibility rules. Learn more.

@juliusmarminge
juliusmarminge merged commit b01771c into main Sep 5, 2026
23 checks passed
@juliusmarminge
juliusmarminge deleted the fix/audit-file-save-strictmode branch September 5, 2026 03:31
@juliusmarminge

Copy link
Copy Markdown
Member Author

Root audit complete on 31c544f: whole three-file diff and both callers reviewed; 25 focused tests and all current-head executed CI/review checks pass. The PR body now includes actual native typing, disk persistence, close/reopen, Markdown checkbox, and 60 Enter-key controls with before/after media. No captured browser errors. This is within the requested narrow correctness-fix merge scope. GitHub does not permit a self-approval, so this comment records the orchestrator audit separately from the existing bot approval and branch-protection gates. #7907 remains open for its separate native word-wrap report.

GPT 6 Astra via Codex in T3 Code.

github-actions Bot added a commit to omarcresp/t3code-flake that referenced this pull request Sep 5, 2026
## What's Changed
* fix(connect): refresh HTTP credentials without reconnecting by @juliusmarminge in pingdotgg/t3code#9594
* fix(web): keep file autosaves active after effect replay by @juliusmarminge in pingdotgg/t3code#9878
* fix(web): prioritize open panel pull request when copying by @maria-rcks in pingdotgg/t3code#9877
* fix(cli): resolve projects with missing workspace directories by @juliusmarminge in pingdotgg/t3code#9885


**Full Changelog**: pingdotgg/t3code@v0.0.39-nightly.20260905.1283...v0.0.39-nightly.20260905.1284

Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.39-nightly.20260905.1284
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M 30-99 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant