---
status: reconciled-partial
coverage_depth: deep
last_reconciled: 2026-07-18
specs:
  - apps/oshun/web/e2e/incident-triage.spec.ts
  - apps/oshun/web/e2e/operator-inbox-decision-bff.spec.ts
source: WALKTHROUGH/journeys/incident-triage.md
---

# Journey result: Incident triage

- **Walked**: 2026-05-29 by Claude against commit `bf12b0f7d8`; the current
  browser/BFF evidence was subsequently deepened through 2026-07-16.
- **Reconciled**: 2026-07-18 against the current journey, fixture/live inbox
  union, shared incident lifecycle store, per-operator decision store, audit
  routes, and focused browser suites. This was a source reconciliation, not a
  fresh runtime walk.
- **Verdict**: **partial** — deep automation proves filtering, reactive
  selection, per-operator verdicts, audited shared acknowledge/assign/escalate/
  snooze/resolve actions, public-report union, and failure guards. Incident
  ingestion, producer dedupe, realtime/on-call delivery, SLA automation, full
  deep-link routing, and list/count refresh from lifecycle state remain open.
- **Primary specs**: `incident-triage.spec.ts` and
  `operator-inbox-decision-bff.spec.ts`.

## Result at a glance

| Evidence lane         | Current result                                                                                       | Authority limit                                                  |
| --------------------- | ---------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- |
| Browser inbox catalog | Six authored base rows plus live public-scene abuse reports; filters and selection are reactive      | Base rows are fixtures; live reports are not lifecycle records   |
| Per-operator verdict  | Rationale-gated create/read/reopen, audit id, reload, auth/payload guards, and operator isolation    | Separate review record; not shared incident state                |
| Shared lifecycle      | Acknowledge, assign, escalate, snooze, resolve, history, audit, and durable snapshot wiring          | Seeded records; no production create/ingestion endpoint          |
| Failure policy        | Duplicate acknowledge, rationale floors, severity/snooze/resolution rules, scope, and network errors | Does not cover producer duplication or realtime loss             |
| Queue synchronization | Selected action targets track the current browser row                                                | Resolving does not remove the fixture row or update header count |
| Operations completion | Not closed                                                                                           | No paging, SLA clock, reconnect channel, or PIR obligation       |

## Evidence map

Three related records participate in the current UI: the browser catalog, the
per-operator decision, and the shared lifecycle state. Keeping them separate is
the central correctness boundary.

```mermaid
flowchart LR
    A[Six fixture inbox rows] --> C[Browser inbox union]
    B[Live public-scene reports] --> C
    C --> D[Filter and select]
    D --> E[Per-operator review verdict]
    E --> F[Decision receipt and audit id]
    D --> G[Shared lifecycle actions]
    G --> H[Incident state, history, and incident audit]
    H -. no list refetch .-> C
    B -. no lifecycle materialization .-> H
    I[Production incident producer] -. absent .-> H
    H -. no realtime or SLA adapter .-> J[On-call and downstream response]
```

## Proven observations

### Catalog and selection

- `/operator/admin` renders the six base Auto/Editorial/Tenant rows and unions
  available public-scene abuse reports. All/category filters use real category
  fields, and selecting a row updates detail, severity, evidence blocks,
  per-operator decision target, and shared lifecycle-action target.
- `/operator/incidents` exposes the current feed and exact open/recent lanes,
  ids, severity/mitigation cells, generated timestamp, and footer destinations.
- Anonymous users are redirected before either operator surface renders.

### Two durable write seams

- `AdminInboxDecisionPanel` requires at least 20 trimmed characters, records a
  per-operator verdict, exposes a real audit id, supports GET readback/reopen,
  and isolates one operator's record from another's.
- Shared incident actions use a different store. Acknowledge is single-writer;
  assign records the assignee; escalation is monotonic and rationale-gated;
  snooze follows severity/duration policy; resolve records a terminal class and
  rationale.
- Successful lifecycle verbs append ordered history and discrete `incident.*`
  audit events. Durable snapshot hydrate/write-through is available when the
  admin database is configured.

### Failure honesty

