# Threat prioritization under uncertainty

A decision-support prototype. Given a set of tracks with incomplete and unequally trustworthy information, it answers two different questions at once:

1. **What should I act on now?** — ranked by expected consequence.
2. **Where should I look next?** — ranked by expected regret: how much worse the committed set becomes if this track's uncertainty resolves badly.

Conflating those two is the standard failure mode. A track that is dangerous *and* well understood belongs at the top of the first list and the bottom of the second, because there is nothing left to learn about it.

All data is simulated. Every constant in `src/engine/config.ts` and `src/sim/config.ts` is a synthetic placeholder and does not represent any real system.

## Run it

```bash
npm install
npm run dev        # http://localhost:5173
```

```bash
npm test           # 69 tests, including the hand-checked value-of-information cases
npm run typecheck
npm run build      # static bundle in dist/
npm run dump       # run the 90-second scenario headless, write frames.json
npm run bench      # engine timing
```

`npm run dump` is the useful one if you want to inspect behaviour without a browser: it executes the whole scenario, prints the ranking at each scripted beat with attribution, and writes every frame to JSON. Two runs produce byte-identical files.

## Dependencies

`react`, `react-dom` at runtime. `typescript`, `vite`, `vitest`, `tsx` to build and test. Nothing else — no chart library, no UI kit, no state library. The rank bands and bars are hand-rolled SVG and CSS.

## Layout

```
src/engine/     pure scoring. Zero imports from React or from src/sim.
                Synchronous, deterministic given a seed, runnable without a browser.
  config.ts       every tunable weight, in one file, with the reasoning
  types.ts        belief-state types
  random.ts       seeded PRNG, gaussian / gamma / dirichlet samplers
  propagate.ts    staleness -> variance growth
  score.ts        slack, consequence, action score
  montecarlo.ts   sample -> rank -> rank distributions
  voi.ts          expected-regret attention scoring
  attribution.ts  why a track moved

src/sim/        hidden ground truth, noisy observations, scenario, events
src/worker/     the engine, off the main thread, at 5 Hz
src/ui/         renders engine output. Contains no scoring logic.
```

The engine directory is the part worth reading. It imports nothing, so it can be run, tested, and argued with on its own.

## How it works, briefly

**Tracks are belief states, not records.** Every uncertain quantity is a distribution. Classification is a Dirichlet, so "confidence" is not an invented score — it is the accumulated evidence count, and a new sensor report is `alpha[i] += 1`.

**Staleness inflates variance; it never moves the mean.** `sigma² += q·Δt + (rel·Δt)²` — covariance propagation with the measurement update removed. A stale dangerous track stays dangerous but becomes a candidate for re-observation, so it holds its action rank while climbing the attention rank.

**Uncertainty is never added to the score.** It is already represented, in the width. Risk aversion is expressed by changing the risk functional (mean → CVaR of the worst decile), not by inflating the score. See `RiskPosture` in `config.ts`.

**Rank is a distribution.** Ranking is computed per sampled world and then aggregated, so a track reports the band its rank occupies and its probability of landing in the top 3.

**Human constraints change the commitment, not the belief.** An operator can pin, suppress, or raise the consequence of a track. Overrides live in their own object, never merged into a distribution, and are routed through the same event reducer as everything else — so they replay identically and appear in the frame dump. Because they change only *what we are committed to*, the existing regret machinery prices them automatically: suppress a top-ranked track and it becomes the top attention priority, carrying the cost of that call.

Longer version, including the three questions a skeptical engineer asks: `EXPLAINER.md`.

## Scope

Deliberately not included: multi-source fusion, asset or interceptor assignment, geospatial views, learned models. The point is a prioritization core that can be inspected and disagreed with, not a command-and-control system.
