# @phoebe/decoding

Neural decoders and foundation-model plumbing for the Phoebe neurotechnology
domain (TODOS phase-181 §181.5). Pure TypeScript, dependency-free, ESM.

The package draws a hard line between **what can be honestly computed here** and
**what needs GPU training on large neural corpora**. The former is implemented
with real, tested algorithms; the latter is exposed as a fail-loud seam that
returns `{ status: 'not_configured', reason }` rather than fabricating trained
weights or outputs.

## Modules

| Module | What it does | Real vs seam |
| --- | --- | --- |
| `prng.ts` | Seeded LCG + Box–Muller Gaussian (no `Math.random`) | real |
| `linalg.ts` | Matmul/inverse, Jacobi eig, Cholesky, SPD matrix functions, tall pseudo-inverse | real |
| `sequence.ts` | Levenshtein distance + error rate | real |
| `kalman.ts` (181.5.1.1) | Kalman-filter neural decoder (predict/update) + least-squares parameter fitting | real |
| `runtime.ts` (181.5.1.1/.2) | Hot-swappable `Decoder` runtime (linear / Elman-RNN / Kalman), versioned model registry, per-subject calibration store, injected-clock latency contract | real |
| `brain-to-text.ts` (181.5.1.3) | CTC prefix beam search + n-gram LM rescoring around an **injected** acoustic model | decoding real; acoustic model injected |
| `foundation.ts` (181.5.2.1–.3) | POYO / NDT2 / LaBraM / MindEye tokenizers | tokenizers real; `train`/`infer`/`reconstruct` are fail-loud seams |
| `adaptation.ts` (181.5.3.1/.2) | Euclidean alignment, Riemannian recenter + Procrustes stretch, online recalibration | real |
| `benchmark.ts` (181.5.2.4) | WER, Wolpaw ITR, co-bps, leave-one-group-out CV, chance calibration | real |

## Key verified numbers (from the test suite)

- **Kalman**: on a simulated linear-Gaussian system the filter's state RMSE is
  `0.176` vs `0.338` for the raw least-squares (`H⁺z`) estimate — a ~48%
  reduction the filter earns from its dynamics + noise model.
- **Brain-to-text**: greedy CTC decode yields `[A,B,C→D]` (WER `0.333`); beam
  search + n-gram rescoring recovers `[A,B,C]` (WER `0.0`).
- **Benchmark chance calibration**: a fixed/random decoder scores at chance
  (accuracy `0.25` = `1/K`, `0` bits/selection); a decoder predicting the
  null mean rate scores exactly `0` co-bps. A trained decoder scores `>0.95`
  accuracy and `>0.5` co-bps — the metrics test decoding, not data flow.

## The fail-loud seam

Foundation-model **training** is not available in this package. `train()`,
`loadPretrained()` (without an injected `WeightsBackend`), `infer()`, and
MindEye `reconstruct()` all return `not_configured` with a reason. Tokenization
runs for real and the inference-dispatch plumbing is wired around the seam, so
injecting a genuine weights backend lights up `loadPretrained` with no code
change — but nothing here invents weights or outputs.

## Development

```bash
npx vitest run                       # tests
npx tsc --noEmit -p tsconfig.json    # typecheck
```

Nx is bypassed on this box (worktree duplication); run vitest/tsc directly.
