ralph-loop[epic-a-to-d-mainline]: iteration 3

This commit is contained in:
zhukang
2026-03-23 17:40:07 +08:00
parent 78ecca3a15
commit 9cd8d593fb
6 changed files with 63 additions and 4 deletions

View File

@@ -0,0 +1,31 @@
# ADR-008: Versioned Workspace State and Recovery Payload Compatibility
## Status
Accepted
## Context
Atlas persists a local workspace-state file and stores recovery payloads for both `Finding` and app uninstall flows. Those payloads have already evolved in place: app recovery entries gained uninstall evidence, and the active roadmap now explicitly calls for payload stability, older-state compatibility, and more trustworthy history behavior.
Without an explicit persistence envelope, Atlas can only rely on best-effort shape decoding. That makes future recovery hardening riskier and leaves migration implicit. The `Apps` flow also risks showing stale preview or stale footprint counts after restore unless restored app payloads are reconciled with fresh app inventory.
## Decision
- Atlas persists workspace state inside a versioned JSON envelope containing `schemaVersion`, `savedAt`, `snapshot`, `currentPlan`, and `settings`.
- Atlas continues to decode legacy top-level `AtlasWorkspaceState` files and rewrites them into the current envelope after a successful load when possible.
- `AppRecoveryPayload` carries an explicit `schemaVersion` and remains backward-compatible with the older raw-`AppFootprint` recovery payload shape.
- App restore flows clear stale uninstall preview state and refresh app inventory before the `Apps` surface reuses footprint counts.
## Consequences
- Atlas now has an explicit persistence contract for future migration work instead of relying on implicit shape matching alone.
- Older state files remain loadable while the repo transitions to the versioned envelope.
- App recovery payloads become safer to evolve because compatibility is now a stated requirement.
- The `Apps` surface becomes more trustworthy after restore because it no longer depends only on stale pre-uninstall preview state.
## Alternatives Considered
- Keep the unversioned top-level state file and rely on ad hoc per-type decoding: rejected because it scales poorly as recovery payloads evolve.
- Break compatibility and require a fresh state file: rejected because it damages trust in `History` and `Recovery`.
- Refresh app inventory only on explicit user action after restore: rejected because it leaves a visible stale-evidence gap in a trust-critical workflow.

View File

@@ -39,7 +39,7 @@
### Infrastructure
- XPC transport
- JSON-backed workspace state persistence
- Versioned JSON-backed workspace state persistence with legacy-shape migration on load
- Recovery-state normalization that prunes expired recovery entries on load/save
- Logging and audit events
- Best-effort permission inspection
@@ -56,6 +56,7 @@
- Release-facing execution must fail closed when real worker/adapter/helper capability is unavailable; scaffold fallback is development-only by opt-in
- Smart Clean now supports a real Trash-based execution path for a safe structured subset of user-owned targets, plus physical restoration when recovery mappings are present
- Restore requests recheck expiry and destination conflicts before side effects, so expired or conflicting recovery items fail closed
- App recovery payloads now carry an explicit schema version, and app restores should be followed by inventory refresh so the `Apps` surface does not keep stale footprint evidence
## Process Boundaries

View File

@@ -67,6 +67,13 @@
- `Storage treemap`, `Menu Bar`, and `Automation` remain out of scope unless the decision log is updated explicitly
- Atlas should compete as an `explainable, recovery-first Mac maintenance workspace`, not as a generic all-in-one cleaner
### D-011 Versioned Workspace State and Recovery Payload Compatibility
- Persisted workspace state uses a versioned JSON envelope instead of an unversioned top-level payload
- Atlas must continue decoding older top-level workspace-state files and rewrite them into the current envelope when possible
- App recovery payloads carry an explicit schema version and must remain backward-compatible with legacy app-only recovery payload shapes
- App payload restores must refresh app inventory before `Apps` reuses footprint counts or uninstall preview state
## Update Rule
Add a new decision entry whenever product scope, protocol, privilege boundaries, release route, or recovery model changes.

View File

@@ -8,7 +8,7 @@
## Protocol Version
- Current implementation version: `0.3.1`
- Current implementation version: `0.3.2`
## UI ↔ Worker Commands
@@ -127,9 +127,26 @@
### AppRecoveryPayload
- `schemaVersion`
- `app`
- `uninstallEvidence`
## Workspace State Persistence
Atlas persists local workspace state in a versioned JSON envelope:
- `schemaVersion`
- `savedAt`
- `snapshot`
- `currentPlan`
- `settings`
Compatibility rules:
- legacy top-level `AtlasWorkspaceState` files must still decode on load
- after a successful legacy decode, Atlas may rewrite the file into the current versioned envelope
- legacy app recovery payloads that stored a raw `AppFootprint` must still decode into the current `AppRecoveryPayload` shape
### AtlasSettings
- `recoveryRetentionDays`
@@ -154,7 +171,8 @@
- `health.snapshot` is backed by `lib/check/health_json.sh` through `MoleHealthAdapter`.
- `scan.start` is backed by `bin/clean.sh --dry-run` through `MoleSmartCleanAdapter` when the upstream workflow succeeds. If it cannot complete, the worker now rejects the request instead of silently fabricating scan results.
- `apps.list` is backed by `MacAppsInventoryAdapter`, which scans local app bundles and derives lightweight leftover counts suitable for interactive refresh.
- The worker persists a local JSON-backed workspace state containing the latest snapshot, current Smart Clean plan, and settings, including the persisted app-language preference.
- The worker persists a versioned local JSON workspace state containing the latest snapshot, current Smart Clean plan, and settings, including the persisted app-language preference.
- Legacy top-level workspace-state files are migrated on load into the current versioned envelope when possible.
- The repository and worker normalize recovery state by pruning expired `RecoveryItem`s and rejecting restore requests that arrive after the retention window has closed.
- Atlas localizes user-facing shell copy through a package-scoped resource bundle and uses the persisted language to keep summaries and settings text aligned.
- App uninstall can invoke the packaged or development helper executable through structured JSON actions.
@@ -166,3 +184,4 @@
- `executePlan` is fail-closed for unsupported targets, but now supports a real Trash-based execution path for a safe structured subset of Smart Clean items.
- `recovery.restore` can physically restore items when `restoreMappings` are present; otherwise it falls back to model rehydration only.
- `recovery.restore` rejects expired recovery items with `restoreExpired` and rejects destination collisions with `restoreConflict`.
- App payload restores should be followed by app-inventory refresh so the `Apps` surface does not reuse stale uninstall preview or stale footprint counts after recovery.

View File

@@ -67,4 +67,5 @@
- `execute_uninstall` removes an app from the current workspace view and creates a recovery entry.
- `restore` can physically restore items when structured recovery mappings are present, and can still rehydrate a `Finding` or an app payload into Atlas state from the recovery payload.
- `restore` must reject expired recovery items before side effects and must fail closed when the original destination already exists.
- When `restore` rehydrates an app payload, the `Apps` surface should refresh inventory before presenting footprint counts or a new uninstall preview.
- User-visible task summaries and settings-driven text should reflect the persisted app-language preference when generated.