# M0–M2 audit — September 12, 2026

**Conclusion:** keep M0–M2 marked complete for the pinned Qwen3-0.6B CPU path.
The repository has a next-token scorer, not yet a generator. The largest usability
problem was the reading flow and stale prose, not missing transformer operations.
Start with [Coming back to Failed Star](reorientation.html); this document is the
review record, not prerequisite reading.

## Scope and standard of evidence

Reviewed the Rust CLI, tokenizer, config/schema, safetensors/mmap, inspection,
matrix primitives and complete forward path; their unit/integration tests; the
fetch/oracle scripts and manifest; CI/setup and verification guidance; the map,
prerequisites, M0–M2 writeups, all eleven learning notes and HTML counterparts;
the site indexes, interactive diagram code, and sync-ledger workflow.

The review distinguishes **tested behavior**, **recorded historical evidence**,
and **remaining limits**. This is not a formal proof, an adversarial-file security
audit, a benchmark, or an independent derivation of the trained model. External
book page references and every outbound resource's continued availability were
not revalidated; the optional local book PDF is absent from this checkout.

## Completeness against the completed milestones

| Gate | Evidence in the repository | Judgment |
|---|---|---|
| M0 text ↔ IDs | Four-stage byte-level BPE, special-literal carving, byte bijection and merge tests, official multilingual/whitespace/code cases in `tests/golden/tokenizer.json` | Complete for the pinned tokenizer; not generic tokenizer.json support |
| M1 inspect weights | Config-derived shared schema; mmap directory bounds and byte coverage; name/shape/dtype checks; tied-head and parameter-count tests; real-file checks | Complete for the supported checkpoint |
| M2 forward → logits | Explicit row-major f32 math; QK-norm, rotate-half RoPE, causal GQA, SwiGLU, both residuals, final norm and last-row projection; complete four-boundary golden vectors | Complete for the tested reference path; not exhaustive prompt coverage |
| Learning product | M0/M1/M2 writeups and Learnings 01–11 have HTML distillations | Complete coverage, with factual and navigation corrections made in this audit |

The recorded August 20, 2026 development-Mac gate passed **86 model-free tests
and all 8 asset-backed tests** against hash-verified assets. See
[Session 26 in PROGRESS](../PROGRESS.md). No inference formula was replaced in
this audit; the numerical implementation already follows the intended Qwen order.

## Findings addressed

### Correctness and verification boundaries

- **Context length was parsed but unenforced.** `forward` accepted more positions
  than `max_position_embeddings`. The CLI now returns a named `PromptTooLong`
  before loading the weight file; direct forward calls assert the limit. Tests
  separate exactly-at-limit from one-too-many, using a tiny model rather than an
  enormous real prompt. This does not guarantee feasible maximum-context runtime.
- **Raw fixture reading ignored trailing bytes.** The f32 reader now rejects
  nonmultiples of four. Its regression test also cleans up its temporary file.
- **Provenance was metadata plus a manual ritual.** `verify_golden.py` now checks
  all four fixture hashes and shape/element/byte counts, plus the four source-asset
  hashes in full mode. It streams the weight hash and never rewrites expected
  outputs. Corrupt bytes, wrong shape, missing assets, and modified source assets
  have independent synthetic regression cases. Cargo does not invoke this script;
  the documented asset-backed gate explicitly runs it first.
- **Completed M2 still suppressed scaffold warnings.** Removed the broad
  dead-code/unused-import/unused-variable allowance and updated the stale
  verification heading; the module is no longer presented as unfinished scaffolding.
- **Sampling visualization hid its actual distribution.** It intersected top-k
  and top-p calculated independently, while displaying probabilities from before
  filtering. It now applies top-k, then top-p over renormalized surviving mass,
  and displays final probabilities. The browser regression uses K=2/P=0.6,
  where the old and new interpretations produce different candidate sets, plus
  temperature and draw-boundary checks. This is a toy preview, not the M3 oracle.

### Explanations and navigation

- Corrected “unlabeled weights”: safetensors already records names, shapes,
  dtypes, and offsets. Config sizes the architecture; code supplies its wiring.
- Qualified GGUF: it supports quantized **and unquantized** tensors and can carry
  architecture/tokenizer metadata; self-contained distributions are not a universal
  format guarantee. Safetensors stores the exported checkpoint dtype.
- Corrected bf16's normal/subnormal range distinction and moved loss scaling
  out of the inference explanation into its primarily training context.
- Separated BPE's learned merge order from static frequency ranking and from
  the model's reserved/added-token vocabulary space. The browser tokenizer is
  explicitly labeled a character-level toy with invented IDs, not Qwen output.
- Qualified embedding-model pooling and contrastive training as common patterns,
  not definitions that every embedding model must satisfy.
- Defined mmap's avoided userspace copy and its immutable-backing-file assumption.
- Replaced copyright-based “proof” of model correctness with the actual boundary:
  pinned executable reference, released assets, and numerical comparisons.
- Separated M3 prefix recomputation from M4 KV caching. Cached attention still
  reads history. Prefill/decode bottlenecks and quantization speedups are conditional,
  not universal promises; quantization now consistently belongs to conditional M7.
- Updated stale “current M2,” owed-M1 links, completed-arc instructions, and
  “inference is only a forward pass” wording in working prose and HTML.
- Added a route through milestone walkthroughs, then optional concept refreshers,
  then code. Existing filenames/URLs stay stable. Radix trees and embedding-model
  terminology are detours, not prerequisites. Old PROGRESS entries remain history.

## Remaining limits, deliberately not disguised as completion

| Limit | Why it matters | When to act |
|---|---|---|
| One five-token full-forward golden prompt | Strong full-vector comparison, limited sequence diversity | M3 adds short multi-step official continuations and forced stop/context cases |
| Config ignores unrepresented architecture switches; tokenizer ignores generic added-token flags | A different compatible-looking file can load but mean different math/tokenization | Keep the pinned-asset contract explicit; add rejection guards before accepting another checkpoint family or tokenizer contract |
| Safetensors JSON map collapses duplicate keys; no dedicated header-size cap | Not a hardened untrusted-file reader | Corrected the false uniqueness comment; add a duplicate-aware parser and explicit resource limits before untrusted-file support |
| Schema size can panic on overflow; inspection totals assume feasible model sizes | Malformed/extreme inputs are not uniformly typed errors | Address with any future generalized/untrusted-model loader, not as a prerequisite to pinned-model M3 |
| Mixed model/tokenizer assets can fail at shape/ID assertions | Library invariants do not substitute for asset provenance | Use the explicit hash gate and pinned fetch; no promise of arbitrary directory compatibility |
| Raw mmap assumes the file is not modified/truncated while mapped | Filesystem mutation can invalidate the mapping contract | Keep model assets immutable during runs; service-grade hardening is out of current scope |
| CLI checks are mostly smoke tests, not a full integration matrix | Argument/output regressions have less automated coverage than math | Add CLI cases with the generation command's input/output contract |
| Sync ledger tracks commits, not semantic equality or working-tree edits | A green report can overstate documentation confidence | Review diffs/links/rendered pages; keep edited baselines PENDING until reconciled commits exist |

These limits are not reasons to erase established M0–M2 results, nor permission
to call the engine generally compatible. New model support should first turn the
relevant assumptions into validation and tests.

## Next work and explanation budget

**Next arc: bounded uncached greedy generation end to end.** Load once, forward
the growing prefix, select one ID, append, and stop under an explicit contract.
Agree on raw input, EOS set/output treatment, ties, empty/zero budgets, context
exhaustion, and UTF-8-safe decoding; prove official token-ID continuation parity.
Do not optimize or introduce chat formatting first.

**Second arc: sampling and streaming.** Teach and test temperature, filter order,
renormalization, seeded draws, and safe incremental text. Finish M3's writeup and
HTML before calling the whole milestone complete. Then M4 builds on the uncached
baseline; M5–M7 remain Metal correctness, measured optimization, conditional
quantization. The [reading guide](reorientation.md) supplies the detailed brief.

The operating guidance now asks every arc to name the runnable artifact, genuinely
new concepts, assumed knowledge, one worked example and discriminating tests.
Basic Rust/arrays/arithmetic may be assumed; inference-specific concepts are
introduced before they change a decision. Reuse existing owners, link optional
depth, and stop explaining when the reader can predict the next value/shape and
explain the failure a test catches. Documentation is part of the arc, not debt
automatically deferred until the next session.

## Verification of this audit

| Executed check | Result |
|---|---|
| `cargo fmt --all -- --check`, `cargo build --locked`, `cargo clippy --locked --all-targets -- -D warnings` | Passed |
| `cargo test --locked` | 89 passed; 8 asset-backed tests explicitly ignored |
| `uv run --directory scripts --frozen verify_golden.py` | All four source assets and four complete fixture files verified |
| `cargo test --locked --release -- --ignored --test-threads=1` | All 8 asset-backed tests passed |
| `uv run --directory scripts --frozen python -m unittest test_verify_golden` | 7 synthetic regression tests passed |
| `cargo run --locked --release -- logits "The capital of France is"` | Top token 12095, `" Paris"`, logit 17.498833 |
| `agent-browser --session audit eval --stdin < tools/sampling-check.js` on the served diagrams page | Sequential filters, temperature, displayed probabilities, controlled draws passed at desktop and narrow widths |
| Local HTML link/anchor scan | All links/anchors resolve inside the served docs tree, 20 pages |
| Rendered inspection | Return route, home entry, default/filtered sampler, changed config/provenance figures; light/dark and representative 390px layouts checked |
| `git diff --check` | Passed |

Commands and setup are in [the testing guide](testing.md) and
[dev-loop](dev-loop.md). This audit runs in a Linux orb; no new Apple Silicon,
Safari, or Metal validation is claimed. Narrow Chromium viewports are not physical
phone tests. The recorded August Mac run remains separate historical evidence.
An overlapping real-model run caused memory contention and was stopped; the
completed run above is correctness evidence, not a performance measurement.

No golden vectors or dependency versions were changed. At the initial audit
handoff, changes were uncommitted and unpublished, and ledger baselines were
`PENDING`. The subsequent authorized shipping step commits the reviewed sources
and stamps those baselines before pushing; the ledger records that reconciliation.
