Skip to content

feat!: unify wire protocol (v2 envelope, error codes, protocol field)#26

Merged
mateusdemboski merged 1 commit into
mainfrom
feat/issue-18-protocol-v2
Jul 6, 2026
Merged

feat!: unify wire protocol (v2 envelope, error codes, protocol field)#26
mateusdemboski merged 1 commit into
mainfrom
feat/issue-18-protocol-v2

Conversation

@mateusdemboski

Copy link
Copy Markdown
Collaborator

Summary

PR2 of #18 — firmware implements the v2 contract from spec/v2/. Breaking (contract commitment; the wire itself is additive, existing 1.x clients keep working).

Protocol v2:

  • ok on every reply, both transports — including GET /api/spec, /api/status, /api/config (the v1 drift this issue exists to fix)
  • errors: {ok, error, code} with the stable code enum from spec/v2/schemas/common
  • getspec / /api/spec return protocol: 2 (exact-match gate; major.minor fallback recognizes 1.x)
  • new GET /api/dump (was serial-only); read gains serial; serial dbbegin now requires size (was silently ignored); dbdata acks {ok, written}

Single owner per reply shape — apicore:

  • shape::* — pure fillers over plain structs, no Arduino headers, the only place JSON keys exist (api_core.cpp)
  • fill* — device-side gatherers (api_core_device.cpp)
  • web_server.cpp / serial_proto.cpp collapse to thin shells with zero JSON keys

Drift is machine-enforced: [env:native] + firmware/test/test_contract/ compile the real shape builders on the host, dump every reply (incl. all 9 error codes) to spec/v2/fixtures/generated/, and CI validates them with Ajv. Verified: renaming serialsrl in C++ turns CI red (must have required property 'serial'), restore turns it green.

Verification

  • pio test -e native -d firmware → 7/7 pass; validate.mjs green over 20 generated fixtures
  • pio run -d firmware → SUCCESS (ESP32-C3 target)
  • Drift negative-test: field rename → validate FAIL exit 1; restore → green

After merge / before release

  • Flash a device with this firmware and re-capture spec/v2/fixtures/ via capture_fixtures.py --version v2 (transport-glue proof; shape proof is already in CI)
  • Hold the release-please PR until PR3 (shared @spoolid/core) lands — 2.0.0 must tag coherent firmware+web+desktop

Part of #18. Next: PR3 shared TS package + client refactors.

🤖 Generated with Claude Code

https://claude.ai/code/session_01SBnenzHAV3VVAF8b8hpwkT

Implements spec/v2: every reply on both transports carries ok (HTTP GETs
included); errors gain a stable code enum; getspec//api/spec returns
protocol: 2 for exact-match gating (older clients fall back to
major.minor); dump becomes transport-universal via GET /api/dump; read
exposes the tag serial; serial dbbegin now requires size and dbdata acks
with written.

All reply shapes move into a new apicore layer: pure shape::* fillers
over plain structs (host-compilable, the only place JSON keys exist) and
device-side gatherers. A PlatformIO native test runs the real shape
builders on the host, dumps every reply to spec/v2/fixtures/generated/,
and CI validates them against the schemas — firmware/schema drift now
fails the build in either direction.

BREAKING CHANGE: wire replies are additive on the wire, but the contract
now requires the ok envelope everywhere and code on every error; clients
should gate on the new protocol field.

Refs #18

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SBnenzHAV3VVAF8b8hpwkT
@mateusdemboski mateusdemboski merged commit 1998b0b into main Jul 6, 2026
8 checks passed
@mateusdemboski mateusdemboski deleted the feat/issue-18-protocol-v2 branch July 6, 2026 23:05
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