# Methodology — Decision Journal

**Version:** `dj-2026-04-28-v1`
**Module:** `engine/decision-journal.js`
**Surface:** `/pages/your-calibration.html`
**Hotkey:** `Ctrl/Cmd+Shift+P` (configurable)
**Lane:** THv2 lane 25 (background)
**Source pattern:** `@hub-of-life/intelligence/engines/decision-journal.ts` (lines 1-273)

---

## Why this exists

The `/audit` page measures the **engine's** calibration: when the engine says 80%, does it hit 80%? The Decision Journal measures **the user's** calibration alongside it: when *you* say 80%, do *you* hit 80%?

Without this loop, THv2 cannot tell Kadeem that he is systematically overconfident on Crypto Strong Buys, or under-confident on macro defense pivots. The forecast-ledger calibrates the model. The decision journal calibrates the operator. Both must close.

This is the Annie-Duke "Thinking in Bets" loop ported into a trading context.

---

## The loop

1. **Pre-mortem capture** — `Ctrl/Cmd+Shift+P` while focused on a ticker. 60-second modal asks:
   - confidence (0-100)
   - thesis (one sentence)
   - risks (what could go wrong?)
   - "what would change my mind?"
   - horizon
   - max acceptable loss

2. **Engine snapshot auto-attached** — score, tier, probUp, regime, factor_inputs, methodology hash. Captured AFTER the user enters confidence (anti-bias).

3. **Optional revision** — engine view is then shown; user can revise their confidence. Both the original and the revised numbers are stored. Drift between them measures how much engine view biases the user.

4. **Outcome resolution** — at horizon-end the user is prompted to record the realized return, quality (great / good / neutral / bad / terrible), surprises, lessons, and a tag from the six categories:
   - `right-for-right-reason`, `right-for-wrong-reason`
   - `wrong-and-knew-it`, `wrong-blindsided`
   - `overconfident`, `underconfident`

5. **Calibration report** — rolling Brier on user confidence vs realized; calibration curve with Wilson 95% CIs per bucket; cohort breakdowns by tier × class × category.

6. **Severe miscalibration banner** — when the user is >30 percentage points off across last 90 days (n≥10), a banner surfaces. *Honest > flattering.*

---

## Calibration math

### Calibration score

```
calibration_score = 1 − (overconfident_count + underconfident_count) / resolved
```

- **overconfident** — `confidence > 0.7` AND outcome ∈ {bad, terrible} OR realized_return < 0
- **underconfident** — `confidence < 0.4` AND outcome ∈ {good, great} OR realized_return > 0
- **calibrated** — neither

`1.0` = perfectly calibrated. `0.0` = completely miscalibrated. CI: Wilson 95% on `calibrated / resolved`.

Source: ported verbatim from `@hub-of-life/intelligence` (port::ts:202-204).

### Brier score

Delegated to `engine/calibration-metrics.js` (lane 7, `cm-2026-04-28-v1`). No inline reimplementation; single source of truth across the audit page and the user-calibration page.

```
brier = mean((confidence − outcome)²)
```

Range [0, 1]; lower is better. <0.20 well-calibrated; >0.30 poorly calibrated.

### Calibration curve

10 buckets at 10% intervals. Per bucket: `predicted = mean(confidence)`, `actual = mean(outcome)`, `n = count`, Wilson 95% CI on actual. Bins with `n < 5` are visually dimmed per scientific-rigor charter.

### Cohort breakdowns

For every `(tier, class, category)` cohort with ≥1 resolved decision: total, good, hit_rate, hit_rate Wilson CI, mean_confidence, miscalibration = mean_confidence − hit_rate. Cohorts with absolute miscalibration <10pp are tagged "aligned"; >10pp tagged "over" or "under".

### User vs engine

For paired decisions (both `user_confidence` and `engine_probUp` populated): agreement rate, user_correct_rate, engine_correct_rate, user_only_right, engine_only_right. Per-tier cohort breakdown. CIs on every claim.

---

## Anti-bias guards

| Risk | Guard |
|------|-------|
| User is biased by engine view | Modal Phase A captures user confidence FIRST; engine view hidden until user commits. Original `user_confidence` preserved even if user revises in Phase B. |
| Journal entries become gameable post-hoc | Engine snapshot (`factor_inputs`, `engine_score`, `engine_tier`) auto-attached at decision time; never editable. |
| User is flattered into thinking they're calibrated | `severeMiscalibrationBanner()` triggers at >30pp absolute gap over last 90 days (n≥10). Caution-tone copy. |
| Low-n cohorts produce false patterns | All cohort claims carry Wilson 95% CIs; UI dims `n<5` buckets. |

---

## Integration with existing systems

- **Trade journal** (`app.js:3690` `renderJournal`) — current journal stores route-level trades with thesis + mark-to-market P&L. Decision Journal sits ABOVE it: every trade is also a decision, but not every decision becomes a trade. Integration spec: `docs/decision-journal-integration-spec-2026-04-28.md` (lane 1 ships).

- **Forecast ledger** (lane 16, `engine/forecast-ledger.js`) — every Decision Journal entry stores `linked_claim_id`. The two channels live on the same Forecast Claim record:
  - `engine_probUp` → calibrated by lane 16
  - `user_confidence` → calibrated by lane 25

- **Calibration metrics** (lane 7, `engine/calibration-metrics.js`) — Brier/ECE delegated; no duplication.

- **Resolution scheduler** (lane 16, `engine/resolution-scheduler.js`) — at horizon-end the cron auto-resolves the engine's claim AND surfaces a "resolve your decision" prompt for the user. The two outcomes share the same `realized_return` source-of-truth from the bar store.

---

## Storage

- **localStorage key:** `thv2.decisionJournal.v1`
- **Schema:** `dj-2026-04-28-v1`
- **State shape:** `{ schema_version, decisions: { [decision_id]: Decision } }`
- **Cap:** 2000 decisions (oldest by `decided_at` evicted on overflow)
- **Mirror:** IndexedDB via `engine/store.js` (DOM bound — not in this module)

---

## Roadmap

- v2: per-tag calibration (overconfident vs underconfident vs blindsided as distinct buckets)
- v2: forecast-ledger composition layer (write decisions as forecast-ledger claims with `claim_source: 'user'`)
- v3: agent prompt — when engine and user disagree on a Strong Buy, surface "Your historical correctness on disagreements: 0.62. Want to size larger?"
- v3: time-decay weighting on calibration (recent calls weighted heavier)