- Duplicate acknowledge returns 409 with the original actor remaining canonical.
  Short escalation/resolution rationales fail both in the UI and at the BFF
  without state or audit mutation.
- Invalid resolution classes, non-monotonic escalation, S1/excessive snooze,
  missing scope, and network failures remain explicit. The browser does not
  paint an optimistic lifecycle state when the write fails.
- A public report can appear in the browser union without automatically gaining
  shared lifecycle state. The result preserves that limitation instead of
  implying every visible row supports the same verbs.

## Boundaries and gaps

- **A fixture row is not incident ingestion.** The shared lifecycle store starts
  from seeded incidents and exposes no production create route, producer
  contract, recurrence counter, or dedupe key.
- **A per-operator verdict is not shared status.** Its audit id and reopen
  operation serve reviewer accountability; acknowledge/assign/escalate/snooze/
  resolve remain separate shared mutations.
- **A live union row is not a lifecycle record.** Public-scene reports can enter
  the browser catalog without materializing a matching shared incident.
- **A successful mutation is not queue synchronization.** The fixture-backed
  list and header counts do not refetch from lifecycle state, so resolve does
  not remove the row or decrement the count in the current view.
- **Filtering is not durable operator context.** Selected filter and row state
  are component-local rather than URL-addressable or shareable.
- **Escalation state is not on-call delivery.** No realtime subscription,
  missed-window reconciliation, paging adapter, acknowledgement SLA, breach
  event, or post-incident-review workflow is connected.
- **Some evidence destinations remain partial.** The current deep-link matrix is
  not complete or universally return-addressable.
- **One browser is not concurrency coverage.** Duplicate producers, two
  simultaneous resolvers, stale selected rows, and cross-tab updates need
  explicit policies beyond the existing acknowledge conflict.

## Re-run evidence

The current focused suite can be run serially with:

```bash
PW_BROWSER_CHANNEL=chrome pnpm exec playwright test \
  -c apps/oshun/web/playwright.config.ts \
  apps/oshun/web/e2e/incident-triage.spec.ts \
  apps/oshun/web/e2e/operator-inbox-decision-bff.spec.ts \
  --workers=1
```

It requires the real-development-infrastructure web/BFF composition and the test
reseed path for deterministic incident state. A green run proves the current
queue/decision/lifecycle core; it must not be reported as live incident
ingestion, paging, or SLA operations.

## Source trail

- [Source-reconciled journey](../journeys/incident-triage.md)
- [Incident-triage browser suite](../../apps/oshun/web/e2e/incident-triage.spec.ts)
- [Per-operator decision suite](../../apps/oshun/web/e2e/operator-inbox-decision-bff.spec.ts)
- [Admin inbox console](../../apps/oshun/web/src/components/lilith/AdminInboxConsole.tsx)
- [Inbox decision panel](../../apps/oshun/web/src/components/lilith/AdminInboxDecisionPanel.tsx)
- [Shared lifecycle actions](../../apps/oshun/web/src/components/lilith/AdminInboxIncidentActions.tsx)
- [Incident lifecycle route](../../apps/oshun/bff/src/routes/admin-operator-incidents.ts)
- [Incident lifecycle store](../../apps/oshun/bff/src/admin/operator-incident-store.ts)

## Cross-references

- [Citation-drift result](./citation-drift-resolution.md)
- [Public-scene abuse-report journey](../journeys/public-scene-abuse-report.md)
- [User flag-to-review journey](../journeys/user-flag-to-review.md)
- [Operator admin view](../operator/operator-admin.md)
- [Operator overview](../operator/operator-overview.md)
- [Journey coverage matrix](../journeys/coverage.md)

## Open questions

- What production ingestion contract, recurrence model, and idempotency key will
  create shared lifecycle incidents from every producer?
- How will public-report union rows materialize shared incident state before the
  UI exposes lifecycle controls for them?
- When will the inbox list/counts subscribe or refetch after mutations, and how
  will filters/selection survive URL sharing and reconnect?
- Which realtime snapshot, missed-window reconciliation, paging, SLA, breach,
  and post-incident-review contracts complete the operator loop?
- What deep-link/return envelope covers every incident category without losing
  selected incident, filter, tenant, or evidence context?
