Proto Wire Unit Clarity

Summary

A pre-cutover sweep that puts the unit in the field name for every wire scalar that carries one, while there are zero users and breaking the wire is free. Concretely: rename time time_seconds and par_time par_time_seconds, resolve the ambiguous penalties unit, and scan the rest of proto/** for other implicit-unit / ambiguous scalars. Breaking JSON-name changes are accepted per the pre-cutover premise; the change ships as one coordinated server + all-three-client release and buf breaking is waived. After cutover this window closes — a bare double time becomes an un-fixable, silently-misread hazard once field clients rely on it.

Context

The client semantic-exposure map’s flagship uncatchable hazard is a wire scalar whose unit lives only in a developer’s head: Score.time is a bare double with no unit on the field, so every hand-mirror (web libs/api-client, iOS, Android) and the on-device ParTimer must remember it is seconds. A rename can’t be caught later by any tolerance mechanism — Behavior B guards unknown enum values, not misread units — so the only real fix is to name the unit at the source, and the only cheap moment to do it is now, pre-cutover, when a breaking rename costs a coordinated release and nothing else.

This is orthogonal to the tolerance work: “fix the wire shape now” (this item) and “tolerate unknown values later” (Behavior B, calc-method-core-dispatch) are two timescales that both hold.

Details

  • time time_seconds (Score and any request/response carrying it). Bare double, always seconds; the unit moves into the name forever.
  • par_time par_time_seconds (scoring profile / drill par time + the ParTimer). Same treatment.
  • penalties — review and resolve. TimePlus-only scalar (see the “penalties is TimePlus-only” note); its unit is ambiguous on the wire — penalty seconds added to the raw time, or a count of penalties? Confirm against the TimePlus dispatcher, then either rename to penalty_seconds (if seconds) or document it as a unitless count (and consider penalty_count).
  • Scan proto/** for the rest: every bare numeric scalar whose unit/meaning is implicit (durations, delays/intervals on the ParTimer, distances, timeouts, sizes). Each is either renamed to carry its unit or explicitly documented as unitless.
  • Carry each rename end-to-end: proto/** (source of truth), libs/core conversions (From/TryFrom), and all three hand-mirrors (web libs/api-client, iOS decoders, Android decoders), plus the ParTimer. No compatibility shim — the old names are removed, not aliased.

Release shape: one coordinated server + all-3-client release (server and every client speak the new names together). buf breaking will flag the renames; we waive it (recorded here), since there are no consumers to protect pre-cutover.

Requirements

  • Every wire scalar that carries a physical unit names that unit in the field (e.g. _seconds); no bare time/par_time remain on the wire.
  • time time_seconds and par_time par_time_seconds are renamed across proto/**, libs/core conversions, all three hand-mirrors, and the ParTimer.
  • The penalties unit is confirmed and the field is either renamed to carry its unit or documented as unitless.
  • A recorded scan of proto/** enumerates every remaining implicit-unit / ambiguous scalar; each is renamed or explicitly documented.
  • Ships as a single coordinated server + all-3-client release with no compat shim; the buf breaking waiver is recorded.
  • Tests: core proto round-trip uses the new field names; a scoring round-trip asserts the seconds semantics survive core > proto.

Scope / non-goals

  • In: the unit-clarity renames + the penalties resolution + the proto-wide implicit-unit scan + carrying it through core, server, all hand-mirrors, and the ParTimer.
  • Out: any enum-value tolerance (that is calc-method-core-dispatch / Behavior B); non-unit proto restructures; post-cutover renames (this window is explicitly pre-cutover — after cutover, additive-and-tolerate takes over).

Open Questions

  1. penalties: penalty seconds ( penalty_seconds) or a count of penalties ( keep numeric, document unitless / penalty_count)? Resolve against the TimePlus dispatcher before renaming.
  2. Does the ParTimer’s random-delay / interval field carry an implicit-seconds unit that should join this sweep, or is it already explicit?