# Final code review fix — report 2 Date: 2026-08-17 Branch: feature/signal-calibration-config ## Summary Nine findings from the final code review of the signal-calibration feature. No C++ files were modified (those were already fixed in a separate commit). --- ## Finding 1 — `calibration.js:20` cross-hub parity break in `baseSignalName` **File:** `Client/udpstreamer/static/calibration.js` **Change:** Line 20: `open > 0` → `open >= 0`. An input of `"[0]"` has the `[` at index 0. The old guard `open > 0` let it fall through and return `"[0]"` unchanged, which then passed the non-empty check in `normaliseCal` and was accepted as a signal name. Go's `arrayIndexSuffix` regexp and the C++ `strchr` truncation both reduce `"[0]"` to the empty string and reject the entry. The fix makes JS match. Two assertions added to the existing tests in `Client/udpstreamer/test/calibration.test.js`: - In `'baseSignalName strips an element suffix'`: `assert.strictEqual(C.baseSignalName('[0]'), '');` - In `'normaliseCal rejects invalid entries'`: `assert.strictEqual(C.normaliseCal({source: 'w', signal: '[0]'}), null);` --- ## Finding 2 — `app.js:3470` stale transitional guard **File:** `Client/udpstreamer/static/app.js` **Change:** Replaced ```js if (typeof refreshVScaleMenu === 'function') refreshVScaleMenu(); // Task 8 ``` with: ```js refreshVScaleMenu(); ``` `refreshVScaleMenu` is a hoisted function declaration and is always defined. The guard and the task-number comment were both remnants of incremental development and are now removed. --- ## Finding 3 — `index.html:223` wrong length cap on unit input **File:** `Client/udpstreamer/static/index.html` **Change:** Removed `maxlength="16"` from ``. `maxlength` counts UTF-16 code units, not UTF-8 bytes, so it under-counted multi-byte characters. `normaliseCal` (using `TextEncoder`) is the correct and sole enforcement point. --- ## Finding 4 — stale trigger-threshold unit hint on signal change **File:** `Client/udpstreamer/static/app.js` **Change:** Added `refreshTrigThresholdField();` at both places where `trig.signal` is assigned in the `trig-signal` change handler (lines ~2555 and ~2565). Previously the hint was only updated when calibration changed, not when the user picked a different trigger signal, leaving a stale unit name in the tooltip. --- ## Finding 5 — `configcheck/main.go:159` dead code **File:** `Test/E2E/suite/client/configcheck/main.go` **Change:** Deleted `func (c *conn) nextOneOf(...)` (33 lines) and its preceding doc comment. The function had no callers; the actual reload check uses `c.next("configReloaded")` and a separate `c.nextWithin("sources", ...)` peek. No import became orphaned. --- ## Finding 6 — `hub_calibration_test.go:132-134` orphaned comment **File:** `Common/Client/go/wshub/hub_calibration_test.go` **Change:** Deleted the three-line comment block that introduced `waitBroadcast`, a function that was never written. The comment sat directly above `func sleepMillis(...)` and served no purpose. --- ## Finding 7 — `calibration.go:14` false parity claim in comment **File:** `Common/Client/go/wshub/calibration.go` **Change:** Rewrote the comment above `arrayIndexSuffix`. The old comment said the regexp "Mirrors the C++ `strchr(signal,'[')` truncation" — which is false. The regexp is anchored to the end of the string and requires digits; `strchr` finds the first `[` anywhere in the name. For `A[1]B`, Go's regexp leaves it unchanged while C++ truncates to `A`. The new comment describes both implementations accurately and calls out the known difference explicitly. --- ## Finding 8 — `Docs/StreamHub-API.md` missing calibration limits **File:** `Docs/StreamHub-API.md` **Change:** Added two rows to the §5 Limits table: | Limit | Value | |-------|-------| | Calibration entries | 256 (C++ hub, `kMaxCalibration`); unbounded (Go hub) | | Calibration unit override | 16 UTF-8 bytes | --- ## Finding 9 — spec discrepancies **File:** `docs/superpowers/specs/2026-08-16-udpstreamer-signal-calibration-and-config-design.md` Five sub-items: **9a** — `configReloaded` row missing `path` field. Both hubs always include `path` in `configReloaded` (verified: Go `buildConfigAckMsg` passes `sm.Path()` as path; C++ `BroadcastConfigAck` formats `sourcesFile_` as `"path"` in the ok branch for `configReloaded`). Fixed: added `"path":string` to the `configReloaded` row in the WebSocket protocol table. **9b** — "max 16 chars" → "max 16 UTF-8 bytes". Fixed the unit validation cell in the data-model table. **9c** — `StreamString` fields → fixed `char[]` arrays. `CalibrationEntry` in `StreamHub.h` uses `char source[128]`, `char signal[128]`, `char unit[17]` — no `StreamString`. Updated the C++ hub-implementation paragraph to name the struct and its actual field types. **9d** — E2E chain scenario → standalone `configcheck` program. Replaced the description of a chain-scenario extension with an accurate description of `Test/E2E/suite/client/configcheck/`, quoting its actual behaviour (connects to either hub, asserts calibration protocol, exits non-zero on failure). **9e** — "four new frames" → five. Updated the documentation paragraph to list all five frames by name: `calibration`, `setCalibration`, `configSaved`, `reloadConfig`, `configReloaded`. --- ## Verification output ### `node --check` + `node --test` ``` (node --check static/app.js && node --check static/calibration.js) → SYNTAX OK TAP version 13 ok 1 - baseSignalName strips an element suffix ok 2 - calKey is stable and separates the two fields ok 3 - normaliseCal accepts a valid entry and fills defaults ok 4 - normaliseCal strips an element suffix from the signal name ok 5 - normaliseCal truncates an over-long unit ok 6 - normaliseCal leaves short non-ASCII units untouched ok 7 - normaliseCal truncates an over-long ASCII unit to exactly 16 bytes ok 8 - normaliseCal cuts a mid-rune byte boundary back to the last complete rune ok 9 - normaliseCal leaves a unit that is exactly 16 bytes ending on a complete multi-byte rune untouched ok 10 - normaliseCal rejects invalid entries ok 11 - applyCal and invertCal round-trip ok 12 - applyCal passes non-finite samples through untouched ok 13 - calRange re-orders when the scale is negative ok 14 - CalTable.get returns IDENTITY for an unknown signal ok 15 - CalTable.get resolves an element name to its base signal ok 16 - CalTable.set stores, overwrites, and deletes identity entries ok 17 - CalTable.replaceAll drops the previous contents ok 18 - CalTable.list is sorted by source then signal 1..18 # tests 18 # pass 18 # fail 0 ``` Note: 18 tests (unchanged from before) because the new assertions were added inside two existing test functions, not as separate test cases. ### Go wshub ``` cd Common/Client/go && go build ./... && go vet ./... && go test ./wshub/ ok marte2/common/wshub 1.288s ``` ### Go configcheck ``` cd Test/E2E/suite/client/configcheck && go build ./... && go vet ./... (silent — CONFIGCHECK OK) ```