Skip to content

Desktop: #81's anomaly notices are an ephemeral, unreadable toast — need a reviewable/actionable surface #88

Description

@samkeen

Follow-up to #81, which specified the adapter rendering only loosely ("desktop toast/flash, maybe a per-note marker") and shipped the toast. In practice, on a vault with more than one anomaly in a pass, that toast is close to useless.

The gap

anomalyNotice() (ui/src/reconcile.ts:64-82) flattens every collision/restamp in a ProjectReport into one run-on string, joined with spaces — no line breaks between anomalies. That string is handed to flash() (ui/src/main.ts:204-212), which paints it as plain textContent in a toast and auto-clears it on a fixed 4.5s timer (ui/src/main.ts:1463-1489, 2359-2362) regardless of how much text it's carrying.

A real example, reindexing a ~1100-note vault with three unrelated duplicate-b2id collisions:

Indexed 1122 note(s) — 0 embedded, 0 stamped. ⚠ Duplicate b2id: AlteredCraft/Workshops/AGENTS.md not indexed — AlteredCraft/Workshops/CLAUDE.md kept it. Delete the copy, or remove its b2id line for a fresh identity. Duplicate b2id: __DailyNotes/2026.07.09.md, __DailyNotes/2026.07.10.md not indexed — __Templates/Daily note template.md kept it. Delete the copy, or remove its b2id line for a fresh identity. Duplicate b2id: AlteredCraft/Product Development/B2/B2 Steward Trace Harness - one generator for eval tasks and training data.md not indexed — AlteredCraft/Product Development/B2/B2 Steward Eval - measuring the vault-steward model.md kept it. Delete the copy, or remove its b2id line for a fresh identity.

Three distinct, unrelated anomalies, each naming two files, run together into one paragraph. Concretely, this toast:

  • gives no way to jump to (or even copy) any of the named paths — they're inert text, not tree/open-note affordances;
  • gives no way to act on one anomaly independently of the others (dismiss, defer, mark seen);
  • disappears on the same 4.5s clock whether it's carrying one anomaly or ten, so a multi-anomaly pass is very likely gone before it's been read in full; and
  • has no "where did that go" recall — closing/missing the toast loses it until the next projection pass happens to re-surface the same anomaly.

The underlying data is already fully typed and structured on both sides (B2idCollision, RestampedNote in crates/b2-core/src/ingest.rs:101-148 and mirrored in ui/src/types.ts:307-360) and the CLI already exposes it faithfully via reindex --dry-run --json. The desktop toast is the one place this collapses to an unstructured sentence.

Proposed shape

Exploratory — not committing to one of these:

  • Keep the toast as the "something needs attention" ping (one line: "⚠ 3 anomalies from this reindex"), but make it click-through to a lightweight review surface — a panel/modal listing each collision/restamp as its own row: the shadowed path(s), the path that kept the identity, why, and the suggested fix — each path wired to the existing reveal-in-tree/open-note affordance (expandAncestors, ui/src/main.ts:218) instead of being inert text.
  • Whatever the surface, it must stay consistent with index-engine.md §8 / S2: "nothing anomaly-shaped is ever stored" — every notice re-derives from vault + index each pass. So this is a richer rendering of the ProjectReport already returned by project/reindex, not a persisted notification log or dismissal-state table. A panel reopened after restart shows nothing until the next pass re-surfaces the same anomaly, same as today.
  • Open question: does a non-time-boxed but user-dismissible banner cover this, or does it need a full multi-row panel? Probably depends on how often multi-anomaly passes actually occur in practice — worth a look at real vaults before committing to UI weight.

Non-goals

Sources

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions