# TODO — remaining gaps vs C reference
State snapshot (2026-07-18): `main` @ `e77b58b` (v1.2.0), clean tree,
submodule pinned at `56edae7`. Sample tracker: **Exact 139 · Partial 0 ·
Missing 0** — every C sample is a faithful port after the counts toggle
(`demo/release-counts-toggle`) and the Washer/Spinner divergence fixes
(`demo/washer-spinner-exact`). Per-category table in `demo/task-samples.md`.
This list is what remains after the July 2026 gap audit (C headers vs `src/`,
C test suite vs Rust tests, C samples vs demo registry) **and** the follow-up
work that landed in v1.2.0. Everything from the original audit's "real work"
list is done: `contact_is_valid`/`contact_get_data` (`f6b1b70`),
`LargeWorldAABBTest` (`76d5025`, `src/manifold_tests.rs:92`), timer module +
filled `b2Profile` timings (`b072ccb`, `src/timer.rs`), Benchmark Capacity
Exact, the six Joints Partials → Exact (`abafa81`), Replay Viewer inspector /
query index / keyframe popup → Exact (`aba0a86`..`0a1f5d4`), and all C View
flags in the panel (`5b802a9`).
## 1. Tracker doc corrections (5 min, docs only)
`demo/task-samples.md` lags its own table after the Replay Exact upgrade:
- [x] Line ~43 headline says "Exact **117** · Partial **22**" — table says
118 / 21. Same stale count in the Phase 2 paragraph.
- [x] "Audit follow-ups" section: the **Contact / AABB (lib)** bullet is not
checked off, but both items shipped (`f6b1b70`, `76d5025`). Mark done.
- [x] "Decisions (Phase 0)" still says Replay is "now `partial` route-only" —
it is Exact since `0a1f5d4`.
## 2. Count-gated samples + last 2 divergences — RESOLVED (all Exact)
**Counts toggle shipped on `demo/release-counts-toggle`.** The count-gated ports
run the C `m_isDebug` (reduced) counts by default so wasm stays real-time, and now
expose the exact C release (NDEBUG) scene via a user-facing **COUNTS** toggle
(`demo/src/demos/counts.ts` → `pickCount(debug, release)`; default DEBUG). Both
counts are the C source's own gated values, so matching either is Exact. **19 of
the former 21 Partials flipped to Exact** (all 21 formerly disclosed in
`demo/src/registry.ts`): 17 Benchmark + Collision/Dynamic Tree
(100×100 / 1000×1000) + World/Tiles (`cycleCount` 10 / 600).
**Last 2 divergences closed on `demo/washer-spinner-exact`** → all 139 Exact:
- [x] Benchmark / Washer — `shapeDef.enableHitEvents = true` on the grid bodies
(`benchmarks.c:680`). Demo calls `enable_body_hit_events(b, true)`
(`b2Body_EnableHitEvents`) on each grid body after attaching its box.
- [x] Benchmark / Spinner — `chainDef` surface-material friction 0.1 on the ground
loop (`benchmarks.c:375`). Demo uses `attach_chain_ex(..., 0.1)` (restitution/
rolling/tangent 0, matching C's zeroed `b2SurfaceMaterial`).
- [x] Profile current wasm perf per sample; bump any counts that hold 60 fps
to release values (some likely can: Sleep, CreateDestroy, Kinematic).
Measured 2026-07-18 (desktop Chrome, release wasm): at C release counts
Sleep = 33.4 ms/step (5050 awake), CreateDestroy = 17.3 ms/step +
create/destroy overhead, Kinematic = 26.4 ms/step — all exceed the 16.7 ms
60 fps budget, so no counts were bumped. The toggle makes the release scene
*reachable* (disclosed "may run below real-time") without changing the default.
- [x] Add a user-facing "release counts" toggle (default DEBUG) so the Exact
scene is *reachable* even where it isn't real-time — shipped on
`demo/release-counts-toggle`; status-legend note added to the tracker.
## 3. Library non-goals (do NOT re-audit; intentional omissions)
Documented single-threaded / registry-less design decisions, listed here so
future gap sweeps don't re-flag them:
- Threading: `parallel_for.c`, `scheduler.c`, `test_thread.c`,
`b2World_SetWorkerCount/GetWorkerCount`, `enqueueTask`/`finishTask`/
`userTaskContext` in `b2WorldDef` (see `src/types/mod.rs`,
`src/world/api.rs` headers).
- `b2World_DumpMemoryStats` — no allocation tracking (arena allocator
replaced by native `Vec`/ownership, `src/core.rs` header).
- `arena_allocator.c`, `test_container.c` (`b2Array` → std `Vec`).
- `_GetWorld` accessors + world registry (`TestWorldRecycle`,
`TestSetWorkerCount`) — world is passed explicitly.
- `b2SetAllocator`, `b2GetByteCount`, `b2SetAssertFcn`, `b2SetLogFcn`,
`b2Yield` — allocator/diagnostic shims.
- Replay worker-count slider — N/A on serial wasm (disclosed in registry).
## 4. Housekeeping
- [x] Local branches `demo/phase-3-harness-parity`, `demo/phase-3-housekeeping`,
`demo/phase-3-joints-replay-exact` all point at merged history
(`2cf7fe4` or behind) — delete once confirmed fully merged into `main`.
(verified gone 2026-07-18 — only `main` remains)
## 5. Performance roadmap (vs C, updated 2026-07-19)
Serial Rust vs serial C (`-w=1`), 10 benchmark scenes, measured interleaved (C
then Rust per scene, min of 2 runs) to defeat thermal throttling; geometric mean
≈ **1.25× slower than C** (range 1.13–1.47×). Full per-scene table and
methodology are in the README `## Performance` section.
- [x] Capsule/segment manifold path (narrow phase) — **misattribution.** The
spinner outlier was not the `collide` path; it was C's `B2_VALIDATE`-only
validators (`b2ValidateIsland`) running in release on island-churning scenes.
Gating them to debug builds took spinner 2.73× → 1.21×. (commit ea08a52)
- [x] Dynamic tree refit + pair traversal — **misattribution**, same root cause
as above; no dynamic-tree change was needed. (commits ea08a52, 0cf469d)
- [x] SIMD contact solver — ported C's 4-wide `b2FloatW` contact-solver kernels
as safe `[f32; 4]` lane-wise Rust (graph colors wide, overflow set scalar,
per-lane op order identical so the determinism hash is unchanged). Geometric
mean 1.45× → 1.25×; pyramid scenes 2.12×/1.71× → ~1.45×. (commit 6045779)
- [ ] Close the remaining 1.25× → 1.0×: explicit `core::arch` intrinsics for the
wide prepare/solve kernels (rustc's autovectorized lane loops still trail C's
hand-written SSE2 on the pyramid scenes, ~1.45×), then profile the residual
~1.15–1.35× general codegen gap elsewhere.
- [ ] Re-measure after each change with `cargo run --release --example benchmark`
vs the C app, interleaved per scene.