Skip to content

4.5.0: parse what the firmware accepts - #11

Merged
travisccook merged 2 commits into
mainfrom
parse-leniency-4.5.0
Sep 29, 2026
Merged

travisccook merged 2 commits into
mainfrom
parse-leniency-4.5.0

Conversation

@travisccook

@travisccook travisccook commented Sep 29, 2026 •

Copy link
Copy Markdown
Owner

Stored WCB commands on c2-body showed rows of "Unrecognized token" in DroidNet's command builder although the firmware runs them. This release teaches the parser what the firmware accepts.

Engine

  • Empty fragments — a stray, doubled or trailing ^ became an empty raw step. The WCB skips empty segments (parseCommandsNoChecksum), so parseWCBValue drops them.
  • omittable param flag — the firmware accepts the command with the param left off and uses its own default. match() accepts it absent (param = ''); encode() leaves it and every later param off when it is ''. The composer offers a Board default choice. The validator keeps omittable params at the template's end and rejects omittable + required.
  • leadingZeros param flag — the firmware reads the number with atoi/toInt, so 03 is 3. A rebuild writes the canonical code (firmware-equivalent). Enums with numeric codes only.

Data

  • flthy-hps — colour omittable on 002/003/004/005/007 and speed on 003 (processCommands uses defaultColor / shortColor / DIMPULSESPEED). A007 now decodes as Short Circuit in the board colour. RC and wag servo commands accept |n (HPHalt, v1.81 main.cpp:1420, :947-960).
  • psi-pro Run Effect — leadingZeros (atoi, PSIPro main.cpp:2369), so 4T03 parses.
  • ia-magic-panel Run Mode — leadingZeros (inputString.toInt()), so T00 parses.

Tests

  • New test/parse-leniency.test.js (40 tests), including the c2-body alarm, cantina and angrysound values.
  • The 4.2.0 freeze test allowlists the four servo ids whose duration support only widens.
  • The 4.3.0 A007 tests now expect Short Circuit, per the 4.3.0 rule "decode by what the firmware runs". Whether the droid's holoprojectors run A007 as short circuit is still bench test 11.

npm run validate clean; npm test 465/465. Across c2-body's 54 stored commands, raw steps drop from 29 (in 23 commands) to 12; the rest are DroidNet @F tokens, *MO, ;H,STOPEMOTE,GRACEFUL (WCB WIFI branch only) and T35|180 (the Magic Panel ignores |n).

🤖 Generated with Claude Code

https://claude.ai/code/session_018RWJkx8uxyZY9qy9upAPvE

Stored WCB commands on c2-body showed as unrecognized raw steps although the
firmware runs them:

- A stray, doubled or trailing '^' became an empty raw step. The WCB skips
  empty segments (parseCommandsNoChecksum: `if (!singleCmd.isEmpty())`), so
  parseWCBValue now drops them.
- New param flag `omittable`: the firmware accepts the command with the param
  left off and uses its own default. match() accepts it absent (param = ''),
  encode() leaves it and every later param off when it is ''. The composer
  offers a "Board default" choice. The validator keeps omittable params at the
  end of the template and rejects omittable + required.
- New param flag `leadingZeros`: the firmware reads the number with
  atoi/toInt, so '03' is '3'. The zeros sit outside the capture group, so a
  rebuild writes the canonical code. Only on enums with numeric codes.

Data:
- flthy-hps: colour omittable on 002/003/004/005/007 and speed on 003
  (processCommands: defaultColor / shortColor / DIMPULSESPEED when absent),
  so A007 decodes as Short Circuit in the board colour. RC and wag servo
  commands accept |n (HPHalt, v1.81 main.cpp:1420, :947-960).
- psi-pro Run Effect: leadingZeros (atoi, PSIPro main.cpp:2369).
- ia-magic-panel Run Mode: leadingZeros (inputString.toInt()).

The 4.2.0 freeze test allowlists the four servo ids whose duration support
only widens; the 4.3.0 A007 tests now expect Short Circuit, which is what the
firmware runs (the 4.3.0 rule: decode by what the firmware runs).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018RWJkx8uxyZY9qy9upAPvE
@github-actions

Copy link
Copy Markdown

Review: 4.5.0 — parse what the firmware accepts

Checks (all pass)

  • npm ci: OK
  • npm run validate: OK, all boards ✓ and cross-file ✓. The only warnings are the existing non-standard-category notes.
  • npm test: 14/14 suites, 465/465 tests passed.

