# File merge safety and recovery

Established-baseline **Sync now** performs an all-or-nothing, file-level three-way merge. Explicit push and pull remain directional. This phase does not merge JSON fields, text hunks, or divergent sessions, and does not publish partial progress during a conflict.

## Decisions and selection

`sync/file-merge-planner.ts` compares the union of baseline, local, and remote paths. An unavailable ancestor is not an established empty baseline. Absence is not empty content.

| Observation | Decision |
| --- | --- |
| Local and remote equal, including absence | Accept equality. |
| Only local differs from baseline | Select local bytes or deletion. |
| Only remote differs from baseline | Select remote bytes or deletion. |
| Both differ and final contents differ | Require review; includes divergent additions and delete/modify. |
| Ancestor unavailable or selection changed | Keep initial-source/selection review. |
| Remote would write/delete the current session | Require review; never install that version automatically. |
| Case aliases or file/descendant paths occur across inputs | Withhold the complete group; require a reviewed direction. |
| A changed top-level target has a noncanonical physical case variant | Withhold publication/apply; normalize the selected file spelling without changing reviewed bytes before retrying. |
| Accepted settings change the session storage root while sessions are selected | Withhold merged publication/apply/baseline acceptance; require explicit directional review, including interrupted journals. |

Merged input is bounded to 64 MiB decoded content and 16,384 input-file entries; the private journal also has a 384 MiB serialized bound. Larger transfers require a reviewed directional operation rather than an unbounded merge. One unresolved path prevents the whole merged transfer. The planner verifies safe/denied paths, canonical base64 and hashes before authorization. It rejects invalid UTF-16 paths and compares protected context paths using Node’s UTF-8 conversion followed by NFC/case identity. Read-only filesystem preflight refuses non-regular targets, symlinked parents, and hard links before combined publication; applicability is rechecked at installation boundaries. Merged publishable content is scanned for secrets unless the existing global override is explicitly enabled. No file values appear in conflict summaries.

Include order remains significant for policy review in this phase. The inventory covers `sync-policy.ts`, `sync-state.ts`, `remote-snapshot.ts`, config/review fingerprints, `settings-management.ts` stale-selection guards, `snapshot.ts` collection/filter/preservation, codec selection validation, all three backend metadata readers/writers, and selection/attention UI tests. Collection uses membership and sorts files, but ordered lists are also serialized into immutable metadata and captured review identities; those existing compatibility/freshness consumers have not been normalized. Order-only and membership changes retain the existing regression-tested review behavior.

## Authorization and lifecycle

| Setting/mode | Startup | Shutdown |
| --- | --- | --- |
| New opt-in absent/false | Existing `sync.automatic` observation policy only | Existing selected-content push when automatic + sessions are enabled; reload skips it |
| `sync.automaticTransfer: true`, TUI/RPC, idle | One observation and established-baseline conflict-free transfer attempt | Drain startup work before the existing shutdown policy |
| New opt-in enabled, agent busy | Wait for `agent_settled`; no polling/timer for the pending boundary | Clear pending work |
| Print/JSON | No new automatic startup observation/transfer | Existing shutdown behavior only |
| First sync, mismatched/legacy selection, missing established head | No automatic transfer; keep review attention | No overlapping startup transfer |
| Read/check/write/verify backend | Automatic transfer refuses; manual sync remains available with its capability disclosed | Existing shutdown behavior only |

Owned work starts after factory load, is keyed by `sessionManager`, and uses session ID/file, CWD, agent-root, signal, and captured config checks at mutation/review boundaries. It is cancelled/drained before foreground commands, a new agent run, session replacement, and shutdown. Read-only legacy checks are not cancelled merely because an agent run begins. The deadline starts with the owned check, not while waiting for idle. Policy/session/owner checks are repeated around remote, planning, commit, and apply boundaries. User Settings is a foreground command, so it drains work before private ordered saves; a cross-process settings change is detected at authorization boundaries, not through a watcher.

Idle is not proof that external writers are absent. Exact-path `withFileMutationQueue` coordinates Pi edit/write tools within this process. The operation lock coordinates cooperating pi-sync processes. Neither owns other Pi sessions, arbitrary editors, or malicious same-user filesystem changes. Apply checks preimages/postimages again at each installation boundary, but Node does not provide an atomic file-content compare-and-swap against uncoordinated external writers. Pause those writers when strong exclusion is required. Only this process's current session is protected.

Merged transfer never reloads resources, activates recovery copies, changes tools, or alters the model-visible prefix. Existing directional commands may offer a separate explicit resource-reload confirmation; the user’s confirmation remains required. Successful automatic transfers emit one summary; local-resource changes require an explicit later reload/restart/resume.

## Durable boundaries

A setup/backend/selection-bound `*.merge-journal.json` under the denied private state root stores before/after snapshots, the complete upload, expected/committed opaque head, original state fingerprint, backup location, and the effective session root when sessions are selected. Session collection, backup, preflight, apply, and recovery use that pinned root; replacement contexts must match it. Older merge journals without root evidence require directional recovery for selected sessions, never an inferred root. A final local hash/baseline check after durable journal publication refuses stale candidates before any backend attempt and retires those provably unpublished candidates. File permissions are `0600`; new private state subdirectories are `0700`. Sensitive content stays out of discoverable resource directories. Backend snapshot references, content IDs, and revisions remain distinct.

```mermaid
flowchart LR
  A[Validate baseline and reviewed inputs] --> B[Durable private backup]
  B --> C[Publish private merge journal]
  C --> D[Conditional or reviewed-capability remote publication]
  D --> E[Record committed head]
  E --> F[Guarded atomic local writes and deletes]
  F --> G[Publish accepted baseline]
  G --> H[Remove completed journal]
```

Temporary-file publication syncs file contents before rename. On POSIX, directory entries are synced after critical rename/delete boundaries, including newly created ancestor names before journal retirement. Windows retains Node's atomic-rename behavior but does not expose directory fsync; this is not a claim of power-loss durability on every filesystem. Snapshot backends keep their existing documented publication capabilities.

| Interruption | Recovery/result |
| --- | --- |
| Before journal publication | No remote/local mutation; baseline unchanged. |
| Proven pre-commit race with no possible activation | Remove candidate journal and re-read/replan, at most three attempts; review again if interactive. |
| Generic network, after-commit conflict, cancellation, or unknown publication outcome | Retain journal and backup; never blindly republish. |
| Candidate is current and immutable bytes match | Continue guarded local apply; do not publish again. |
| Uncommitted candidate is not active and original opaque revision/local bytes/state still match | Reconcile without transfer, clear the journal, return cancelled rather than false success. |
| Candidate/head/selection/state cannot be reconciled | Refuse automatic recovery; preserve evidence for review. |
| Some local files installed before failure | Each remaining target must equal its recorded preimage or postimage; roll forward idempotently, never restore old bytes over a newer edit. |
| Local apply complete but baseline write failed | Retain journal; verify current head/files, publish baseline, then remove journal. |
| Crash after baseline publication before journal removal | Reconciliation recognizes the recorded committed baseline and clears the redundant journal, even if a later remote head advanced; an unaccepted journal still retains evidence. |

Use `/sync sync` to reconcile a journal. If newer local or remote edits prevent that, preserve evidence, review `/sync diff`, then explicitly choose `/sync push --force` or `/sync pull --force`. Directional recovery revalidates the reviewed local bytes/current head and archives the old journal only after its chosen result and baseline are committed. It does not restore the stale merge preimage. Existing reviewed rollback also retains its normal backup/publication semantics.

Disabling the opt-in cancels pending work but does not undo completed transfers or delete evidence. Do not use an older binary while a journal is pending. Once recovery finishes, accepted state retains the existing hash-only schema; no new state interpretation is required for older readers. Original and resolved journals/backups are kept under denied storage, not loaded as resources. This phase does not add automatic backup pruning.

## Directional snapshot recovery

