# Callback Pipeline Invariants

Hard-won rules from debugging cascade bugs where local mutations were invisible to the UI
or secondary sync paths silently dropped updates. See RETRO-AGENT-FAILURE-PATTERNS.md for
the full retrospective (failure modes F2 and C-4).

---

## INV-1: Every local mutation of all_cards_ref MUST be followed by apply_filters

**Pattern:** `all_cards_ref.borrow_mut()` ... `apply_filters(&window, &all_cards_ref.borrow(), &runtime)`

After mutating the card data in `all_cards_ref`, the view model is stale until `apply_filters`
rebuilds it. Without this call, the UI shows old data even though the underlying model changed.

**Current compliant sites (as of 2026-04-16):**
- `main.rs:2815` — borrow_mut() for optimistic note update
- `main.rs:2841` — second borrow_mut() site in same save_note path
- Both are followed by `apply_filters` at `main.rs:2855`

**Historical violation:** `apply_optimistic_note_update` removed in plan 20.1.1-08 (was non-compliant).

**Reviewer grep:**
```
grep -n "all_cards_ref\.borrow_mut()" crates/app/src/main.rs
```
Every match must have a corresponding `apply_filters(` within ~30 lines below it.

---

## INV-2: Every secondary sync path MUST end with invoke_sync_cards_updated

**Pattern:** `w.invoke_sync_cards_updated()` at the end of any path that modifies synced state.

Any callback or handler that changes data the sync cycle also touches (settings save, settings
clear, manual refresh) must call `invoke_sync_cards_updated()` so the sync pipeline picks up
the change. Missing this call causes the UI to show stale data until the next automatic sync.

**Current compliant sites (as of 2026-04-16):**
- `main.rs:1920` — primary sync completion
- `main.rs:2674` — secondary sync path
- `main.rs:2997` — `on_settings_shopify_clear_clicked`
- `main.rs:3100` — `on_settings_save_clicked`

**Historical violation:** commit `dc622640` — `on_settings_shopify_clear_clicked` was missing
`invoke_sync_cards_updated`, causing cards to disappear after clearing the Shopify token.

**Reviewer grep:**
```
grep -n "invoke_sync_cards_updated" crates/app/src/main.rs
```
Cross-reference against settings/clear/save handlers to ensure coverage.

---

## INV-3: apply_optimistic_* MUST be followed by apply_filters

**Pattern:** `apply_optimistic_[fn_name](` ... `apply_filters(&w, ...)`

Optimistic updates modify the in-memory card model. The apply_filters call propagates the
change to the filtered view shown in the UI.

**Current compliant sites (as of 2026-04-16):**
- `main.rs:4045` — call to `apply_optimistic_item_remove` + `apply_filters` at `main.rs:4061`

Only one live call site today. Pattern is preventive — future optimistic update functions must comply.

**Reviewer grep:**
```
grep -n "apply_optimistic_" crates/app/src/main.rs | grep -v test
```
Every non-test match must have `apply_filters` within ~30 lines.

---

## Reviewer composite grep (catches violations across INV-1 and INV-3)

```
grep -n "apply_optimistic_\|all_cards_ref\.borrow_mut" crates/app/src/main.rs
```
For each match, verify `apply_filters(` appears within the next ~30 lines of code.
