Files
CleanMM/Docs/ADR/ADR-007-Recovery-Retention-Enforcement.md
zhukang 1cb9a42c7b fix: enforce recovery retention and fail-closed restore semantics
- prune expired recovery items on load/save and reject expired restores at worker boundary
- add restoreExpired and restoreConflict protocol/application error mapping
- disable expired restore actions in History and reload persisted state after restore failures
- add recovery expiry/conflict coverage plus sync protocol, architecture, state-machine, and recovery contract docs
- wire AtlasAppTests into the shared Xcode scheme and add app-layer regression coverage for expired restore reload behavior

Refs: ATL-221 ATL-222 ATL-223 ATL-224 ATL-225, vibe-kanban SID-9
2026-03-13 14:38:50 +08:00

33 lines
2.0 KiB
Markdown

# ADR-007: Recovery Retention Enforcement
## Status
Accepted
## Context
Atlas already documents a retention-window recovery model, including `RecoveryItem.expiresAt`, the `expired` task-state concept, and `restore_expired` in the error-code registry. The shipped worker, however, still restores items solely by presence in `snapshot.recoveryItems`. That means an expired entry can remain visible and restorable if it has not yet been pruned from persisted state.
This creates a trust gap in a release-sensitive area: History and Recovery can claim that items are available only while the retention window remains open, while the implementation still allows restore after expiry.
## Decision
- Atlas must treat expiry as an enforced worker and persistence boundary, not only as UI copy.
- `AtlasWorkspaceRepository` must prune expired `RecoveryItem`s on load and save so stale entries do not remain in active recovery state across launches.
- `AtlasScaffoldWorkerService.restoreItems` must recheck expiry at request time and fail closed before any restore side effect.
- Restore rejections must use stable restore-specific protocol codes for expiry and restore conflicts.
- Presentation may add defensive restore disabling for expired entries, but worker enforcement remains authoritative.
## Consequences
- Recovery behavior now matches the documented retention contract.
- Expired entries stop appearing as active recovery inventory after repository normalization.
- Restore batches remain fail closed: if any selected item is expired, the batch is rejected before mutation.
- Protocol consumers must handle the additional restore-specific rejection codes.
## Alternatives Considered
- Narrow docs to match current behavior: rejected because it preserves an avoidable trust gap.
- Enforce expiry only in the restore command: rejected because stale entries would still persist in active recovery state.
- Fix only in UI: rejected because restore is ultimately a worker-boundary guarantee.