# Phase 10: Refresh & Auto-Archive Runtime Wiring - Context

**Gathered:** 2026-03-20
**Status:** Ready for planning

<domain>
## Phase Boundary

Wire the existing dead refresh UI controls (refresh-all button, per-card Refresh menu item) to the tested RefreshDispatcher, and hook auto-archive computation into the card load/rebuild pipeline. This phase closes 3 partial requirements (DATA-05, DATA-06, ARCH-02) and 3 broken flows identified during milestone audit.

</domain>

<decisions>
## Implementation Decisions

### Refresh callback wiring
- Refresh-all button `clicked` handler calls `dispatch_refresh_all` (already exists in main.rs)
- Per-card Refresh menu item calls `RefreshDispatcher::dispatch(RefreshCommand::RefreshRecipient { recipient_id })`
- After refresh-all: re-fetch all snapshots via `fetch_card_snapshots`, full card model rebuild, then re-run the complete filter/sort pipeline (search, status, archive filter, discovery sort)
- After per-card refresh: re-fetch just that recipient's snapshot and update its entry in the Slint model in-place (no full rebuild)
- NoopClient refresh methods succeed and re-serve existing seed snapshots (so refresh flow is testable during development)

### Auto-archive hook timing
- `compute_archive_state` runs on every card load/rebuild — initial load, refresh-all, and per-card refresh
- Auto-archive is silent — no toast notification when cards transition to TBA during refresh
- Cards auto-archived from "Returned" status enter TBA state (dimmed but visible), giving users time to notice before they fully archive
- `promote_all_tba` runs on app startup AND after every full rebuild (refresh-all), so TBA cards that aged past 12h get promoted without needing a restart
- Manual unarchive override continues to prevent re-auto-archive (existing behavior, no change)

### Post-refresh UI feedback
- Refresh-all button: text changes to "Refreshing..." and button is disabled while in-flight; reverts to "Refresh all" on completion
- Per-card refresh: Refresh menu item shows "Refreshing..." and is disabled while in-flight
- Per-card refresh failure: ellipsis menu button changes to warning color (amber/orange); inside the menu, Refresh item shows "Refresh failed - retry" text; warning color reverts on next successful refresh
- Refresh-all failure: toast notification "Refresh failed" (global action merits a toast, unlike per-card which uses inline indicators)

### Claude's Discretion
- Exact warning color for ellipsis button on per-card refresh failure
- Whether per-card refresh re-runs filters for just that card or skips (since single-card update is minimal)
- Toast duration and positioning for refresh-all failure toast (can reuse existing toast infrastructure)
- How to track per-card refresh-in-flight state (likely CardRefreshState enum already exists)

</decisions>

<canonical_refs>
## Canonical References

**Downstream agents MUST read these before planning or implementing.**

### Refresh infrastructure
- `crates/app/src/dashboard/actions.rs` — RefreshCommand, RefreshDispatcher, EditDispatcher (tested dispatch patterns)
- `crates/app/src/main.rs` — dispatch_refresh_all, dispatch_key_input (F5 wiring), NoopClient seed data
- `crates/app/src/service_client.rs` — DashboardDataClient trait with refresh_recipient/refresh_all methods

### Auto-archive infrastructure
- `crates/app/src/dashboard/archive.rs` — ArchiveStore, compute_archive_state, promote_tba_if_due, filter_cards_by_archive
- `crates/app/src/dashboard/mod.rs` — DashboardRuntime with archive_store field

### UI components
- `crates/app/ui/dashboard.slint` — Refresh-all button (line ~227), search bar, card grid
- `crates/app/ui/card.slint` — RecipientCard with show-refresh, refresh-disabled, refresh-label properties

### Prior phase context
- `.planning/phases/03-core-card-dashboard/03-CONTEXT.md` — Refresh interaction model decisions
- `.planning/phases/07-archive-lifecycle-controls/07-CONTEXT.md` — Archive lifecycle, auto-archive trigger, TBA state, toast decisions

</canonical_refs>

<code_context>
## Existing Code Insights

### Reusable Assets
- `RefreshDispatcher` + `RefreshCommand`: Fully tested dispatch logic for both per-card and refresh-all
- `ArchiveStore` + `compute_archive_state`: Fully tested archive computation with one-way latch and manual override
- `promote_tba_if_due`: Ready to call during card rebuild for TBA→Archived promotion
- `filter_cards_by_archive`: Composable filter that slots into existing pipeline
- `CardRefreshState` enum in `state.rs`: Tracks per-card refresh state (Idle/InFlight/etc.)
- Toast infrastructure from Phase 7/8: `show_archive_toast` pattern with Rc<dyn Fn>

### Established Patterns
- Filter pipeline composes: search → status → archive → push to Slint model (archive filter already in pipeline from Phase 7)
- Callback wiring in main.rs uses closures with Rc<RefCell<>> for shared state
- NoopClient provides default implementations for all trait methods
- Per-card state tracked on CardData struct and bound to Slint properties

### Integration Points
- `dashboard.slint` refresh-all TouchArea: needs `clicked` handler
- `card.slint` RecipientCard: has refresh properties but no callback wired
- `main.rs` card projection loop: where compute_archive_state needs to be called
- `main.rs` rebuild_cards helper (or equivalent): where promote_all_tba should run

</code_context>

<specifics>
## Specific Ideas

- Silent auto-archive on refresh is intentional — the TBA dimming provides enough visual signal without interrupting the user with a toast. The 12h grace period before full archiving gives users time to notice and intervene.
- Ellipsis button warning color as a persistent error indicator is a nice pattern — it's visible at glance without being intrusive, and self-heals on successful retry.

</specifics>

<deferred>
## Deferred Ideas

None — discussion stayed within phase scope

</deferred>

---

*Phase: 10-refresh-auto-archive-wiring*
*Context gathered: 2026-03-20*
