Protocol version handshake, mismatch detection, refuse-to-start (#53) #96
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!96
Loading…
Reference in a new issue
No description provided.
Delete branch "area/protocol-version-handshake"
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?
Implements #53 end to end: the handshake exchange, mismatch detection on both sides, and the
refuse-to-start behaviour from the 2026-09-02 "say what a mismatch actually does" update (D46).
Builds on #7 (PR #94):
PROTO_VERSION/PROTO_CONTRACT_VERSION/Proto.CONTRACT_VERSIONalreadyexisted and are untouched —
shared/message_keys.jsonregenerates byte-identical (confirmed byrunning the generator). This PR is the runtime logic.
Watch side (
watchapp/src/c/)ride_link.csendsPROTO_VERSIONthe instantconnection_servicereports a live connection(both the reconnect edge and the cold-start-already-connected case), and decodes it back from
the phone in
ride_link_handle_inbox(). The watch sends first — seeprv_send_protocol_version()'s comment for why: it already owns the only "just connected"detection point in this codebase (#11); the companion's own transport (#4) hasn't landed yet, so
there's no symmetric phone-side trigger to design around yet.
state.c/state.hgain a small, pure (no Pebble SDK, host-testable per D34/#68) protocol-versionblock:
ride_state_set_local/remote_protocol_version(),ride_state_protocol_mismatch(), andgetters for display.
ride_state_start_pause()is the actual gate — wired into the existing idle→running transition,not a parallel one, per the issue's explicit instruction. A known mismatch makes that edge a
no-op (
RIDE_TRANSITION_NONE, same as every other no-op). Scoped to starting a ride only:pause/resume of a ride already running is unaffected even if a mismatch is discovered mid-ride.
No degraded mode anywhere.
carousel.cmakes the refusal visible and distinguishable from the older "already stopped"no-op: a new
CONFIRM_PROTOCOL_MISMATCHnotice shows both versions and names the stale side("Watch v1 / Phone v2 / Phone is old"), dismissed by either button, plus a distinct 3-pulse
vibration so a refused Select doesn't read as a dead button.
watchapp/tests/test_state.c: 6 new host tests.No
proto.c: the issue named it speculatively, but this codebase's existing split is state.c(pure) / ride_link.c (SDK-facing wire glue), with no third file for AppMessage handling. Extending
ride_link.c keeps that split intact.
Companion side (
companion/pebble/)ProtocolHandshake.kt: pure comparison logic (evaluate(watchVersion, phoneVersion)→Match/Mismatch(watchVersion, phoneVersion, staleSide)), zero PebbleKit/Android dependency —same "pure logic tested directly, SDK-touching half is a thin adapter" split
RouteLibraryuses.Real logic on the phone side, not just the watch deciding and the phone trusting it.
#4isexpected to call
evaluate()from a real inbound handler oncePebbleTransportexists.build.gradle.kts: adds the kotest test trio +testOptions.unitTests.all { useJUnitPlatform() }— the first JVM-tested logic in this Android-library module.
ProtocolHandshakeTest.kt: 6 Kotest cases.Docs
docs/PROTOCOL.mdsection 6 gets a short "Implementation" paragraph pointing at where the codelanded. The policy text itself — including the version-bump rules the issue asks for — already
existed (section 1 rule 6, section 6, and
shared/message_keys.json's own_protocol_version_note), written ahead of time alongside #7/D46; this PR doesn't restate it, justlinks to it.
Verified for real
pebble build: clean on gabbro, emery and basalt, no new warnings.watchapp/tests/*.c: all four suites hand-compiled pertests/CMakeLists.txt's exact flags andrun — 102/102 pass, including the 6 new
state.ccases../gradlew :companion:pebble:testDebugUnitTest :companion:pebble:assembleDebugand:companion:core:testand:companion:assembleDebug: allBUILD SUCCESSFUL; the pebblemodule's XML test report confirms all 6
ProtocolHandshakeTestcases actually ran.uv run tools/gen_message_keys.py generate: both generated files reported unchanged.Claude-Session: https://claude.ai/code/session_01DAoXbRmJUf2uxNYBfdAXPt