`snapshot/snapshot-transaction.ts` writes private version-4 journals with verified before/after hashes, bounded planned subtree evidence, and durable removal/replacement intent. Proven top-level regular-file case replacements use version 5: one entry records the original `target` and canonical `afterTarget`, with one backup and one physical Pi queue reservation. Pi resolves existing queue keys with `realpath`, so nesting both aliases would self-deadlock. Apply removes the original spelling and installs the canonical spelling; guarded recovery stages and restores the original spelling even when the bytes did not change. Independent aliases, symlinks, hard links, changed spellings, and multiple writes are not coalesced. It installs files through synced temporary-file renames. Backup files, nested directories, and the top-level `before` directory are synced before journal publication.

Before each recursive apply/recovery deletion, removal intent is published durably for the target and affected descendants; only absence gains recognition, never unknown content. Directory and symlink restoration are staged beside the target, checked against the backup, synced, revalidated, and renamed into place. Copy failure/cancellation leaves the live postimage unchanged; a rename interruption after deletion remains recoverable from the retained backup and intent.

A complete group is checked before recovery mutates any path. Each destructive boundary checks ownership and bytes again; backup hashing precedes the live-target observation, and both file and non-file restoration recheck immediately before removal. Current-session paths and unowned session roots are refused.

Startup recovery reads the journal first and resolves an explicit context root or configured fallback only when its entries target session storage. An unused custom pinned root remains syntactically validated but cannot authorize a path or block guarded agent-root-only restoration. Session targets still require owned roots and current-session protection. It does not parse unrelated or interrupted Pi settings to restore agent-root-only targets.

Legacy version-1 journals lack proven postimages and are retired only if every target still equals its backed-up preimage. Version-2 journals retain their original guarded image rules; known postimages can be restored through staging and upgraded removal evidence, but old missing intermediate states without durable intent still require manual review.

Newer bytes, missing/corrupted backups, malformed journals, and unknown intermediate directory states retain evidence rather than triggering a blind rollback. Interrupted preparation directories without a journal are preserved but never loaded as resources.

Preserve a blocked transaction and backup, close Pi, and review selected paths before restoring them manually. Do not downgrade while either journal format is pending. Pi can load resources before the lifecycle recovery hook; recovery does not undo cached or already-loaded code, so explicitly reload/restart after restored resources need to be used.

## PR #1455 review ledger

| Feedback | Independent evidence and relationship to the goal | Severity | Outcome |
| --- | --- | --- | --- |
| [Session-root transition](https://github.com/narumiruna/pi-extensions/pull/1455#discussion_r4186081768) | The new merge apply filters by hash differences, so switching roots skips equal session files and falsely accepts their baseline. Six manual/automatic default/custom transition regressions and a committed-journal regression fail against the reviewed implementation. | P1: accepted missing sessions can become remote deletions after restart. | Implemented: require directional review before publication or recovery apply; do not advance the baseline. |
| [Non-file recovery removal](https://github.com/narumiruna/pi-extensions/pull/1455#discussion_r4186081778) | The new guarded restore awaited backup hashing after reading the target and omitted the final non-file recheck. Deterministic directory, symlink, missing-preimage, and backup-hashing race regressions fail against the reviewed implementation. | P1: newer external bytes can be recursively removed. | Implemented: recheck bytes and ownership/cancellation before removal; hash backups before observing live targets; retain evidence on refusal. |
| Submitted review and conversation summary | Both are wrappers for the two inline findings and contain no additional technical proposal or unanswered question. The submitted review names the current reviewed commit. | Informational. | No separate code action required. |

Review-fix validation passed: `npm run check`; `npm test` (489 files / 6,313 tests); the two focused suites (46 tests); and an isolated built-package RPC readiness/EOF-shutdown smoke with no provider request. Eleven regressions were also run against the original reviewed code and failed as expected, then passed with the fixes.

### Review of commit `922909ab`

| Feedback | Evidence and scope | Severity | Outcome |
| --- | --- | --- | --- |
| [Persist merge session root](https://github.com/narumiruna/pi-extensions/pull/1455#discussion_r4186536746) | `completeJournal()` recomputes the active root; snapshots cannot identify a context-selected root. A missing addition can be installed in a different directory and accepted. This is introduced by merge recovery. | P1 | Already addressed in this review follow-up; deterministic regression coverage added. |
| [Configured startup recovery root](https://github.com/narumiruna/pi-extensions/pull/1455#discussion_r4186536759) | Startup passes only `sessionDirFromContext()` while snapshot creation uses the configured fallback. The new ownership check rejects legitimate external-root journals. | P2: persistent recovery/startup refusal, with backup retained. | Already addressed in this review follow-up; deterministic regression coverage added. |
| [Pre-publication local recheck](https://github.com/narumiruna/pi-extensions/pull/1455#discussion_r4186536766) | Backup and journal persistence follow the final hash check; the next validation checks config/owner but not local bytes. The new merged publication can commit a stale candidate. | P1 | Already addressed in this review follow-up; deterministic regression coverage added. |
| [Backup-directory fsync](https://github.com/narumiruna/pi-extensions/pull/1455#discussion_r4186536774) | Regular backup files are synced but their containing directory is not; the new durable journal can outlive its backup directory entries after power loss. | P1 | Already addressed in this review follow-up; deterministic regression coverage added. |
| [Directory replacement progress](https://github.com/narumiruna/pi-extensions/pull/1455#discussion_r4186536792) | Directory-to-file deletion leaves a missing target recognized only by the current call, not rollback/restart. The new guards refuse an owned interruption. | P1 | Already addressed in this review follow-up; deterministic regression coverage added. |
| [Staged directory restoration](https://github.com/narumiruna/pi-extensions/pull/1455#discussion_r4186536804) | Direct recovery `fs.cp()` exposes partial live data; the new image guards subsequently reject it. This worsens retry recovery despite intact backups. | P1 | Already addressed in this review follow-up; deterministic regression coverage added. |

The additional submitted review and updated automated summary are informational wrappers for these six findings, not separate technical requests. Earlier findings remain addressed and resolved; their evidence is unchanged. Sixteen new regressions fail against `922909ab` and pass with the follow-up. `test/recovery-durability.test.ts` covers configured/context roots, backup-directory fsync ordering and failure, file/directory deletion interruptions, immediate rollback, staged-copy/rename failures, cancellation, newer bytes, and retry. `test/merged-sync.test.ts` adds cross-context/legacy-root journal refusal and stale edits during backup/journal publication. These deterministic tests verify ordering and restart states, not physical power-loss behavior. POSIX fsync is exercised; Windows directory fsync remains unsupported. Final follow-up validation passed: `npm run check`; `npm test` (490 files / 6,333 tests); five focused suites (104 tests); and an isolated built-package RPC readiness, `/sync` registration, and EOF-shutdown smoke with no provider request. No item is deferred or awaiting a decision.

The original feedback inventory contained two inline threads, one submitted review, and one conversation summary. No out-of-scope item or missing decision was identified. The session-root class audit also found that filesystem preflight must use the same configured/context root as apply, including when settings are not selected; stable-custom-root apply and unsafe-parent tests cover that path. The recovery audit covers file and non-file removal branches as well as target observations inside `verifyTarget()`. These checks reduce stale-read windows but do not add atomic CAS against uncoordinated external writers.

### Review of commit `63cc880a`

| Feedback | Independent evidence and scope | Severity | Outcome |
| --- | --- | --- | --- |
| [Effective-root active-session protection](https://github.com/narumiruna/pi-extensions/pull/1455#discussion_r4186910298) | The merge pins an effective root but `protectedSessionPaths()` still uses only the context root; default-directory managers can select an external session file without changing their directory (`SessionManager.setSessionFile()` / `_setSessionFile()` preserves it). The new merge and journal apply can therefore overwrite active history under the configured root. | P1 | Already addressed: pass the pinned root into protection for planning and journal apply, and compare changed physical targets with the active file before publication/apply so plain virtual paths and symlink-root spellings cannot bypass protection. |
| [Pre-publication target aliases](https://github.com/narumiruna/pi-extensions/pull/1455#discussion_r4186910308) | Preflight checks each path independently; apply checks only changed-path realpaths. Two changed aliases block after publication, while one changed alias can instead install inconsistent bytes for an unchanged virtual alias. Both violate the merged-transfer safety goal. The complete before/after path union needs an identity check, not only changed paths. | P2 | Already addressed: shared preflight/apply identity checks reject aliases, including missing targets resolved through existing ancestors; repeat preflight after journal publication and retire a provably unattempted candidate if it becomes unsafe. |
| Class audit: directional protection under the same configured/default-manager mismatch | `7bd20c07^:packages/pi-sync/src/sync/sync-mutations.ts` already contains the context-only protection helper and both directional apply calls. These explicit pull/rollback paths have the same limitation independently of this PR; this follow-up changes only merged planning and journal recovery. | P1 | Valid but out of scope — deferred. Recommend a separate pull request for directional root/protection alignment; none was opened. |

The new submitted review and updated summary are informational wrappers for these findings. Earlier items remain addressed and resolved, with unchanged evidence reused. Twenty-five regressions fail against `63cc880a` and pass with the fixes.

`test/merge-target-safety.test.ts` uses a real default-directory `SessionManager` with an external active file, tests manual/automatic edit and deletion barriers plus committed-journal recovery for session, plain, and symlink-root virtual paths, and covers changed/unchanged/deleted/missing aliases, symlink-root aliases, alias introduction during journal publication, retained recovery evidence, and distinct physical targets with the same basename.

The scope audit checked every `protectedSessionPaths()` caller and both alias-validation boundaries; unchanged directional semantics remain the separately recommended follow-up above.

Final follow-up validation passed: `npm run check`; `npm test` (491 files / 6,359 tests); four focused suites (92 tests); and an isolated built-package RPC readiness, `/sync` registration, and EOF-shutdown smoke without a provider request. Windows, compiled runtime, live storage transfers, and physical power loss remain unverified; external-writer atomic CAS is not claimed.

### Review of commit `cfbf4762`

| Feedback | Independent evidence and scope | Severity | Outcome |
| --- | --- | --- | --- |
| [Quadratic transaction planning](https://github.com/narumiruna/pi-extensions/pull/1455#discussion_r4187442008) | `prepareTransaction()` performs a full `find()` and descendant `filter()` over writes per target, and rehashes each descendant for every ancestor. These recovery-evidence scans are introduced by this PR and make supported large directional transfers unnecessarily expensive. Application also repeatedly finds journal entries; delete pruning and deleted-ancestor checks scan the full delete list. | P2 | Already addressed: index images, descendants, outer deletes, and deleted writes once, and use exact journal-entry maps during apply. |
| [Cancellation in mutating preflight](https://github.com/narumiruna/pi-extensions/pull/1455#discussion_r4187442015) | `applyMergedSnapshot()` forwards neither lifecycle guard to `preflightSnapshotMutations()`, whose parent walk creates and fsyncs directories. Its post-preflight `validate()` is too late. The second caller, `applySnapshot()`, likewise has guards available but does not forward them. This PR introduces guarded transfer/recovery lifecycle contracts and exposes the unguarded helper to automatic work. | P2 | Already addressed: both callers forward lifecycle guards; parent lookup, mkdir, and directory fsync revalidate before later steps. |

The new submitted review and updated automated summary wrap these findings. Earlier thread outcomes and the pre-existing directional root-protection deferral are unchanged. `snapshot-transaction-plan.ts` preserves first-exact-write selection, descendant ordering/duplicates, native path-containment semantics, delete pruning, and deleted-write recognition. Its cost follows path depth and emitted evidence, not all target/write pairs. The target limit now refuses before queue reservation or private evidence creation. Repeated durable journal publication for removals and filesystem backup/hash work remain; this is not a claim that all transfer I/O is linear.

`test/snapshot-transaction-plan.test.ts` compares indexed evidence with the prior full-scan definitions across eight decision classes, checks maximum-width ancestor-operation counts and wide descendant output, verifies actual prepared journal images without repeated write-array scans, and covers over-bound refusal. `test/snapshot-preflight-guards.test.ts` covers abort/ownership replacement after lstat, mkdir, and fsync; pre-abort; ENOENT-shaped cancellation; delete-parent reads; deferred file-ancestor replacement; and guard propagation through both apply callers. Fourteen integration/guard regressions fail against `cfbf4762` and pass with the fixes. The already-started mkdir/fsync may finish; cancellation does not remove an already-created empty prefix, but no later parent creation or file installation proceeds.

The semantic audit covers both mutating preflight callers, every helper filesystem await, indexed evidence ordering and containment equivalence, deferred ancestor replacement, private evidence retention, and generated runtime imports. No new UI, setting, persistence schema, model-prefix transition, or background resource was introduced.

Final follow-up validation passed: `npm run check`; `npm test` (493 files / 6,386 tests); five focused suites (77 tests); package runtime build; and isolated built-package RPC readiness, `/sync` registration, lazy `/sync status` execution, and EOF shutdown without an agent run or provider request. Windows, compiled runtime, live storage transfers, and physical power loss remain unverified.

### Review of commit `7a107a01`

| Feedback | Independent evidence and scope | Severity | Initial outcome |
| --- | --- | --- | --- |
| [Retire removal intent before replacement](https://github.com/narumiruna/pi-extensions/pull/1455#discussion_r4190563930) | Version-3 removal authorization survives apply and recovery renames. A later deletion of the installed replacement is accepted as an owned missing intermediate and can resurrect the preimage. Clearing only after rename leaves the same crash window; partial directory postimage recognition can likewise mistake removed installed children for unfinished materialization. This is introduced by the PR's removal-progress protocol. | P1 | Already addressed: version-4 evidence durably retires removal authorization before materialization, gates missing/partial postimages, and refuses ambiguous older evidence. |

The new submitted review and updated summary are informational wrappers. The earlier directional root-protection deferral is unchanged. The audit covers every target-installing rename in apply and file/directory/symlink restoration, parent materialization, descendant progress, missing postimages, partial-tree recognition, cancellation/ownership, failed atomic journal publication, legacy upgrade, and retained private backups.

Version-4 `removalPending` recognizes a missing non-preimage only until replacement is armed. Apply retires all relevant removal intents in one durable batch before creating parents or installing writes; a one-use, non-persisted allowance for each not-yet-installed deleted write exists only in the uninterrupted original call, never rollback or startup recovery. Restoration retires intent before every installation and supersedes descendant progress before installing a complete parent preimage. `replacementStarted` makes incomplete directory postimages unsafe to adopt. Current complete preimages/postimages remain recoverable. Older missing or partial evidence cannot prove that replacement never committed and requires review; upgrading a legacy journal does not promote old removal markers into fresh authorization.

This necessarily narrows earlier retry guarantees: once materialization is armed, a missing or incomplete image is ambiguous even if the actual mkdir/rename never ran. Such failures retain backups for manual review instead of immediate restoration or automatic retry. Interrupted removal before arming, and staging-copy/cancellation failures before live removal, remain recoverable. This supersedes the earlier unconditional rename-failure retry claim without changing successful transfers, settings, model context, or the deferred directional protection policy.

`test/replacement-progress.test.ts` covers committed apply replacements followed by root/child deletion, intact postimage controls, committed file/directory/symlink restoration followed by deletion, older missing evidence, invalid/conflicting flags, failed retirement publication, and abort/owner loss after retirement. Fourteen of its fifteen tests fail against `7a107a01`; eight directly expose unsafe recovery or absent publication ordering, two check durable phase controls, two check the new parser's field validation, and two cover post-publication lifecycle guards. Existing interruption tests now assert conservative retention where the new boundary intentionally requires review.

Final validation passed: `npm run check`; `npm test` (494 files / 6,401 tests); five focused suites (89 tests); package build; an isolated built-package RPC readiness, `/sync` registration, lazy `/sync status`, and EOF-shutdown smoke without an agent run/provider request; and the fenced-code-aware required-heading audit across 36 package READMEs. `npm run package:pack -- sync` passed its dry run; inspected contents include the updated README/audit, source transaction module, and built runtime. README scope, warnings, relative links, and installed documentation were audited against `docs/readme-conventions.md`, with persistence/lifecycle checks against the extension and settings guides. No new setting, UI, dependency, entrypoint, or model-prefix transition was introduced. Windows, compiled runtime, live storage transfers, physical power loss, and arbitrary external-writer atomic CAS remain unverified or unclaimed.

### Review of commit `df3a1919`

| Feedback | Classification and evidence | Outcome |
| --- | --- | --- |
| [Armed removal before replacement](https://github.com/narumiruna/pi-extensions/pull/1455#discussion_r4190874837) | Conflicting safety requirements: a journal update and filesystem rename cannot be atomic together. If removal intent remains usable after rename, an external deletion after a committed replacement is indistinguishable from a crash before rename; the earlier regression in `replacement-progress.test.ts` demonstrates unsafe resurrection. Version-4 retirement before materialization deliberately withholds automatic recovery after an interrupted arm, retains the backup, and requires reviewed recovery. | Not actionable as proposed without violating newer-state protection. Do not claim automatic recovery for ambiguous missing or partial trees. |
| [Duplicate remote download](https://github.com/narumiruna/pi-extensions/pull/1455#discussion_r4190874847) | `syncBoth()` previously read a full remote snapshot before delegating to `mergeSync()`; established/manual and authorized automatic transfers now delegate before backend creation or remote observation. First-source and unconfigured automatic paths retain their existing selection checks. | Actionable; fixed, with a no-op single-read and a transfer planning/verification read-count regression. |
| [Invalid session settings](https://github.com/narumiruna/pi-extensions/pull/1455#discussion_r4190874850) | Malformed JSON, invalid UTF-8, non-object settings, and non-string session roots must not masquerade as an absent root. Parser errors must not quote private JSON. | Actionable; strict root extraction now refuses before merged publication, including committed journal recovery; invalid-input regressions cover no baseline advance. |
| [Cancelled task publishing attention](https://github.com/narumiruna/pi-extensions/pull/1455#discussion_r4190874858) | Task cancellation leaves the session signal alive until finalization. | Actionable; final attention publication now requires a live combined task signal except for an intentional timeout; a cancelled-transfer test observes zero publication. |
| [Apply-only stale head](https://github.com/narumiruna/pi-extensions/pull/1455#discussion_r4191215646) | The old candidate recorded a committed head even though no publication occurred. A new, validated `applyOnly` marker distinguishes it from an uncertain published candidate. If the remote advances while all local preimages and baseline still match, the candidate is retired without apply; after a partial apply, evidence is retained for review. No-op acceptance and post-apply baseline publication also recheck the remote head. | Actionable; journal-publication, post-apply, and no-op races have deterministic regressions. |
| [Quadratic merged apply lookups](https://github.com/narumiruna/pi-extensions/pull/1455#discussion_r4191215654) | The full-array `includes()`/`indexOf()` scans occur per changed path at the supported width. | Actionable; exact target membership and relative lookup are preindexed; the remaining filesystem and journal I/O is not claimed linear. |

### Review of commit `6474b65a` / PR head `4a756001`

| Feedback | Classification and evidence | Outcome |
| --- | --- | --- |
| [Completed merge with newer remote](https://github.com/narumiruna/pi-extensions/pull/1455#discussion_r4191674827) | `completeJournal()` previously compared the live head before recognizing the accepted baseline, although the durable baseline already records the committed revision. A later remote advance cannot undo that acceptance. | Actionable; check the recorded committed head's accepted-state fingerprint on a head mismatch before rejecting or attempting apply. Only retire matching completed journals; never accept an uncommitted or mismatched baseline. Published and apply-only completed-journal regressions and an unaccepted-journal control exercise the boundary. |
| [Recover invalid settings postimage](https://github.com/narumiruna/pi-extensions/pull/1455#discussion_r4191880420) | Startup read potentially half-applied `settings.json` before the pending transaction journal; this prevents guarded restoration of unrelated settings targets. | Already addressed by the latest follow-up: read the journal first and resolve/compare session roots only when entries target session storage. Interrupted settings restoration covers absent, default, and unused custom pinned roots; external and nested-agent session-target controls retain evidence on invalid settings. Targets within the recorded custom root still require session authorization even when that root lies inside the agent directory. The former unused-custom-root limitation is superseded. |
| [Case-only directional replacement](https://github.com/narumiruna/pi-extensions/pull/1455#discussion_r4191880425) | The queue detects two physical aliases on case-insensitive filesystems. Simply dropping the second queue key is unsafe: the preparation backs up both aliases as distinct targets; after deleting one, the other entry's recorded preimage is missing, and recovery expects two incompatible postimages at one inode. No case-insensitive filesystem is available on this runner. | Already addressed by the latest follow-up: coalesce only a proven regular-file top-level write/delete pair into one version-5 journal entry and backup, retain original/canonical spelling evidence, reserve its physical Pi queue once, and guard both apply and original-spelling restoration. Deterministic case-insensitive lookup tests cover equal/changed bytes, actual interrupted apply, recovery phases, newer edits/deletions, and unsafe pair classes. A native filesystem test is included but skips on this case-sensitive host; native Windows/macOS behavior remains unverified. |

The submitted reviews are wrappers without additional technical requests. The previous six findings were addressed in the current code but their threads had not yet been answered; the armed-removal suggestion conflicts with safe newer-state protection and is not adopted as proposed. The original twelve resolved threads retain their documented outcomes; the pre-existing directional protection deferral remains unchanged. No other changed path compares a committed journal head before testing completed baseline acceptance; the apply-only branch is also covered by the same completed-state check. No user settings schema, commands, UI, runtime loading, model-visible context, or journal schema changed. `npm run check` passed; all 76 pi-sync suites passed (1,270 tests); `npm test` completed with 6,611 passed and four unrelated `pi-worktree` failures on this host's Git 2.34.1 (`git worktree list -z` unsupported, sparse-index interpretation), so the root test gate is not claimed passing. Case-insensitive filesystem and physical power-loss verification remain outstanding.

Submitted review and conversation summary are informational wrappers for these six inline findings. Earlier feedback outcomes, the pre-existing directional root-protection deferral, and external-writer CAS limitations above remain unchanged. Existing apply-only journals without the new marker retain conservative ambiguous-outcome behavior; no older evidence is reinterpreted as proof of a non-publication. This ledger is a code-review record, not a claim that the GitHub threads are resolved.

Follow-up validation: `npm run check` passed, focused merged/automatic/selection suites passed (73 tests), package build and pack passed, and inspected tarball includes `dist/index.ts`, source and this audit without tests. An isolated Pi RPC package-directory load passed `get_commands` readiness, sync registration and EOF shutdown with `PI_OFFLINE=1` and no provider request. `npm test` ran 6,610 tests: 6,606 passed and four unrelated `pi-worktree` Git integration tests failed on the host Git 2.34.1 because it does not implement `git worktree list -z`; this is not a passing root test gate. Tests exercise cancellation, parser confidentiality, invalid UTF-8, pre-publication remote advancement, post-apply head advancement, safe retention after local apply, and no-op baseline revalidation. Windows and physical power-loss smokes remain unverified; no live-provider transfer was run.

### Review of commit `8d2b927e`

| Feedback | Classification and evidence | Outcome |
| --- | --- | --- |
| [Unused custom settings root](https://github.com/narumiruna/pi-extensions/pull/1455#discussion_r4192042942) | Same remaining failure class as the settings-postimage thread: no entry needs session-root authorization, but an unused custom root blocked restoration. | Already addressed by the session-target-only ownership check and custom-root regression; session targets still fail closed. |
| [Legacy remote selection](https://github.com/narumiruna/pi-extensions/pull/1455#discussion_r4192042949) | Legacy metadata is not authoritative selection compatibility. The old hard-coded planner authorization allowed adopting local policy without selection review. | Already addressed: pass actual compatibility into the planner; route legacy snapshots to a decision explaining unknown scope and explicit policy adoption, including `--yes` and automatic callers. Three planning regressions assert no publication, apply, journal creation, or baseline advance. Two recovery regressions refuse an unaccepted legacy apply-only journal while still retiring an already accepted completed baseline without overwriting newer content. |
| [Rollback force barrier](https://github.com/narumiruna/pi-extensions/pull/1455#discussion_r4192042953) | Rollback's compatibility flag reused the push/pull journal bypass without their reviewed recovery semantics. | Already addressed: rollback always requires no merge journal before snapshot reads, review, backup, apply, publication, or retirement. Tests cover both force values and retain exact evidence/newer local bytes. Only explicit push/pull retain the recovery bypass. |

The latest submitted review and conversation summary are informational wrappers. All 25 thread findings have recorded outcomes; the earlier armed-removal proposal remains conflicting with newer-state protection, not silently implemented. The pre-existing directional session-protection deferral is unchanged. The current failure-class audit covers every `requireNoMergeJournal()` caller, both merge planners, startup root authorization, every case-replacement backup/queue/install/recovery boundary, old journal versions, and cancellation/evidence retention. Version-5 naming evidence does not relax version-4 missing/partial-image refusal. No user setting, command route, UI component, model prefix, dependency, or entrypoint changed. Native case-insensitive filesystem and physical power-loss behavior are not claimed by the deterministic lookup fixture.

Latest follow-up validation: `npm run check` passed. Five focused suites passed (124 tests, one native-filesystem skip); the earlier whole-package run passed 1,296 tests, and the subsequent legacy-recovery and nested-root regressions pass in the final root run. `npm test` completed with 6,640 passed, four pre-existing `pi-worktree` Git integration failures, and one native case-insensitive filesystem skip. The failures remain attributable to this host's Git 2.34.1 (`worktree list -z` unsupported and sparse-checkout inventory interpretation); the root test gate is not claimed passing. Package build, pack dry-run/content inspection, and built-package RPC registration, lazy `/sync status`, and EOF shutdown passed without a provider request. The implementation plan retains these unavailable/failed validation paths rather than claiming native filesystem or full-suite success.

### Review of commit `df11c190`

| Feedback | Independent evidence, scope and severity | Outcome |
| --- | --- | --- |
| [Startup observation at commit](https://github.com/narumiruna/pi-extensions/pull/1455#discussion_r4192391510) | P2: startup retains a pre-transfer observation on committed apply/baseline failure because it omits the existing commit callback. This PR introduces automatic transfer. Foreground paths already clear at that boundary. | Already addressed: startup now clears the observation through `onCommit`, retaining useful precommit observations and existing cancellation/timeout policy. |
| [Cleanup versus rollback](https://github.com/narumiruna/pi-extensions/pull/1455#discussion_r4192391518) | P2: successful evidence removal followed by parent fsync failure enters apply rollback with no backup. This PR adds cleanup directory fsync. | Already addressed: completed apply cleanup is outside rollback; a durable version-6 completion marker precedes evidence deletion, and post-deletion directory fsync is best effort. File installation/restoration durability precedes cleanup; cleanup fsync failure cannot withhold acceptance or claim retained evidence. Cleanup deletion errors still propagate directly, without attempting rollback on potentially deleted backups. |
| [Recovery descendant indexing](https://github.com/narumiruna/pi-extensions/pull/1455#discussion_r4192391523) | P2: the new owned post-tree walk recursively hashes every directory subtree again and enumerates all expected paths per directory. This PR introduces that guarded recovery recognition. | Already addressed: build expected ancestor prefixes once, recurse into directories without hashing their complete subtree again, and hash only leaves. Each verification retains one complete current-image hash plus one leaf-only ownership walk. No whole-transfer linearity claim. |

Prior 25 thread outcomes and the separate pre-existing directional session-protection deferral are unchanged. The submitted review and automated summary are wrappers for these three findings, not additional requests. `automatic-transfer.test.ts` verifies precommit/committed failures and existing cancellation boundaries; `transaction-cleanup.test.ts` covers apply/restoration cleanup fsync errors, surviving completed evidence with newer/protected bytes, and irreversible deletion errors; `transaction-completion.test.ts` covers invalid flags, failed marker publication, and completed external-session cleanup without backup/settings authorization; `recovery-tree-index.test.ts` asserts constant expected-key enumeration and at most two live-leaf hashes per verification for wide/deep incomplete trees. Twelve new regressions fail against `df11c190` and pass with these fixes (the precommit control already passes). Cleanup fsync failure may leave obsolete evidence after physical power loss; a completed version-6 journal permits cleanup only and cannot overwrite newer bytes or active-session targets. The marker is published only after file/parent durability, before any evidence deletion; marker publication failure leaves the caller unaccepted and evidence available. Invalid or older-version completion flags fail closed. Completed cleanup does not require session-root authorization because it mutates no resource target; syntactic journal/root/path validation remains. Version-6 preserves version-5 naming and version-4 removal rules for uncompleted evidence. Do not downgrade with pending journals, as already documented. No claim of atomic filesystem/journal cleanup is made. No settings schema, UI component, command, loading, dependency or model-prefix transition changes.

Final semantic audit follows `docs/extension-conventions.md`, `docs/extension-settings.md`, root and package instructions: both commit boundaries/callback consumers; startup cancellation, timeout, session replacement and shutdown; all cleanup callers; deletion versus marker publication ordering; versions 1–6, naming and removal compatibility; completed-journal ownership and malformed-evidence refusal; tree completeness, symlink/unknown leaves and ancestor enumeration. Cleanup after completion mutates only owned private evidence; no stale resource target is touched after those awaits. Successful apply and restoration fsync resource parents before completion publication. Existing Changeset includes these fixes.

Validation: `npm run check` passes; final nine focused suites pass (153 tests, one native-filesystem skip); `git diff --check` passes. `npm test` completed with 6,653 passed, four unchanged `pi-worktree` Git 2.34.1 failures and one native-filesystem skip; the root gate is not green. One subsequent completed-external-session regression also passes in the final focused/compiler checks. Built-package RPC readiness, sync registration, lazy `/sync status` and EOF shutdown pass without provider requests. Initial check failed because dependencies were absent; `npm install` restored the declared dependencies without manifest/lockfile changes and reports three existing high-severity audit findings. No blanket audit fix was applied. Native case-insensitive filesystem, Windows/compiled runtime, physical power loss, live storage transfers and arbitrary external-writer CAS remain unverified/unclaimed. No release/publication/tag action was taken.

### Review of commits `3859ccdc` / `44ae3023`

| Feedback | Independent evidence, scope and severity | Outcome |
| --- | --- | --- |
| [Post-backup recovery pull freshness](https://github.com/narumiruna/pi-extensions/pull/1455#discussion_r4192618857) | P1: pending-journal force pull rechecks reviewed hashes before backup compression/fsync and apply-root resolution. A late edit becomes the transaction preimage and is overwritten, potentially after backup collection. The PR adds this reviewed journal-retirement path. | Already addressed: repeat head/local freshness checks after backup and apply-root resolution, immediately before commit/apply; validate mutation ownership around both reads. Ordinary pre-existing directional policy is not expanded. Nine regressions cover local edits/deletions and remote advancement at backup-file publication, backup-directory fsync and session-root settings reads; no commit callback, transaction creation, baseline advance or journal retirement occurs. |
| [Case spelling queue lifetime](https://github.com/narumiruna/pi-extensions/pull/1455#discussion_r4192618865) | P2: installed Pi registers existing paths by realpath and missing/ENOTDIR paths by resolved spelling; its registration is serialized, but queue keys are not inode identities. Coalescing retains only the original queue, so canonical-spelling writes in the missing interval can race installation. Recovery reverses the spellings and has the same failure class. The PR adds coalesced naming recovery. | Already addressed: after removal, reserve the distinct missing destination spelling while retaining the original physical queue, and require absence before reservation and again under the added queue. Earlier gap writers finish before reservation and cause refusal, even with recognizable pre/postimage bytes. Apply holds both keys through installation; recovery reverses them. Already-original or missing recovery targets do not reenter their existing queue. Failed case apply retains evidence instead of running rollback after releasing the extra queue; startup recovery reserves its own destination key. Twelve regressions use actual Pi queue registration with named built-in lookup bindings synchronized to the filesystem fixture: both spellings blocked through apply/recovery rename, six pre-reservation writer controls, and four abort/owner-change drain/release controls. Native case-insensitive storage remains unavailable; the native test is included and skipped, not claimed verified. |
| [Completed legacy cleanup](https://github.com/narumiruna/pi-extensions/pull/1455#discussion_r4192677975) | P2: version-1 entries have no postimage fields; completion labels them version 6, whose validator currently requires those fields. An interrupted cleanup becomes permanently rejected. This PR introduces the completion marker. | Already addressed: absent legacy image fields are permitted only with a valid version-6 completed marker; present fields still require valid types. Type/path/root/flag validation and uncompleted image requirements remain. Four interrupted version-1 completion tests cover file/directory/symlink/missing preimages, then newer bytes and cleanup retry; malformed completed and uncompleted-legacy controls retain evidence. |

Prior 28 outcomes and the out-of-scope pre-existing directional session-protection deferral are unchanged. The new reviews and automated summary wrap these three requests. No additional unanswered discussion item is present.

Failure-class audit: all `readMergeJournal()` freshness/bypass and directional retirement callers; actual Pi's existing-path, missing-path, ENOTDIR and serialized registration branches; both case spellings through apply and restoration; already-original/absent recovery states; recognizable late images; all queue callbacks and post-await abort/ownership checks; completion of versions 1–6 and present/missing/malformed evidence. All added queue scopes release on failure; cancellation drains a pending reservation after the preceding unrelated Pi writer finishes rather than cancelling that writer. Case-apply failure now deliberately retains evidence for guarded recovery instead of restoring under an incomplete queue set; adjacent code documents this compatibility deviation. No arbitrary external-process filesystem CAS or concurrent naming-change exclusion is claimed; pause uncoordinated writers for strong exclusion, as previously documented. No settings schema, journal version, command, UI, loading, dependency or model-prefix change; existing Changeset updated.

Validation: `npm run check` passes; ten focused suites pass (191 tests, one native-filesystem skip). Twenty-five new tests fail against reviewed head `44ae3023` and pass with the fix; 21 directly reproduce freshness/queue/legacy-cleanup failures and four verify ownership/cancellation at the added reservation (the old implementation lacks that boundary). Final full `npm test`: 6,681 passed, four unchanged `pi-worktree` Git 2.34.1 failures, one native-filesystem skip. The full local gate is not green. `git diff --check` passes. Windows/compiled runtime, native case-insensitive storage, physical power loss and live storage transfers remain unverified; no npm publication, tag or release dispatch occurred. Applicable guides: extension-conventions, extension-settings and package/root instructions, with Test/Review coverage for the touched mutation, lifecycle and persistence rules.

### Review of commit `8b604672`

| Feedback | Independent evidence, scope and severity | Outcome |
| --- | --- | --- |
| [Physical built-in case variants](https://github.com/narumiruna/pi-extensions/pull/1455#discussion_r4192931486) | P2: collection canonicalizes top-level names (including `append_system.md` to `APPEND_SYSTEM.md`), while merged apply owns only the exact canonical target. A remote edit can publish then block at its missing preimage; a remote deletion can silently accept absence while leaving the variant. This PR introduces merged apply instead of the directional case-variant handling. | Implemented: reject noncanonical directory-entry spellings for changed top-level targets before publication, after candidate-journal publication, and under the apply queues before planning and after mutating preflight. Preserve committed evidence; do not coalesce independent physical files or add an ambiguous naming journal. Normalize selected spelling without changing reviewed bytes before retrying. |
| Class audit: filesystem-specific test control | P2: the independent-spelling control added by this PR assumes a case-sensitive filesystem; it fails on this case-insensitive host before apply. The complementary native case-insensitive recovery test passes here. | Implemented: detect alias lookup and skip only the inapplicable case-sensitive control; retain both native filesystem branches. |

Prior 31 thread outcomes and the pre-existing directional session-protection P1 deferral are unchanged. The new submitted review and updated automated summary are informational wrappers with no separate technical request. The complete paginated GitHub evidence contains 32 threads and no truncated nested comment pages. The existing ledger and prior verification are reused for unchanged material; no duplicate replies are required.

The failure-class audit covers snapshot collection/filter canonicalization, directional variant deletion/coalescing, changed top-level additions/edits/deletions, the before/after union, both publication preflights, committed-journal apply, exact mutation queues, and post-await cancellation/ownership. All built-in top-level file names are tested, including lower-case canonical names with upper-case physical variants. Unchanged variants do not block independent canonical changes; nested resources do not enter this top-level naming policy. Directory entries are checked regardless of type, so symlinks/directories cannot masquerade as canonical absence. This deliberately refuses rather than automatically renaming variants; existing directional naming and journal formats are unchanged. The new directory read rechecks lifecycle guards on success and failure, including ENOENT-shaped cancellation. No new setting, UI, background resource, dependency, runtime-loading, metadata, or model-prefix transition. Arbitrary external naming/content CAS remains unclaimed.

`test/merge-case-variants.test.ts` verifies manual/automatic edit and deletion barriers with unchanged head/baseline/bytes, candidate-journal publication races, committed-journal retention and successful normalization/retry without republishing, every built-in file name, cancellation/owner loss, unchanged-variant compatibility, and spelling changes during awaited apply validation. Twenty of the 21 new tests fail against `8b604672` and pass with the fix; the unchanged-variant compatibility control already passes. The native case-insensitive installation/recovery test now runs and passes on this host; the case-sensitive control skips. Direct Vitest invocations of the older queue suite require the canonical TMPDIR used by `scripts/run-tests.mjs`: `/var` versus `/private/var` changes missing-path queue keys. Two preliminary queue assertions failed with the noncanonical direct harness and pass under the canonical harness, without changing production queue code. This does not claim arbitrary symlink-spelling queue exclusion.

Applicable guides: `docs/extension-conventions.md`, `docs/extension-settings.md`, root and package instructions. Test/Review cover exact mutation serialization, lifecycle guard propagation, private evidence retention and unchanged settings policy; Validator gates cover build, formatting, boundaries and workspace types. Existing Changeset updated. Prior loading/pack smoke evidence remains applicable because no loading or package metadata changed.

Final validation: `npm run check` passes; six focused suites pass (148 tests, one case-sensitive-filesystem control skip), including the native case-insensitive recovery smoke and the new 21-test suite. The complete `npm test` retry finished with 6,706 passed / two failed: the subsequently fixed case-sensitive control and an unrelated `pi-subagents` attachment-preflight timeout. The initial full attempt exceeded the Bash tool's 300-second deadline with several timeout failures; the completed retry ran as an owned non-interactive job without increasing any test timeout. Final whole-package run: 1,360 passed / one `git-routes.test.ts` five-second timeout / one filesystem-specific skip; that unchanged Git route suite passes in the final focused run. Neither the full root nor whole-package gate is claimed green. The unrelated attachment test and unchanged Git route are absent from this PR's diff; defer host-load/slow-test investigation to a separate PR if it recurs, without increasing per-test limits. `npm install` preserved the manifests/lockfile and reported three existing high-severity audit warnings; no blanket audit fix was applied. Windows/compiled-runtime, physical power loss, arbitrary symlink-spelling queue exclusion, external-writer CAS and live user-storage transfers remain unverified/unclaimed. No release, tag or npm publication action was taken.

### Review of commit `9e1c544a`

| Feedback | Independent evidence, scope and severity | Outcome |
| --- | --- | --- |
| [Cancelled startup progress](https://github.com/narumiruna/pi-extensions/pull/1455#discussion_r4193272689) | P2: the new automatic transfer's non-timeout cancellation bypasses attention publication, leaving its `sync ...` status indefinitely after `agent_start`. `interrupt()` drains without resetting attention; earlier lifecycle tests only checked after shutdown, which masked the defect. | Implemented: current task finalization clears the exact progress key on non-timeout cancellation without publishing captured attention. Replaced/aborted session continuations cannot clear replacement-owned presentation. |

All 32 previous thread outcomes remain unchanged; the refreshed evidence contains 33 threads with no missing thread or nested-comment pages. Submitted review and automated-summary updates are informational wrappers, not additional requests. The pre-existing directional session-protection P1 remains out of scope; recommend a separate PR, none created. Unchanged commits and previous validation evidence are reused without duplicate replies.

The cancellation-class audit covers foreground stop, agent interruption, replacement, shutdown, pending idle work, pre-progress cancellation, timeout publication, and the detached rejection/timer cleanup path. `automatic-transfer.test.ts` now checks cleanup immediately after drain, before shutdown can mask it; TUI/RPC stop and interrupt tests assert no attention publication, notification or widget, and aborted-session controls preserve newer presentation. Five assertions fail against `9e1c544a` and pass with the fix. Existing deadline and observation tests preserve timeout/failure publication. No settings read/write policy, UI component, command, loading, package metadata, dependency, journal format, model prefix or background resource changed.

Applicable guides: `docs/extension-conventions.md`, `docs/extension-settings.md`, root and package instructions. Test/Review cover task-owned status cleanup, exact-key ownership, cancellation and post-await session guards; Validator covers workspace builds, Biome, boundaries and types. Existing Changeset updated. `npm run check` passes; four focused lifecycle/observation suites pass (65 tests); full `npm test` passes (504 files, 6,711 tests, one native case-insensitive-filesystem skip on this Linux host). The first test attempt caught a test-only mock-context typing error, corrected before the successful full run. Prior unchanged runtime-loading/pack evidence remains applicable; no new smoke is required by this change. Windows/compiled runtime, native case-insensitive storage on this host, physical power loss, live storage transfers and arbitrary external-writer CAS remain unverified/unclaimed. No npm publication, tag or release dispatch occurred.

### Review of commit `5979a7b4`

| Feedback | Independent evidence, scope and severity | Outcome |
| --- | --- | --- |
| [Settings change after observation](https://github.com/narumiruna/pi-extensions/pull/1455#discussion_r4194333290) | P2: automatic startup captures an observation before lazy operations loading and transfer. A later config mismatch suppresses the failure notification but `finally` still publishes the old observation. Missing/invalid settings and successful-return paths share the finalization problem. This is introduced by automatic startup transfer. | Implemented: re-read settings and compare the captured check identity immediately before final attention publication. Changed, missing or unreadable settings reset the captured observation and presentation; unchanged settings retain existing timeout/failure behavior. |

The refreshed GitHub evidence contains 34 threads and no missing thread/nested-comment pages. Prior 33 outcomes are unchanged and reused without duplicate replies. The latest submitted review and automated summary are informational wrappers. The pre-existing directional session-protection P1 remains out of scope; recommend a separate PR, none created.

The failure-class audit covers every startup config-mismatch return, pre/post-observation lazy loading, transfer success/failure, absent/invalid settings, pending idle work, unchanged settings, cancellation, timeout, replacement, shutdown and detached cleanup. Settings reads retain the existing ordered reader; no settings writer, validation, precedence or persistence policy changes. Task/session ownership and the task's cancellation reason are rechecked after the added settings-read await. Cancellation during that read clears only owned progress; replacement preserves newer presentation. Settings mismatch resets attention because neither the captured observation nor setup-bound review presentation is authoritative for the new config. This is a point-in-time validation, not an atomic guarantee against subsequent external settings edits.

`automatic-transfer.test.ts` adds 18 TUI/RPC cases for selection changes, missing files and malformed files during lazy loading, transfer failure and successful return; existing presentation is explicitly simulated and must be cleared without stale publication or notification. All 18 fail against `5979a7b4` and pass with the fix. Two additional synchronized tests cancel or replace the session during the final settings read and verify post-await ownership. The first full test attempt exposed a fixture intercepting `fs.readFile` rather than the authoritative reader's `fs.open`/file-handle path; the fixture now synchronizes at the actual open boundary, with no timeout override. Existing cancellation, timeout, committed-failure and unchanged-observation tests remain passing.

Validation: `npm run check` passes; four focused suites pass (85 tests); final full `npm test` passes (504 files, 6,731 tests, one native case-insensitive-filesystem skip on this Linux host). Applicable guides: `docs/extension-conventions.md`, `docs/extension-settings.md`, root and package instructions. Test/Review verify exact status/widget cleanup, stale settings refusal and session/task lifecycle; Validator verifies builds, Biome, package boundaries and workspace types. Existing Changeset updated. No settings schema, command, UI component, dependency, entrypoint, journal, runtime-loading, package metadata or model-prefix change. Prior unchanged loading/pack evidence remains applicable. Windows/compiled runtime, native case-insensitive storage on this host, physical power loss, live transfers and external-writer CAS remain unverified/unclaimed. No npm publication, tag or release dispatch occurred.

### Review of commit `4a68fe7d`

| Feedback | Independent evidence, scope and severity | Outcome |
| --- | --- | --- |
| [Legacy automatic selection attention](https://github.com/narumiruna/pi-extensions/pull/1455#discussion_r4194606210) | P2: the new startup gate skips legacy selection while the existing advisory classifier stays quiet/one-sided. The corrected merge decision is unreachable during automatic startup. | Implemented: mark automatic-transfer observations and classify established legacy selection as review, with explicit policy-adoption guidance. Preserve observation-only defaults; never invoke a legacy automatic transfer. |
| [Prepared root-transition recovery](https://github.com/narumiruna/pi-extensions/pull/1455#discussion_r4194606219) | P2: the new guarded journal pins the destination session root before writing settings; startup derives the old root and blocks restoration, including untouched or partially installed preimages. | Implemented: version-7 reviewed settings-transition evidence binds the old root to the verified settings backup and the destination root to a stored postimage matching the recorded hash. Either reviewed root authorizes guarded recovery; arbitrary contexts, corrupted evidence and newer targets still refuse. Completed transition journals permit cleanup only. |
| [Native configured-path compatibility](https://github.com/narumiruna/pi-extensions/pull/1455#discussion_r4194606227) | P2: the new validator rejects safe names accepted by settings and actual snapshot collection on POSIX (colons, reserved Windows names, trailing spaces/dots, format/control characters). Established manual/automatic Sync delegates to merge and becomes unusable. | Implemented: use native filename restrictions rather than portable Windows restrictions on POSIX. Preserve traversal, backslash, private-storage, session-file and invalid UTF-16/NUL barriers; sanitize terminal controls only at display boundaries, without rewriting raw paths. |

Earlier 34 thread outcomes are unchanged. The refreshed evidence contains 37 threads with no missing thread or nested-comment pages. Submitted review and automated summary are wrappers, not extra requests. The pre-existing directional active-session/root alignment P1 remains out of scope; recommend a separate PR, none created.

The transition audit covers all preparation/apply/recovery/completion callers; versions 1–7; default/custom roots; absent/regular settings preimages; session-before-settings and settings-installed interruptions; canonical and case-coalesced settings entries; backup hash, canonical payload, strict JSON/UTF-8/root checks; arbitrary context/root and protected-target refusal; post-await ownership/cancellation; and completed cleanup without backups. A case replacement armed with a still-missing settings image remains ambiguous and retains evidence; transition authorization does not override the earlier removal/replacement safety policy. New transition authorization is private, bounded by the existing journal size limit, and never inferred from an unverified root alone. Older journals lack this explicit transition evidence and retain their existing root-ownership requirements; preserve blocked legacy evidence for reviewed recovery rather than upgrading it into invented authorization. Do not downgrade with pending journals. The protocol remains subject to documented external-writer CAS and power-loss limits.

The selection audit covers automatic opt-in versus legacy advisory defaults, no/local/remote changes, TUI widgets, RPC notifications, first-sync/missing/empty precedence, unchanged settings identities and no transfer invocation. The path audit compares include validation, collection, filtering, both apply validators, baseline/local/remote planner inputs and identity collisions. POSIX native colon/reserved/format/control names retain their raw bytes through no-op, remote edit and deletion merges. Snapshot path labels escape controls and display-direction characters as `\uXXXX` before multiline diff/apply/publication formatting, preventing forged review lines without changing raw payloads; other single-line terminal text masks those characters. Windows native-invalid names remain refused; Windows runtime verification is not claimed.

Regression comparison in an isolated detached worktree selects 78 cases: 67 fail against `4a68fe7d` and pass with the fixes (six legacy opt-in cases, four default/custom pre-settings recovery cases, 48 real native-path merges and nine planner cases); 11 compatibility controls already pass. Additional tests cover settings-preimage absence, case-coalesced settings, corrupt/malformed transition evidence, unrelated roots/managers, protected sessions, newer settings, read/stat cancellation and owner replacement, completed version-7 cleanup without backups, and escaped non-interactive path previews. Earlier regression runs selected the same class before the six newline-filename cases were added (61 failures / 11 passing controls). A preliminary case fixture double-spied the same rename function and recursively called its mock; the fixture now injects interruption through its existing lookup wrapper and proves the requested abort boundary was reached. No production workaround or timeout increase was used.

Final validation: `npm run check` passes; full `npm test` passes (507 files, 6,830 tests, one native case-insensitive-filesystem skip on this Linux host). Nine focused suites pass (353 tests, one native case-insensitive-filesystem skip). Package build, `npm run package:pack -- sync`, and a 194-file published-content inspection pass: built entrypoint, source helper and guidance are included, with tests, dependencies and operational state excluded. The fenced-code-aware required-heading audit passes for all 36 READMEs. An isolated built-package RPC smoke passes readiness, `/sync` registration, lazy `/sync status` and EOF shutdown without an agent run or provider request; generated-path/Jiti tests also pass in the root gate. Existing Changeset and README are updated; no new setting, dependency, public command, model-prefix transition or extension entrypoint is added. Test/Review cover the touched lifecycle, status, persistence and rendering MUST rules from `docs/extension-conventions.md`, `docs/extension-settings.md`, `docs/readme-conventions.md`, root and package instructions; Validator gates cover builds, Biome, boundaries and types. Windows/compiled runtime, native case-insensitive storage, physical power loss, live transfers and arbitrary external-writer CAS remain unverified/unclaimed. No publication, tag or release dispatch occurred.

### Review of commit `d91c3b4e`

| Feedback | Independent evidence, scope and severity | Outcome |
| --- | --- | --- |
| [Canonical tilde expansion](https://github.com/narumiruna/pi-extensions/pull/1455#discussion_r4195062272) | P2: transition evidence expands through `snapshot-paths.ts`'s raw HOME fallback, unlike apply/configured roots using `os.homedir()`. With HOME unset, a new transition can be omitted or record the wrong previous root. This is introduced by transition evidence. The HOME-empty subclaim is contradicted on this Node runtime: `os.homedir()` returns an empty string and both existing expansions resolve identically. | Implemented for the verified unset branch: reuse the authoritative session-path expansion for both images. Keep empty/present HOME as compatibility controls, not claimed reproductions. |
| [Missing settings postimage](https://github.com/narumiruna/pi-extensions/pull/1455#discussion_r4195062283) | P2: the new transition preparer requires a write, omitting settings deletions. A prepared transaction with an explicit/context-pinned default root then fails startup against still-live custom settings. Older unpinned default-root branches may already recover; do not claim every deletion branch fails. | Implemented: a planned settings deletion with recorded `afterImage: missing` stores an explicit null postimage marker, authorizing the default root while still verifying the settings backup and both reviewed roots. An empty file/string is not absence. |

Earlier 37 outcomes are unchanged; refreshed GitHub evidence contains 39 threads and no missing thread/nested-comment pages. The new submitted review and automated-summary update are informational wrappers. The pre-existing directional active-session/root protection P1 remains out of scope; recommend a separate PR, none created.

The settings-image audit covers regular and absent preimages; file, empty-file and missing postimages; write/delete/no-settings actions; tilde root/descendant expansion with unset/empty/present HOME; preparation/deletion/partial installation; missing/existing session preimages; completed cleanup; and invalid/corrupt/newer/unrelated-root refusal. Explicit null is bound to the entry's missing postimage and the default destination root, never inferred from an empty file or an unowned path. Existing version-7 string payloads retain their interpretation; older code can fail closed on the new null branch, so the existing pending-journal downgrade warning remains applicable. Ownership/cancellation guards, private journal bounds, backup durability, group recognition, naming/armed-gap restrictions and root/path validation remain unchanged.

`root-transition-images.test.ts` adds 27 cases: twelve canonical-root evidence cases (unset/empty/present HOME, `~`/`~/stored`, both directions), four actual unset-HOME interrupted transactions, six deletion interruptions with existing/missing session preimages, four invalid/newer-image/root controls, and completed missing-postimage cleanup without backups. OS fallback is mocked into an isolated temporary home for real transaction tests; empty-HOME/CWD-root cases only exercise private evidence and never create session files under CWD or the real OS home. An isolated comparison selects 18 cases: ten fail against `d91c3b4e` and pass with the fix (four canonical evidence mismatches, four interrupted tilde transitions and two prepared deletions); eight empty/present-HOME compatibility controls already pass. The empty-HOME subclaim is explicitly not adopted as a reproduction on this runtime.

Validation: `npm run check` passes; full `npm test` passes (508 files, 6,857 tests, one native case-insensitive-filesystem skip); five focused recovery suites pass (116 tests, one skip). Package build, `npm run package:pack -- sync`, and a 194-file published-content inspection pass; the built entrypoint, both root modules and audit are present, while tests, dependencies and operational state are excluded. An isolated built-package RPC smoke passes readiness, registration, lazy `/sync status` and EOF shutdown without an agent run or provider request. Generated import/Jiti tests pass in the root gate; README and package metadata/loading entrypoints are unchanged. Applicable guides: `docs/extension-conventions.md`, `docs/extension-settings.md`, root and package instructions. Test/Review cover settings-image validity, root authorization, lifecycle guards and evidence retention; Validator covers builds, Biome, boundaries and types. Existing Changeset updated. No settings writer/precedence policy, dependency, command, entrypoint, journal version, UI or model-prefix transition is added. Windows/compiled runtime, native case-insensitive storage, physical power loss, live transfers and external-writer CAS remain unverified/unclaimed. No npm publication, tag or release dispatch occurred.

## Verification and guides

`test/file-merge-planner.test.ts` covers decision classes, exhaustive hash/absence combinations, checksum/path validation, protected sessions, collision groups, determinism, and immutability. `test/merged-sync.test.ts` covers independent edits/additions/deletions, complete conflict barriers, stale review/apply, private evidence, finite race retry, uncertain publication, cancellation, partial local installation, baseline-write failure, restart-style recovery, and reviewed directional recovery. Existing Git/WebDAV/S3 contract suites verify opaque revisions, conditional/conflict phases, and unknown outcomes; new automatic policy refuses the weak S3 publication capability instead of claiming CAS.

`test/automatic-transfer.test.ts` covers mode gates, no modal prompts, busy/idle scheduling, one attempt, foreground/agent/session/shutdown draining, policy disablement, first-source/selection/head barriers, ordered validation, private permissions, and unknown-field preservation. `test/merge-review.test.ts` verifies cell-exact TUI review with non-default confirmation/cancellation bindings and hard `Ctrl+C`, RPC pagination without custom TUI, and stale-owner refusal. `test/snapshot-transaction-recovery.test.ts` verifies legacy/modern image recognition, newer-write refusal, backup/root/parser protection, cancellation, and file/directory transitions. Existing snapshot mutation tests inject failures at atomic rename/delete boundaries. Installed Pi’s Node Jiti aliases and compiled-runtime virtual modules resolve the coding-agent root to the running Pi, so the imported public queues are the runtime’s queues; packaged peers declare the supported runtime floor.

Existing settings suites retain atomic failures, malformed-file protection, cross-process locking, and save rollback; generated-runtime suites exercise registration, lifecycle, exact emitted imports, and a representative lazy boundary through Pi Jiti.

The semantic review follows `docs/extension-conventions.md`, `docs/extension-settings.md`, `docs/readme-conventions.md`, and `packages/pi-sync/AGENTS.md`. Repository gates, package build/pack inspection, and the isolated package-directory RPC smoke are recorded in the Phase 1 PR. No live-provider or user-storage transfer is needed to verify this deterministic contract. Publishing, tags, and release dispatch remain outside authorization.
