# Scratchpad — #791 Upgrade config protection (ms-791 worker lane) **Lane:** web1:ms-791 → reports to MS-LEAD (web1:mosaic-100). Do NOT contact Jason/Mos directly. **Worktree:** `/home/hermes/agent-work/stack-agents-dir-791`, branch `feat/791-upgrade-config-protection` off `origin/main` `9745bc3f` (verified exact head). ## Mission prompt (verbatim intent) Protect operator-owned config under `~/.config/mosaic` from framework-upgrade wipes. Ratified combination (Mos-approved, do NOT re-litigate): (b) strict ownership separation [PRIMARY] + (a) transactional pre-update snapshot [safety net] + (d) regeneration-from-SSOT [recovery]. (c) periodic timer DEFERRED. HARD GATE: unit test that an upgrade run touches NO path outside the manifest. Design-first: write design doc, send to MS-LEAD, WAIT for confirmation before impl. ## Session 1 (2026-07-16) — Phase 1 design ### Evidence gathered (wipe mechanism, file/line) - `mosaic update` → `update-checker.ts:509` `buildReseedCommand` → `bash install.sh` (`MOSAIC_SYNC_ONLY=1`, `MOSAIC_INSTALL_MODE=keep`). - Wipe = `packages/mosaic/framework/install.sh:199` `rsync -a --delete` + `PRESERVE_PATHS` denylist (`install.sh:47`). cp-fallback `install.sh:223` `find ... -exec rm -rf`. - Denylist gaps → WIPED: `agents/*.conf`, `policy/*.md`, `*.local.md`, harvester/SOP, `tools/_lib/credentials.json`. - Stale comment `update-checker.ts:492` claims `*.local` preserved — PRESERVE_PATHS has no such entry. - TS path `file-adapter.ts:157` → `file-ops.ts:66` `syncDirectory` = non-destructive copy-overlay, BUT its preserve list (`file-adapter.ts:164`) already DRIFTED from install.sh (missing `fleet/backlog`, `fleet/roles.local`). Evidence for single shared manifest SSOT. - Existing snapshot (`install.sh:76`) = /tmp, crash-trap only, deleted on success → inadequate; no `mosaic restore`. - `fleet-reconciler.ts:93,234` already has `regenerate-projections-from-roster` phase separate from lifecycle → `mosaic fleet regen` = thin projection-only wrapper (no restart), no FCM-M4/M5 preemption. ### Design decisions - **(b)** Invert to allow-list: shared `framework/framework-manifest.json` (framework globs + operatorReserved carve-outs); resolve per-path, deny-wins; **UNKNOWN ⇒ operator (fail-safe)**. Mechanism: drop `--delete`; non-deleting bulk copy + explicit manifest-scoped prune pass (iterate framework globs only → operator/unknown structurally unreachable). Pure prune-planner fn for tests. - **(a)** Snapshot to `~/.local/state/mosaic/backups/pre-update-/` 0700/0600, retention N=5, post-sync verify+restore, `mosaic restore --list/--from`. No secret values in output. - **(d)** `mosaic fleet regen` projection-only, preview-first, never restart. - HARD GATE test includes a deliberately-unanticipated operator path to prove fail-safe default. - **PR split:** PR1 manifest+guard (root fix, ships alone) → PR2 snapshot/restore (secrev) → PR3 regen+docs. PR2/PR3 depend on PR1. ### Status Design doc written: `docs/design/791-upgrade-config-protection.md`. Sent to MS-LEAD. **WAITING for confirmation before Phase 2 implementation.** No impl started.