shared/message_keys.json plus C and Kotlin codegen (#7) #94
No reviewers
Labels
No labels
area:companion
area:docs
area:shared
area:tooling
area:watchapp
blocker
kind:chore
kind:feature
kind:spike
kind:test
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
robert/PedalPebble!94
Loading…
Reference in a new issue
No description provided.
Delete branch "tooling/message-keys-codegen"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Implements #7:
shared/message_keys.jsonplus C and Kotlin codegen, generated fromdocs/PROTOCOL.mdsections 2-5 (not
docs/PLAN.md— D28, the 2026-09-01 correction), including all three of the issue'supdate comments (protocol version +
CONFIG_PAGES; PLAN.md to PROTOCOL.md as the source; per-typesentinels,
STATE_ACK/CMD_TIME,SCREEN_W/H/SHAPE,NAV_REJOIN_BEARING/M, the renumberedMAP_*keys, and
HR_SAMPLES' actual(uint8 dt, uint8 bpm)pair format).What's here
shared/message_keys.json— every key in PROTOCOL.md sections 2-3 (52 keys), the currentprotocol_version, per-type sentinels, and PROTOCOL.md section 4's update-rate policy asheartbeat_ms/period_msper group.tools/gen_message_keys.py— stdlib-only Python (no third-party deps), run viauv runso CIneeds nothing baked into any Docker image.
generatewrites both outputs;checkregenerates intomemory and diffs against what's checked in (used by CI). Fails loudly (
ContractError) on aduplicate key id or a gap in a group's id sequence — PROTOCOL.md deliberately leaves gaps between
groups (1-6, 10-24, 30-33, ...) but never within one, so the gap check is scoped per group.
tools/test_gen_message_keys.py— 8 tests, including the duplicate-id/gap acceptance criterionagainst deliberately broken fixtures, plus a check that the real committed contract validates clean.
tools/check_field_id_coverage.py+tools/test_field_id_coverage.py— cross-checkswatchapp/src/c/fields.h'sFieldIdenum against the contract (NFR-C10 / the issue's own addedcriterion): every
FieldIdhas a wire key or is documented watch-local, no wire key names aFieldIdthat doesn't exist.watchapp/src/c/proto.handcompanion/pebble/.../Proto.kt— generated output, checked in.ride_link.c / state.h reconciliation
ride_link.c's four hand-copiedWIRE_KEY_*#defines (added in #11 because no generated headerexisted yet) are replaced with the generated
PROTO_KEY_*constants — exactly what #11's ownclosing comment asked for.
state.h'sRideCmdenum called its own numbering "an interpretation, not a documented fact",because PROTOCOL.md gave
CMD's value vocabulary ("start / pause / resume / ...") as prose only,never a numbered table. Closed by adding a numbered
CMDvalue table to PROTOCOL.md section 3 thatmatches
RideCmd's existing values exactly, rather than inventing a different numbering — nothinghas shipped to a device yet, so the already-implemented ordering became the documented fact (D48:
reopened with a checked fact, not a redesign argument).
shared/message_keys.jsondoes notgenerate these values itself — they're a key's payload vocabulary, not a message key, and stay out
of this contract's scope. No mismatch was found or introduced; flagging this per the task's request
to check for one.
fast-lane.yml
The
lint-and-secretsjob's "generated-code freshness (G5, C5, C10)" step was a placeholder thatprinted a notice and exited once
shared/message_keys.jsonexisted. It's now real:tools/gen_message_keys.py check(regenerate + diff, the actual NFR-C5(a) gate) plustools/check_field_id_coverage.py, and a new step runs both tools' unit tests. Runs viauv run(installed with the same curl command
tooling/docker/pebble-toolchain/Dockerfilealready uses forpebble-tool) rather than requiring a
ci-toolsimage rebuild+push — that image has nopython3, andrebuilding/pushing it is documented as a manual, occasional step Robert runs locally
(
tooling/docker/README.md), not something this PR should trigger as a side effect.Verified for real
pebble buildsucceeds for emery, gabbro and basalt with the generatedproto.hactuallycompiled into
ride_link.c.watchapp/tests/*(hand-compiled withgcc, nocmakebinary in this sandbox): all 96 assertionspass across
test_fields/test_page/test_state/test_page_render_geometry— no regression.:companion:pebble:compileDebugKotlinand:companion:pebble:assembleDebugboth succeed with thegenerated
Proto.kt, using the real Android SDK + JDK 21 (ANDROID_HOME/JAVA_HOMEper tonight'ssetup).
:companion:core:testpasses clean.tools/test_gen_message_keys.py+tools/test_field_id_coverage.py: 13 tests pass.https://claude.ai/code/session_01DAoXbRmJUf2uxNYBfdAXPt