Loupe
Documentation

Flow

Multi-cluster investigation.

Sometimes a case has three real incidents happening at once. Most tools force the operator to pick one or accept a hedged synthesis. Loupe refuses to commit one story when there are three, surfaces each cluster on a failure card, and lets you batch-narrate all of them with a single click.

What triggers the multi-cluster surface

Loupe runs the narrator multiple times and ships only when ≥3 of the runs agree on archetype + supporting events above the consensus threshold. When the runs disagree — typically because two or more independent storylines coexist in the same case file (DB outage + SSH brute force + nginx 5xx burst) — the consensus gate refuses to ship a single brief. Instead, you see a neutral failure card:

Loupe couldn't build a confident brief for this case. Try narrowing the time window, attaching more evidence, or regenerating in a moment.

Below the message: every detected cluster, with an Analyze button per cluster, plus a single Analyze all button that batches them.

Two paths from the failure card

Per-cluster Analyze

Click Analyzenext to one cluster. Loupe scopes the case to that cluster's exact event set (not just its time window — event-ID filtering, so cross-source events don't pull in noise) and re-runs the narrator. Brief succeeds → an investigation thread lands in the threads pane. Click Back to clusters on the brief footer to return without re-running. Pick the next cluster.

For when you want to think between clusters.

Analyze all

Click Analyze allat the top of the cluster list. Loupe sequences each cluster through the same scope-and-narrate path. Threads materialize in the threads pane as they land. Failed clusters skip silently — one bad cluster doesn't stop the batch. Last cluster's brief stays on screen when done; the threads pane carries the full set.

For when you trust Loupe to do the legwork. ~15–25s per cluster. A 5-cluster case takes about 90 seconds end-to-end.

Why this is the doctrine-aligned path

Loupe's architecture explicitly rejects the "synthesize something even when the evidence disagrees" pattern. From the Determinism Doctrine §11:

What this doctrine does NOT promise: a single confident brief on every case. When the signal is genuinely chaotic — three distinct storylines in the same window, parallel root causes — Loupe will refuse to commit one story. The operator routes into per-cluster analysis. Honest failure beats invented confidence.

Real-model end-to-end test testAnomalyHardChaosFallsThrough asserts this behavior against a fixture (Tests/LoupeTests/Fixtures/Scenarios/anomaly/hard/chaos.log) containing three real interleaved incidents (DB OOM outage, SSH brute force, nginx 5xx burst). The expected outcome is not a single brief — it's the multi-cluster failure surface, and the assertion verifies that running the per-cluster analyze on each cluster produces three distinct, confident threads.

What you end up with in the threads pane

Each cluster you analyze produces an InvestigationThread — a frozen capture of the brief at that moment, with its own archetype + subcategory + supporting evidence + per-thread hash-chained audit history. Threads are organized by lineage (a regenerate creates a new thread linked to its parent, never overwrites).

You curate threads via three dispositions: Active (the default — under consideration), Included (this thread feeds into the final RCA), Dismissed with a reason (examined and rejected as not relevant). Dismissed threads are preserved — an auditor can see what you considered and why you ruled it out.

Threads ship in the export bundle (per-template opt-in) under Investigation Threads/<id>/ with each thread's brief, audit chain, and disposition decision.

Reproducibility

Cluster failure surface: Sources/Loupe/Views/Narrator/NarratorBriefView.swift (FailureCard + clusterGuidance). Analyze All orchestration: Sources/Loupe/Views/CaseViews.swift (onAnalyzeAll closure). Real-model test fixture: Tests/LoupeTests/Fixtures/Scenarios/anomaly/hard/chaos.log.