---
phase: 07-archive-lifecycle-controls
plan: 01
subsystem: ui
tags: [rust, archive, state-machine, tdd, filter, lifecycle]

# Dependency graph
requires:
  - phase: 06-advanced-discovery-fuzzy-search
    provides: filter pipeline (filter_cards_by_search, filter_cards_by_status, ModeState, DashboardCardViewModel)
provides:
  - ArchiveState enum (Active, ToBeArchived, Archived) in archive.rs
  - ArchiveRecord and ArchiveStore in archive.rs
  - compute_archive_state (auto-archive trigger, one-way latch, manual override)
  - promote_tba_if_due (12h TBA-to-Archived promotion)
  - filter_cards_by_archive returning (visible_cards, hidden_archived_count)
  - archive_state field on DashboardCardViewModel
  - show_archived field on ModeState
  - archive_store field on DashboardRuntime with toggle/manual archive methods
  - Archive UI callbacks and opacity bindings in card.slint / dashboard.slint
affects:
  - 07-02 (archive UI wiring in main.rs and Slint callbacks)
  - 07-03 (toast notifications for archive actions)

# Tech tracking
tech-stack:
  added: []
  patterns:
    - Three-state lifecycle enum with Default=Active and Copy derive
    - One-way latch pattern: auto_archived flag prevents state reversion
    - Sticky override flag: manual_unarchive_override survives syncs
    - filter returns (Vec<T>, usize) tuple for paired visible/hidden data

key-files:
  created:
    - crates/app/src/dashboard/archive.rs
  modified:
    - crates/app/src/dashboard/view_model.rs
    - crates/app/src/dashboard/discovery.rs
    - crates/app/src/dashboard/mod.rs
    - crates/app/src/dashboard/projection.rs
    - crates/app/src/main.rs
    - crates/app/ui/card.slint
    - crates/app/ui/dashboard.slint

key-decisions:
  - "archive_state field on DashboardCardViewModel always defaults to Active; ArchiveStore applies actual state post-projection"
  - "filter_cards_by_archive returns (Vec, usize) tuple so callers get hidden count without a separate query"
  - "ArchiveState derives Default=Active and Copy so it can be embedded in ViewModel without heap allocation"

patterns-established:
  - "Archive filter slots into filter pipeline after status filter, before Slint model push"
  - "ArchiveStore injected into DashboardRuntime alongside card_ui and discovery_state"
  - "TBA state is always visible (show_archived has no effect on TBA); only Archived state is hidden"

requirements-completed: [ARCH-01, ARCH-02, ARCH-03]

# Metrics
duration: 5min
completed: 2026-03-11
---

# Phase 7 Plan 1: Archive State Module Summary

**Three-state archive lifecycle (Active/ToBeArchived/Archived) with auto-archive trigger, one-way latch, manual override, 12h TBA promotion, and archive filter — all TDD-verified with 16 unit tests.**

## Performance

- **Duration:** 5 min
- **Started:** 2026-03-11T07:17:27Z
- **Completed:** 2026-03-11T07:22:30Z
- **Tasks:** 1 (TDD: RED + GREEN combined)
- **Files modified:** 8

## Accomplishments
- Created `archive.rs` with complete archive state machine: `ArchiveState`, `ArchiveRecord`, `ArchiveStore`, `compute_archive_state`, `promote_tba_if_due`, `filter_cards_by_archive`
- Extended `DashboardCardViewModel` with `archive_state: ArchiveState` field (defaults Active)
- Extended `ModeState` with `show_archived: bool` field (defaults false via Default derive)
- Added `archive_store: ArchiveStore` to `DashboardRuntime` with `toggle_show_archived`, `manual_archive`, `manual_unarchive`, `archive_now`, `undo_archive` methods
- Wired `archive_state: ArchiveState::Active` into `projection.rs` project_snapshot
- All 16 archive unit tests pass; full suite: 91 lib tests + 12 integration tests, 0 failures

## Task Commits

1. **Archive state module with TDD** - `14d1546` (feat)

## Files Created/Modified
- `crates/app/src/dashboard/archive.rs` - Complete archive module: types, store, compute logic, filter
- `crates/app/src/dashboard/view_model.rs` - Added `archive_state: ArchiveState` field with Active default
- `crates/app/src/dashboard/discovery.rs` - Added `show_archived: bool` to ModeState; updated test helpers
- `crates/app/src/dashboard/mod.rs` - Added `pub mod archive`, re-exports, `archive_store` on DashboardRuntime, archive methods
- `crates/app/src/dashboard/projection.rs` - Set `archive_state: ArchiveState::Active` in project_snapshot
- `crates/app/src/main.rs` - Added `archive_state: 0` to seed CardData structs; mirrored in card_data_to_filter_model
- `crates/app/ui/card.slint` - Added `archive-state` property, opacity binding, archive/unarchive hover controls, callbacks
- `crates/app/ui/dashboard.slint` - Added `archive-state` to CardData struct, hidden-archived-count/current-show-archived properties, archive callbacks

## Decisions Made
- `archive_state` defaults to `ArchiveState::Active` in `project_snapshot`; actual state applied post-projection by `ArchiveStore` (matches plan spec)
- `filter_cards_by_archive` returns `(Vec<DashboardCardViewModel>, usize)` tuple — hidden count is Archived only, not TBA
- `ArchiveState` derives `Copy` to match the embedding pattern of other small enums in the view model

## Deviations from Plan

### Auto-fixed Issues

**1. [Rule 2 - Missing Critical] Added `archive_state: 0` to seed CardData in main.rs**
- **Found during:** Implementation (post-archive.rs creation)
- **Issue:** Slint `CardData` struct already had `archive-state: int` field defined; seed card initializers missing the field caused compile error
- **Fix:** Added `archive_state: 0` to all three seed `CardData` structs; updated `card_data_to_filter_model` to mirror archive_state from CardData into ViewModel
- **Files modified:** `crates/app/src/main.rs`
- **Verification:** `cargo test -p app` compiles and all tests pass
- **Committed in:** `14d1546` (part of task commit)

**2. [Rule 3 - Blocking] Updated three test helper constructors in discovery.rs**
- **Found during:** After adding `archive_state` to DashboardCardViewModel struct
- **Issue:** Three `make_card*` test helper functions constructing `DashboardCardViewModel` struct literals failed to compile (missing new field)
- **Fix:** Added `archive_state: super::super::ArchiveState::Active` to each test helper
- **Files modified:** `crates/app/src/dashboard/discovery.rs`
- **Verification:** `cargo test -p app` passes with 91 tests
- **Committed in:** `14d1546` (part of task commit)

---

**Total deviations:** 2 auto-fixed (1 missing critical, 1 blocking)
**Impact on plan:** Both fixes necessary for compilation correctness. No scope creep.

## Issues Encountered
None beyond the above auto-fixed struct literal breakage from adding the new field.

## Next Phase Readiness
- Archive state logic is complete and fully tested
- UI callbacks and opacity bindings in place in card.slint and dashboard.slint
- Ready for Phase 7 Plan 2: wiring archive actions in main.rs callbacks and implementing the ChipBar "Show archived" toggle

## Self-Check: PASSED

- archive.rs: FOUND
- 07-01-SUMMARY.md: FOUND
- commit 14d1546: FOUND

---
*Phase: 07-archive-lifecycle-controls*
*Completed: 2026-03-11*
