A browser-based implementation of Analysis of Competing Hypotheses, the structured analytic technique developed by Richards J. Heuer, Jr. at CIA and taught across the U.S. intelligence community.
It ranks explanations by the evidence against them rather than the evidence for them, flags reporting that feels compelling but discriminates nothing, shows what the judgment is balanced on, and emits a finished product using ICD 203 estimative language — with probability and confidence stated as the separate things they are.
Runs entirely in the browser. No backend, no telemetry, no network calls.
Most analytic errors are not arithmetic errors. They come from building a case for the first plausible explanation and then reading every subsequent report as support for it. ACH is the standard countermeasure: list the hypotheses first, rate every item of evidence against all of them, and prefer the hypothesis with the least evidence against it.
The technique is simple enough to do on paper and tedious enough that people skip the parts that matter — diagnosticity and sensitivity. Those parts are the reason this tool exists.
Refutation-based ranking. Hypotheses are ordered by weighted inconsistency, ascending. Supporting evidence is computed and displayed but never drives the ordering, so you can see when a leading hypothesis is winning by default rather than on its merits.
Weighted evidence. Each item carries a NATO Admiralty reliability grade (A–F) and an analyst-assigned relevance (1–5). A rumour and a forensic finding do not move the matrix equally.
Diagnosticity scoring. Evidence that rates identically against every hypothesis is flagged as non-diagnostic. This is Heuer's least intuitive and most useful result: an item can be true, vivid, central to the narrative, and still carry no weight in choosing between explanations. The sample case includes one deliberately.
Sensitivity analysis. For every item, the tool recomputes the ranking with that item withdrawn, and reports both whether the leader changes and how much the margin over the runner-up narrows. A judgment resting on a single uncorroborated report is identified as such.
ICD 203 output. The BLUF uses the seven-band estimative probability lexicon (almost no chance → almost certain). Confidence is assessed separately from probability, as the standard requires, and is driven by source quality, diagnosticity, matrix coverage, and how well the margin survives losing any single report.
Key Assumptions Check. Assumptions are recorded with their own confidence and an explicit "would be invalidated if" condition, which then feeds the indicators-to-watch section of the report.
Markdown export. A complete analytic product: BLUF, ranking table, evidence with diagnosticity, sensitivity, confidence with drivers, assumptions, indicators, and a stated method-and-limitations section.
The bundled case — Meridian Port Authority, the Republic of Castellia, and every source in it — is entirely fictional. It is constructed so the obvious answer is the wrong one.
A ransom note was left on 41 hosts. The intuitive read is criminal ransomware. But no working payment channel ever existed, encryption spared the finance systems and targeted crane control, operators spent 31 days querying berth schedules without exfiltrating anything, and the binaries carry artifacts inconsistent with the family they impersonate. The matrix ranks criminal ransomware third of four.
That result is asserted by a test, so a change to the scoring model that breaks the lesson breaks the build:
the sample case > reaches the counter-intuitive conclusion the case is built to teach
The case also demonstrates the honest-limitations path. The tool reports moderate rather than high confidence, because withdrawing a single item — E1, the ransom note — collapses the separation between the top two hypotheses entirely.
Ratings use Heuer's five-point scale — CC very consistent (+2), C consistent (+1), N neutral (0), I inconsistent (−1), II very inconsistent (−2) — plus NA, which is tracked as a gap rather than silently treated as neutral.
Each item's weight is reliability × (relevance / 3), where reliability maps A=1.0 down to E=0.3, and F ("cannot be judged") sits at 0.5.
A hypothesis's score is the weighted sum of its inconsistent ratings only. Lower is stronger. Ties break toward higher support, so a corroborated hypothesis edges out a merely un-refuted one.
Diagnosticity is the standard deviation of an item's ratings across hypotheses; zero means it discriminates nothing.
Confidence is basis × (0.35 + 0.65 × avg_reliability), where basis combines diagnostic share, matrix coverage, and the worst-case margin under single-item removal. Source quality gates the result multiplicatively rather than contributing a term — a complete, internally consistent matrix built entirely on unreliable reporting is a well-organised guess and should not score as anything else.
The percentages are not probabilities. They are a softmax over negated inconsistency: monotonic with the ranking and useful for readability, but not derived from base rates or likelihood ratios and carrying no calibration guarantee. The ordering is the finding. For genuinely probabilistic work, use explicit Bayesian updating with stated priors.
Weights are judgments, not measurements. Reliability grades and relevance scores are analyst inputs. The tool makes those judgments explicit and auditable; it does not make them correct.
ACH reduces confirmation bias; it does not remove it. The matrix can only rank the hypotheses you entered. The hypothesis you failed to consider is the largest residual risk in any ACH product, and no amount of arithmetic inside the matrix will surface it.
Not a substitute for tradecraft. This is a calculator for a technique, and it will faithfully compute a well-formed answer from a badly framed question.
npm install
npm run devThen open the printed localhost URL. Everything persists to localStorage — nothing is transmitted anywhere, and the app degrades gracefully where storage is unavailable.
Build a static bundle (it runs fine from file://, so it works air-gapped):
npm run buildRun the test suite:
npm testRegenerate the README screenshots — headless Chrome at 2× device scale, no dev server needed:
npm run build && node scripts/screenshots.mjs?sample=1— load the demonstration case over whatever is in storage?report=1— open directly to the generated product
| Path | What's in it |
|---|---|
src/scoring.ts |
The analytic engine — weighting, ranking, diagnosticity, sensitivity, confidence |
src/report.ts |
Markdown product generation |
src/sample.ts |
The fictional worked example |
src/types.ts |
Data model, rating scale, Admiralty grades |
src/scoring.test.ts |
39 tests over the scoring model |
scripts/screenshots.mjs |
Headless Chrome screenshot pipeline |
This is an independent educational project. It is not affiliated with, endorsed by, or derived from any government agency, and contains no non-public information. All sample data is fictional. The ICD 203 estimative lexicon and the NATO Admiralty Code are publicly published standards.
- Richards J. Heuer, Jr., Psychology of Intelligence Analysis (CIA Center for the Study of Intelligence, 1999) — Chapter 8 covers ACH
- Heuer & Pherson, Structured Analytic Techniques for Intelligence Analysis
- ODNI Intelligence Community Directive 203, Analytic Standards
MIT — see LICENSE.


