Skip to content

Use names for JSON configuration targets#373

Merged
jclark merged 11 commits into
masterfrom
config-target-json
Jul 14, 2026
Merged

Use names for JSON configuration targets#373
jclark merged 11 commits into
masterfrom
config-target-json

Conversation

@jclark

@jclark jclark commented Jul 12, 2026

Copy link
Copy Markdown
Owner

Summary

  • serialize ConfigTarget message flags, survey flags, save types, and reset types with stable model names instead of integers
  • update the workbench apply payload and regenerate its embedded assets
  • add a hidden strict satpulsetool gps --target-json input for applying a complete ConfigTarget from an argument or stdin

The previous workbench wire format duplicated Go bit assignments in TypeScript, so renumbering a model flag could silently change requests. The CLI also could not express several ConfigTarget semantics. The name vocabulary removes the bit coupling, while the JSON target path exposes the raw model without applying per-flag CLI policy.

Validation

  • make test
  • npm --prefix webui run typecheck
  • npm --prefix webui test
  • go generate ./cmd/satpulsewb

Fixes #372

jclark added 10 commits July 12, 2026 13:38
ConfigTarget options used raw integer flags on the workbench wire, coupling the frontend to Go bit assignments and leaving parts of the model unavailable to satpulsetool.

Give message, survey, save, and reset options stable name-based JSON forms, update the workbench and embedded assets, and add a strict hidden --target-json path for applying complete targets without CLI policy.

Fixes #372
Empty JSON targets bypassed the normal probe default and entered passive capture, while target flag exclusivity failed open and could ignore message-file options. Make target JSON always reach the configurator and validate changed flags against an explicit session-option allowlist.

Reject unnamed model bits and unknown frontend names, centralize the remaining option spellings, and cover stdin, trailing data, readback merging, exclusivity, and workbench wire conversion.
The workbench still declared ConfigTarget vocabulary locally and built apply requests through Record<string, any>, so TypeScript could not detect drift from Go serialization.

Add central ConfigTarget types and runtime names to gps/ts, validate the complete vocabulary with generated Go JSON fixtures, and use those types throughout workbench readback and apply handling.
The workbench kept NMEA, PVT, satellite, and raw message selections as numeric bitmasks even after the ConfigTarget wire format moved to named values. This left UI state coupled to local flag ordering and required untyped conversion helpers.\n\nRepresent each selection group as an immutable typed set, derive wire arrays from the centralized ConfigTarget vocabulary, and cover set toggling plus unchanged and empty wire semantics in the workbench tests. Regenerate the embedded workbench assets.
Workbench apply requests could retain the read-only port property and panic the configurator, while HTTP decoding accepted unknown fields and trailing values. JSON CLI mode also discarded the show-port capability requirement and fell through to flag-derived property setters.

Sanitize read-only properties at the session boundary, make request decoding strict, isolate JSON target construction, and retain display capability diagnostics. Replace the duplicated flag maps and ordering slices with one generic ordered codec and add regression coverage.
The config/wb-apply scenario was written on master while the ConfigTarget
wire format was moving to named values on this branch, so it posted
Opts.SatsMsg as the numeric bitmask 3. Once the two met in the merge, the
apply request failed to decode and the scenario got a 400.

Send the flag names the config panel sends, matching the array-of-names
encoding SatsMsgFlags now marshals to.
The wb-apply scenario applied only Opts.SatsMsg, so the four other message
flag groups crossed the ConfigTarget wire format untested: a regression in
the array-of-names encoding of PVTMsg, NMEAMsg, RTCMMsg or RawMsg would
have passed the suite. The one check it did make was also vacuous, because
NMEA GSV decodes to a SatellitesMsg just as NAV-SAT does and NMEA defaults
on: a satellites kind arrived whether or not the VALSET landed.

Apply every group, and check them against the workbench packet stream,
which names the packet the receiver actually sent rather than the kind the
session decoded from it. NAV-TIMELS (PVTMsg), NAV-SAT/NAV-SIG (SatsMsg) and
MSM4 (RTCMMsg) are off in the personality defaults, so their packets can
only be the apply landing; NMEA, which defaults on, is disabled with the
empty array, shown gone, and re-enabled by name, pinning both encodings.
RawMsg is wire-format-only: the nav bank holds no RXM packet for its keys
to gate.
@jclark jclark marked this pull request as ready for review July 12, 2026 11:24
ConfigOptions.Socket says the connection is a proxy rather than a serial port,
and gpscfg.Configure leans on it hard: it sends the probe immediately instead
of waiting for the line to go silent, asserts validPacketReceived before any
packet has arrived, nils the silence and deadline timers, and returns early,
skipping the framing checks. Nothing downstream cross-checks it against the
port it was handed. The --target-json path only forced Socket on, when --socket
was given, so a target carrying "Opts":{"Socket":true} kept that value on a
serial device and took the proxy path against real hardware. Assign it from the
transport in both directions instead, the way Session already does for its own
requests, so the JSON cannot state a transport fact it is not in a position to
know. The flag path gets the same assignment: it was correct only because no
flag ever writes configOpts.Socket, an invariant nothing stated.

The flag types accepted a JSON null as an empty array, the one strictness hole
left in the target's entry points, where the opt.Val groups reject it. Reject
it there too.

NMEASelectableMsgFlag did not exclude NMEAMsgOther: filter with a plain
predicate widens the readonly tuple back to the full union, so the type
promised a subset it did not deliver. Give it a type-guard predicate. The
survey report flag could also send "survey" twice, since the array concat that
replaced the bitwise OR does not dedupe.

Also lift the JSON decoding into decodeTargetJSON, drop an unreachable
configSupport reset in resolve, and correct the note in webui/CLAUDE.md that
says gps/ts never reaches a bundle: the workbench now value-imports the name
vocabularies, so editing gps/ts requires regenerating cmd/satpulsewb/dist.
@jclark jclark merged commit 8c6f8a2 into master Jul 14, 2026
5 checks passed
@jclark jclark deleted the config-target-json branch July 14, 2026 06:30
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.

Accept a whole ConfigTarget as JSON in satpulsetool gps

1 participant