Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
125c341
feat(studio): controller-fault detection, post-mortem probes, soak dr…
mbreiser Sep 12, 2026
530f64b
fix(soak): keep the bridge up between iterations; soak always logs be…
mbreiser Sep 12, 2026
5df1fa9
docs(soak plan): first bench night — wedge reproduced on a third cont…
mbreiser Sep 12, 2026
c5f1b1e
fix(closed loop): a controller REJECT of SET_FRAME_POSITION counts as…
mbreiser Sep 12, 2026
3a5b047
feat(studio): record the controller's firmware build identity (GET_FI…
mbreiser Sep 12, 2026
4b5266a
chore(studio): footer timestamp for the 2026-09-12 edits
mbreiser Sep 12, 2026
be0d3cf
feat(telemetry): controller ring-buffer drain into the run log — T4 h…
mbreiser Sep 12, 2026
16bb3ca
fix(closed loop): wrap frame indices into the configured frame count …
mbreiser Sep 12, 2026
f8c6ce3
fix(fw #50): Codex review — evidence-first post-mortem, ack-means-sto…
mbreiser Sep 12, 2026
d60ea5d
docs(CLAUDE.md): telemetry ring gate is the 0xCB flag, not the health…
mbreiser Sep 12, 2026
6ae02b5
fix(telemetry): incarnation-aware drainer rearm — NO_ACK first after …
mbreiser Sep 12, 2026
5cb33ba
docs(wedge): firmware ring Codex review outcome + flash go/no-go
mbreiser Sep 12, 2026
b2d2a25
docs: commit the 2026-09-12 Codex reconciliation report; track deferr…
mbreiser Sep 12, 2026
695d077
fix(studio): telemetry poller idles unless the bridge is logging (no …
mbreiser Sep 12, 2026
1ba8421
fix(session): send({silent:true}) skips the bridge command log; the t…
mbreiser Sep 12, 2026
878e48b
fix(telemetry): poller.stop() called window.clearInterval as a method…
mbreiser Sep 12, 2026
793cf15
docs(wedge): T4 iteration-1 gate results — telemetry cross-checks, re…
mbreiser Sep 12, 2026
22f791c
docs: handover for the parallel telemetry-metrics / SD-jitter plannin…
mbreiser Sep 12, 2026
6c1a574
docs(wedge): wedge #2 captured on ring firmware — breadcrumb evidence…
mbreiser Sep 12, 2026
68e9c88
feat(wire/studio): GET_HEALTH v2 tail (ISR breadcrumb, watchdog PC ca…
mbreiser Sep 12, 2026
10191e3
feat(postmortem): self-reset path — a link that drops after the fault…
mbreiser Sep 12, 2026
6212997
docs(CLAUDE.md): watchdog self-reset path, GET_HEALTH v2, GET_CRASHRE…
mbreiser Sep 12, 2026
f49bd4c
fix(wire/studio): CrashReport len is in words (11); GET_CRASHREPORT g…
mbreiser Sep 12, 2026
2f6ea5e
docs(wedge): wedge #3 captured (same breadcrumb window); Codex review…
mbreiser Sep 12, 2026
13fc12f
docs(wedge): wedge #4, shrinking intervals across MCU-only reboots, w…
mbreiser Sep 12, 2026
130f5f2
feat(wire): GET_HEALTH v3 tail — raw WDOG3_CS at boot and live (hardw…
mbreiser Sep 12, 2026
4379302
docs(wedge): watchdog build 86eeb4a flashed and proven by starve test…
mbreiser Sep 12, 2026
18ceeda
feat(studio): a mid-run involuntary link drop (watchdog self-reset) i…
mbreiser Sep 12, 2026
1b5de7e
feat(wire): GET_HEALTH v4 tail — measured watchdog tick rate, live TO…
mbreiser Sep 12, 2026
3081983
docs(wedge): 54b57d0 flashed; watchdog verified again (0.5 s effectiv…
mbreiser Sep 12, 2026
b014b63
docs: close-out and PR consolidation plan for the fw #50 campaign
mbreiser Sep 12, 2026
07131ea
feat(postmortem): a link drop during the probe window ends the window…
mbreiser Sep 12, 2026
b74d87a
feat(wire): GET_HEALTH v5 tail — WDOG3_CNT around each kick (watchdog…
mbreiser Sep 12, 2026
5ed8e69
docs(wedge): 4860fef flashed — watchdog expiry calibrated to 2.0 s; f…
mbreiser Sep 12, 2026
60890ae
docs(wedge): operating point raised to 286 Hz + wide jumps at 22:31; …
mbreiser Sep 13, 2026
8c3bcd3
docs(wedge): finding — display starves at 286 Hz because SET_FRAME_PO…
mbreiser Sep 13, 2026
df5ce7c
fix(studio): bound the raw log panel (3k lines) — an uncapped DOM log…
mbreiser Sep 13, 2026
30dae57
docs(wedge): 200 Hz block from 23:36; 286 Hz block summary; decisions
mbreiser Sep 13, 2026
0d32a3a
docs(wedge): wedge #5 caught by the watchdog — PC in IntervalTimer::e…
mbreiser Sep 13, 2026
ddb73d1
docs(wedge): post-self-reset frame-count bug (pattern set rebuilt on …
mbreiser Sep 13, 2026
8792dc7
docs(wedge): standing order for the night — soak to ~08:00; flash the…
mbreiser Sep 13, 2026
d3ad032
docs(wedge): evidence + proposed fix write-up for Codex review
mbreiser Sep 13, 2026
8739b2e
docs(wedge): Codex review reconciliation — IntervalTimer::end() race …
mbreiser Sep 13, 2026
69c14e6
feat(telemetry): decode fw free-running-timer variant (0xCB bit4), IS…
mbreiser Sep 13, 2026
3a3968f
docs(wedge): firmware diff review 1 (eca07f6) reconciliation
mbreiser Sep 13, 2026
d234f6e
feat(telemetry): STATE kind 10 timer_fail, EXC_RETURN mode bit; docs:…
mbreiser Sep 13, 2026
82fbfc7
docs(wedge): evidence §2b — PIT-only NVIC masking, not PRIMASK
mbreiser Sep 13, 2026
13aba8e
docs(wedge): bench log — fix build candidate 394dee45
mbreiser Sep 13, 2026
595749c
docs(wedge): firmware diff review 3 — flash gate passed for 394dee45
mbreiser Sep 13, 2026
c2b2b10
docs(wedge): wedge #6 (PC 0x212f2 — second return address inside the …
mbreiser Sep 13, 2026
bdaea34
docs(wedge): night-2 wedge-scan table + morning summary (baseline 2 w…
mbreiser Sep 13, 2026
ff6776d
docs(wedge): PIT race reproduced stand-alone (stock dead <0.5 s, PIT-…
mbreiser Sep 13, 2026
4f8ec16
docs(wedge): findings + fix + upstream note draft for Codex review be…
mbreiser Sep 13, 2026
6d92bcf
docs(wedge): Codex reconciliation of findings/fix/upstream note; fina…
mbreiser Sep 13, 2026
444bf10
docs(wedge): handover for the Mode-3 performance session (SD → PSRAM,…
mbreiser Sep 13, 2026
17bf381
docs(wedge): bench log — soak wound down, handover
mbreiser Sep 13, 2026
1d4861a
docs(wedge): fw #50 comment posted; upstream report held for Frank's …
mbreiser Sep 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,41 @@ fix flows to every page automatically; two hand-written HTML pages never will.
equal to both the silkscreen number and the 0xAA wire channel (one number
everywhere; `parseRigIo` rejects `port: 0` with a warning). Label new I/O UI
with the silkscreen names, not DO1/DO2/J3/J4 refdes.
- **Controller-fault lifecycle (fw #50, v0.76) — THE fail-closed rule:** a Mode-3 controller
wedge is detected in `js/fictrac-bridge-client.js` (`_recordApply`: ≥ `faultThreshold` (3) failed
applies in the last `faultWindow` (10) — NOT "3 consecutive", a late same-opcode reply can satisfy
the next request) → latched `fault` event, apply forced OFF → `ArenaSession` calls
`runner.fault(reason, detail)` (wakes the wait, `summary.fault`, `summary.stopAcked`) → the
run's outcome is `CONTROLLER_FAULT` (`run-log.js` `deriveOutcome`, adapter) and it **auto-commits**
(only `ABORTED_BY_USER`/`DISCONNECTED` skip). A timed-out protocol command (`isTimeoutError`) is
labelled the same. After the run unwinds, `runSteps` (module block) calls
`Studio.handleControllerFault(summary)` → `js/studio-postmortem.js` (`createPostmortem().run`):
quiet ≥ 1 s + `session.flushRx()` → confirm 0xC2 (2 s) → probe window (5 s timeouts; typed
`probe` rows with decoded + raw hex, because `["a",…]` rows carry no payload) → policy `halt` |
`reset-continue` (0x01 → `session.reconnect()` via `ArenaLink.reconnect()`/`getPorts()`, VID/PID
match, ambiguity refused → MAC verified → post-reset probe). Runner sends go through
`session.send` (a link facade) so trialParams/STOP are logged. New wire commands used by probes
MUST be exported + golden-tested (`GET_HEALTH` 0xCA decodes 55 B + optional 11 B slowest-op tail;
`GET_FRAME_POSITION` 0x72). **Soak driver** (`Studio.startSoak`, File ▾ → Soak…, `?soak=1`,
advanced-only) loops `Studio.runOnce(false)` (the no-dialog half of `beginRun`), refuses without
a `behavior_v2` ack or without bridge frames, halts on the first fault by default, never
auto-commits. Analyzer: `scripts/wedge-scan.py` (+ `tests/test-wedge-scan.py`, standalone
harness, no pytest). Design + campaign spec: `docs/development/mode3-wedge-soak-plan.md`.
**Firmware build identity (permanent):** `GET_FIRMWARE_VERSION` 0xCB (`encodeGetFirmwareVersion` /
`decodeFirmwareVersion` → `label` "sha[*] RxC date branch") is read by `Studio.refreshFirmwareVersion()`
on every link-up (Connect button AND the `identityOnConnect` state hook that covers gesture-free
`session.reconnect()`), stored as `Studio.firmware` → `run_metadata.firmware`. **Gate it on the
`health` capability bit**: this firmware answers an unknown opcode with `CE_UNKNOWN_CMD` and an
error GLYPH on the arena, so never send 0xCB (or 0xCA) blind. Firmware `main` is compiled for a
**4×10** arena (`panel_count_per_frame_row = 4`); the CSHL 2×10 controllers run `arena-2x10-local` —
a build from `main` rejects every 2×10 pattern (`TRIAL_PARAMS: load failed`, CE_ARENA_MISMATCH).
**Telemetry ring (T4):** `js/arena-telemetry.js` decodes 0xA9 blocks (18 B header + records) and
runs the ack-cursor drainer; `Studio.initTelemetry()` starts the 10 Hz poller on link-up (gated on
GET_FIRMWARE_VERSION `flags` bit 2 — NOT on `health` — then a SET_TELEMETRY 0xA8 ack; Debug ▾
toggle `studio_telemetry`); rows go to the bridge as `{type:'rows'}` (bridge ≥ 3.1
`write_rows`, verbatim `["cc"|"cf"|"cs", …]`). The record schema lives in ONE place — the header
comment of `js/arena-telemetry.js` — mirror any firmware change there + in `wedge-scan.py`
(`CTL_TAGS`/`CTL_STATE_KINDS`). Never drain while the post-mortem owns the link (its `canPoll`).
- **Wire module exports:** `js/arena-wire-g6.js` defines more than it exports —
when adding encoders/decoders, add them to the export list AND a test; audit
with `Object.keys(require('./js/arena-wire-g6.js'))` vs the page's `Wire.*`
Expand Down Expand Up @@ -315,6 +350,31 @@ fix flows to every page automatically; two hand-written HTML pages never will.
`run_metadata` from anything but the acked/inferred level.
- Bump the footer version/timestamp on every edit; never Prettier the HTML.

- **Telemetry ring — four rules from the 2026-09-12 Codex review (all tested):** (1) the ONLY gate
for SET_TELEMETRY 0xA8 is GET_FIRMWARE_VERSION `flags` bit 2 (`decodeFirmwareVersion().telemetry`);
never infer it from the `health` capability (the 0xC2 capability byte is full; health-only builds
answer 0xA8 with CE_UNKNOWN_CMD + an error glyph). (2) **Ack means stored**: the drainer advances
its cursor only when `onRecords` returned non-false; the Studio sink returns the bridge's
acceptance (`FicTracBridgeClient.logRows` → boolean) — never ack rows that went nowhere.
(3) Run-log readers must treat ANY string-tagged array row (`"cc"/"cf"/"cs"`, future streams) as a
stream, not a behavior sample; only `"a"` is decoded (`js/runlog-replay.js`, dashboard
`analysis-core.js`). (4) A controller-fault run's export/auto-commit is DEFERRED until
`Studio.handleControllerFault` finishes (the export closes the bridge's file); the runner emits its
terminal event from `finally`, after the best-effort STOP, so `stopAcked` is in the serialized
summary. Post-mortem owns the link: stop the poller, `await drainer.idle()`, then go quiet;
capability-gated probes are skipped (and recorded) when 0xC2 gave no capabilities.
- **Hardware-watchdog world (fw fb11681+, 2026-09-12):** the controller resets itself ~2 s into a
hang, so after a fault the link DROPS. `studio-postmortem.js run()` checks `session.connected`
after the quiet period and after the confirm probe; a dropped link takes the **self-reset path**
(`resetAndReconnect({skipReset:true})`: reconnect → MAC → GET_HEALTH → `afterReconnect` ring dump
+ crash report → post probes) regardless of policy, outcome `self-reset` (soak continues, counts
as a reset) or `self-reset-failed`. GET_HEALTH ver 2 = 89 B (`HEALTH_PAYLOAD_BYTES_V2`): previous
boot's ISR (`prevIsrLastName`), watchdog-captured PC/LR (`prevWdogPcHex`, valid when
`prevPcCaptured`), `wdogFlags` (armed / prev reset was watchdog / suspended / starving / config
failed). `GET_CRASHREPORT` 0xCC (128 B raw PJRC record, `decodeCrashReport().present`) is gated
like 0xA8 on the 0xCB telemetry flag; `Studio.readCrashReport()` logs a `crash_report` event.
Breadcrumb ops 6–9 are the SET_FRAME_POSITION sub-steps (disarm timer / preload / arm / respond).

## Pattern Designer (`pattern_editor.html`)

Renamed from "Pattern Editor" (v0.10, 2026-07-04); the FILENAME stays `pattern_editor.html`
Expand Down
Loading