Skip to content

Commit dc428fb

Browse files
pmaxhoganclaude
andcommitted
docs(m8): record the M8 codex round-1 fixes + resolve the cancel residual
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012CyiRqk2DVwmJjEu5gcD1m
1 parent afaaead commit dc428fb

1 file changed

Lines changed: 49 additions & 8 deletions

File tree

design/CODEX_NOTES.md

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1244,16 +1244,57 @@ browse -> search -> select -> restore flow against mocked `invoke` + a mocked
12441244
webview path is forbidden, so the signature carries the one-shot token the
12451245
backend resolves to the approved directory. The contract is documented on the
12461246
command + the typed wrapper.
1247-
- **No mid-job cancel command.** ROADMAP M8 lists "cancel mid-restore cleans up the
1248-
partial file"; partial-file cleanup IS implemented (every per-file write is a
1249-
temp file removed on any error, and only renamed into place after blake3
1250-
verification, so a failed/aborted file never leaves a partial under the final
1251-
name). A USER-driven cancel IPC is not wired in this pass - the background job
1252-
runs to completion. The data-safety half of the acceptance (no partial final
1253-
files) holds; an explicit cancel button is a follow-up (honest residual, not a
1254-
fake-green).
12551247
- **ICU plural not used for `restore.selectedCount`.** vue-i18n's default message
12561248
compiler in this repo is not configured for ICU MessageFormat (DESIGN s8.7 names
12571249
it as the V1 target but it is not wired; no existing key uses it). Used a plain
12581250
`{count} selected` interpolation rather than introduce an unparseable ICU string
12591251
that breaks the build. Wiring ICU is a cross-cutting i18n change, out of M8 scope.
1252+
1253+
## M8 codex round-1 fixes (2 P1 + 5 P2; baseline 1a7ad60 @ 887aaab)
1254+
1255+
All findings from `.claude/codex-reviews/M8-20260624-161042.md` fixed. The prior
1256+
"no mid-job cancel command" residual is now RESOLVED (P1-1).
1257+
1258+
- **M8-P1-1 - cancellable restore + cleanup + shutdown drain.** Each restore job
1259+
now holds a shared cancel flag (`Arc<AtomicBool>`) + its spawned `tokio`
1260+
`JoinHandle`, tracked on `AppState` (`seed_restore_job` / `set_restore_job_handle`
1261+
/ `cancel_restore_job` / `cancel_all_restore_jobs` / `finish_restore_job_handle`).
1262+
New `cancel_restore_job(jobId)` IPC sets the flag; `stream_to_disk` checks it
1263+
BEFORE each file and between frames, and on cancel returns `Cancelled` so
1264+
`restore_one_file` DELETES the temp (no partial, nothing renamed into place). The
1265+
job emits a terminal CANCELLED `RestoreJobStatus` (`cancelled: true`,
1266+
`RestoreFileState::Cancelled` for the unfinished files). App shutdown
1267+
(`lib.rs::shutdown_orchestrators`) now `cancel_all_restore_jobs()` + joins every
1268+
restore handle alongside the M5 account drain, so quit leaves no orphaned restore
1269+
task and no partial files. UI: a Cancel button (gated by a `cancelling` flag), a
1270+
`cancelled` terminal label, and a per-file Cancelled state.
1271+
- **M8-P1-2 - no-follow, non-truncating, race-safe temp write.** The temp is now a
1272+
RANDOM name (`.driven-restore-tmp.<uuid>`, not timestamp-derived) opened via
1273+
`open_temp_no_follow`: `create_new(true)` (O_EXCL - fails if the path exists,
1274+
killing a pre-place / race-to-the-path attack) PLUS platform no-follow flags
1275+
(Unix `O_NOFOLLOW`; Windows `FILE_FLAG_OPEN_REPARSE_POINT`) so a symlinked temp
1276+
leaf cannot redirect the write outside the approved root. After the stream the
1277+
rename target is RE-validated via `validate_restore_dest` (catches a TOCTOU leaf
1278+
swap) before renaming. Tests: O_EXCL rejects an existing path; a pre-placed
1279+
symlink at the temp path is refused and the victim target is not overwritten.
1280+
- **M8-P2-1 - surface tree truncation.** `list_remote_tree` now returns
1281+
`RemoteTreeDto { entries, truncated }` instead of a bare `Vec`; `truncated` is set
1282+
when the folder has more immediate children than the cap (or the scan hit its row
1283+
cap). Restore.vue shows a "showing the first N items" notice. The cap itself is
1284+
unchanged.
1285+
- **M8-P2-2 - search input limits per DESIGN s18.8.** `MAX_QUERY_LEN` tightened
1286+
from 1024 to 256 (counted in CHARS), and `search_files` now rejects `\0`, `\r`,
1287+
`\n`, and any other control char before the FTS/GLOB path.
1288+
- **M8-P2-3 - bounded restore-job snapshot memory.** Terminal restore-job records
1289+
are TTL-pruned (1h) and count-capped (32, oldest-terminal first) by
1290+
`prune_terminal_jobs` on every register/put; active jobs are never pruned.
1291+
- **M8-P2-4 - persist active job id + reconcile on remount.** The store keeps the
1292+
returned `jobId` and calls `getRestoreJob(jobId)` after start AND on
1293+
(re)subscription, so a remount / missed terminal event recovers current state
1294+
instead of going stale.
1295+
- **M8-P2-5 - classify remote download failures.** `classify_download_error` maps
1296+
the Drive `download` error into the specific SPEC s24 code (auth.invalid_grant /
1297+
drive.rate_limited / drive.daily_quota_exhausted / drive.quota_exhausted /
1298+
net.intermittent / drive.unreachable for 404/unclassified), reusing the same
1299+
typed `classification_of` downcast + string fallback the executor uses. The
1300+
specific code is stored on the per-file restore failure.

0 commit comments

Comments
 (0)