Safety classes: no HIGH-severity issues

  • The PR changes no safety values.
  • The Flthy servo commands that gain |n duration (rc-lr, rc-ud, wag-lr, wag-ud, flthy-hps.json ~464-560) are all still movement.
  • The colour commands that become omittable are lighting only. shortcircuit is cosmetic, which is correct.
  • No stored-command (;C, ;SEQ, ONFIN) commands are touched.

Board rules

  • Every touched command has new examples covering the omitted and leading-zero forms.
  • The change is additive with no id, template or param renames. A minor bump is defensible, since it adds schema features and parse behaviour.
  • manifest.json, package.json and releases.json are all in sync at 4.5.0.
  • confidence is unchanged. The omittable and leadingZeros claims rest on firmware reading (atoi/toInt, v1.81 duration). Please cite the source, or confirm hardware verification, in the PR description.

Engine (src/droidnet-command-library.js)

  • The engine stays DOM-free. The UI change (paramControlHtml, ui.js ~61) still goes through the engine.

  • Omittable matcher (~276-305): an optional group nested to the end of the template, enforced by the validator. This looks correct. m[i+1] === undefined maps to '', and encode drops the param and everything after it.

  • Round-trip invariant (note, not a blocker): buildWCBValue(parseWCBValue(v)) === v is now deliberately relaxed for three cases, all documented in the guides:

    • empty ^ segments (~366)
    • leadingZeros codes
    • omitted params, which rebuild as-is

    CLAUDE.md still describes byte-identical round-trips with only the rseries exception. Please update the "wire format" invariant paragraph in CLAUDE.md to list these.

  • Minor: leadingZeros regex 0*(code) applies to every alternative. If an enum code is 0 and the input is 00, this matches. That is fine, but worth a test if one doesn't already exist.

Advisory only; no changes made.

…ds byte-identically

The 4T03 example rebuilt as 4T3, which DroidNet's vendored-catalog test
rejects: an example seeds the composer and shows the wire form, so it must be
what the composer writes. The leading-zero parse stays covered by
test/parse-leniency.test.js. The library now enforces the same rule.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018RWJkx8uxyZY9qy9upAPvE
@github-actions

Copy link
Copy Markdown

Review (advisory)

Checks

  • npm ci: OK (only an npm audit notice).
  • npm run validate: PASS. Every board and the cross-file merged catalog show ✓. The only output besides ✓ lines is the usual non-standard-category warnings.
  • npm test: PASS. 14 suites, 466 tests.

Board rules

  • libraryVersion is 4.4.0 → 4.5.0 in libraries/manifest.json, package.json and releases.json, and they agree. Minor is right: there are new schema flags and new accepted forms, and no ids were renamed or removed.
  • The touched commands still have examples. New examples cover the omitted-param and |n forms.
  • I found no new placeholder/param, enum or duplicate-id problems. The validator passes, and it now enforces that omittable params come last in the template and that leadingZeros needs an all-numeric enum (scripts/validate.js).
  • confidence is unchanged by this PR.

Safety (no mislabeling found)

  • flthy.servo.rc-lr and the other three RC/Wag servo commands stay movement. They now accept |n durations (flthy-hps.json:476, and the 3 similar spots). That is correct, but note these are now timed movement commands. The confirm-before-firing warning still applies.
  • The Flthy lighting commands are still cosmetic. psi.mode (psi-pro.json:76) is cosmetic, which is fine for an LED effect. I only looked at the psi-pro command and the Flthy servo commands. I did not confirm the safety class of the IA MagicPanel Run Mode command (ia-magic-panel.json:~75). Its only change is the leadingZeros flag.
  • The PR adds no stored-command runs, so the storedcommand rule is not affected.

Engine (src/droidnet-command-library.js)

  • Round-trip invariant: parseWCBValue now drops empty fragments (~line 368). The invariant build(parse(v)) === v no longer holds byte-for-byte for values with stray, doubled or trailing ^. leadingZeros codes (4T03) also rebuild as 4T3. The PR and releases.json document both as firmware-equivalent, like the rseries canonicalization. Please update the invariant text in CLAUDE.md to list these exceptions. test/parse-leniency.test.js covers the new behavior.
  • The omittable matcher (~line 276) wraps the optional groups correctly. Encode leaves off an omittable param set to '' and everything after it. Together with the validator's tail-only rule, this is consistent.
  • The engine stays DOM-free. The UI change (paramControlHtml) is view-only and adds a Board default option with value ''. It does not add any encode/parse logic.

Nit

  • Omittable params keep a non-empty default (e.g. "5"), so the composer preselects that value and never defaults to Board default. That is probably intended. It is worth saying so in the docs if it is.

Nothing blocking.

@travisccook
travisccook merged commit d623290 into main Sep 29, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant