Symptom
User reported during manual cross-node testing post-#102 / #104:
issue detected, whenever i receive new messages on 7510 and i click on them they disappear...
Click on an inbox row → opens the message detail (read flow) → after closing the detail (or auto-close after `mark_as_read`) the row is missing from the inbox list.
Expected
Read messages stay in the inbox list, marked as read (faded), until explicit Archive/Delete. The kept-locally copy in `mail-local-state` is supposed to survive the contract-side eviction so the row persists locally.
Code path
`OpenMessage` in `ui/src/app.rs`:
- Calls `InboxView::mark_as_read` → `InboxView::remove_messages` → `InboxModel::remove_messages` (strips from in-memory `messages` Vec + sends `UpdateInbox::RemoveMessages` delta to the contract).
- Calls `local_state::local_mark_read(alias, msg.id, KeptMessage{...})` which inserts into the per-alias `kept` HashMap and `bump()`s the GENERATION counter.
`MessageList` rebuild (`app.rs:1140`):
- Iterates `current_model.messages` (now empty for this id after remove_messages).
- Iterates `kept_for(alias)` and pushes the kept entry (with `sender_vk: Vec::new()`, `signature_valid: false`).
- Filters: `is_archived` (no), `is_deleted` (no), `matches_search` (yes for empty), `hide_unsigned` (default false), `quarantine_unknown` (default false).
So the kept row should appear. It doesn't in the bug repro.
Hypotheses
- GENERATION is `AtomicUsize` not a Signal, so `bump()` doesn't trigger a Dioxus re-render. Components that touch GENERATION via `load(..)` only see the new value on the next render that fires for some other reason. `MessageList` reads `GENERATION` (line 1216) but a single `load` in setup doesn't subscribe to changes.
- id mismatch: `KeptMessage` keyed by `MessageId.to_string()` where MessageId is the in-memory `next_msg_id` (monotonic per session, not globally stable). After remove + a follow-up GetResponse rebuilds the model from contract state, the `next_msg_id` resets and ids may drift from the kept entry.
- Render order: `MessageList`'s rebuild loop only runs inside the `if let Some((current_model, id)) = ...find_map(...)` arm. If the active identity is not found in `InboxesData` after a full state rebuild, the kept-merge branch never runs.
Repro plan
- Iso 2-node, `FREENET_LIVE_E2E_SEND=1` doesn't matter — local single-node `cargo make publish-email-test` against `run-node` is enough.
- Two browsers, alice + bob, send alice → bob.
- Bob receives row.
- Bob clicks row → detail opens.
- Bob navigates back → row gone.
Test gap
`live-node.spec.ts` `multi-round + read + archive across nodes` (added in #104, gated) covers this with the assertion `expect(bobApp.getByText(/round one/i)).toBeVisible({ timeout: 10_000 })` after navigating back.
Symptom
User reported during manual cross-node testing post-#102 / #104:
Click on an inbox row → opens the message detail (read flow) → after closing the detail (or auto-close after `mark_as_read`) the row is missing from the inbox list.
Expected
Read messages stay in the inbox list, marked as read (faded), until explicit Archive/Delete. The kept-locally copy in `mail-local-state` is supposed to survive the contract-side eviction so the row persists locally.
Code path
`OpenMessage` in `ui/src/app.rs`:
`MessageList` rebuild (`app.rs:1140`):
So the kept row should appear. It doesn't in the bug repro.
Hypotheses
Repro plan
Test gap
`live-node.spec.ts` `multi-round + read + archive across nodes` (added in #104, gated) covers this with the assertion `expect(bobApp.getByText(/round one/i)).toBeVisible({ timeout: 10_000 })` after navigating back.