fix(gauges): absorb five spec drifts from the contract bc3 serves

Merged
JE
2mo ago
+574-175

Summary

Five drifts between the gauges spec and the contract bc3 serves at the pinned revision, all read off the jbuilder/controller source and doc/api/sections/gauges.md, fixed in the spec (and the one Go hand-written wrapper) and regenerated into all six SDKs:

  1. GaugeNeedle.comment_count — _needle.json.jbuilder emits the singular key unconditionally, next to the envelope's plural comments_count. Modeled @required, with the same doc note SearchResult.comment_count carries. Go GaugeNeedle.CommentCount, Swift commentCount; the dict-shaped SDKs already passed it through. spec/fixtures/gauges/needle_get.json now carries it and the manifest's KNOWN INCOMPLETE note is gone.
  2. UpdateGaugeNeedleInput.gauge_needle is @required, and so is its description — needle_params opens with params.require(:gauge_needle), which rejects a missing wrapper and an empty one alike, and description is the only member the update accepts (needle_params.except(:color, :position)). So the wrapper-less and empty-wrapper bodies every SDK permitted were a 400, never a no-op. Matches CreateGaugeNeedle. Go's wrapper keeps Description *string and refuses nil before the wire as a usage error.
  3. notify doc string — names everyone, default, custom, which is exactly what Subscribers#find_subscribers accepts; the previous working_on was invented and silently notified nobody. Surfaced in generated docs in all six SDKs.
  4. Gauge.previous_needle_position is nullable — JSON null for a gauge's first needle (gauges.md shows it). nullable: true layered in enhance-openapi-go-types.sh like the other nullable-when-present members; Go's wrapper field becomes *int32 so the null is distinguishable from a genuine move from 0. A second fixture, gauges/get_first_needle.json, pins the null against the schema.
  5. ToggleGauge declares NotFoundError — ProjectScoped raises RecordNotFound for an unknown project, as ListGaugeNeedles on the same label already declared.

The three "noted, not proposed" items in the issue (.json suffix asymmetry, maxPageSize: 50, comma-joined bucket_ids) are left as they are.

Red-proof, from the literal wire bodies

  • Go: TestGaugeNeedle_DecodesTheSingularCommentCount (comment_count: 5 next to comments_count: 2 — the field did not exist before) and TestGauge_PreviousNeedlePositionNullIsNotZero (null → nil, wire 0 → non-nil 0; the value type decoded both to 0).
  • Swift: GaugeNeedle is typed, so the test fixture gained comment_count and the decode test asserts commentCount — before the spec change the field was discarded.
  • Kotlin: the "omits the wrapper when no attributes are given" test is replaced by one pinning that the wrapper always goes on the wire, since bc3 requires it.
  • Fixtures: make check-fixture-coverage validates needle_get.json against the now-required member and get_first_needle.json's null against the now-nullable one.

Breaking

Labelled breaking; the # Unreleased section in MIGRATING.md covers both:

  • gauge_needle and its description become required on UpdateGaugeNeedle in TypeScript, Python, Ruby, Kotlin and Swift (a call that omitted them stops compiling or raises before the wire — it always got a 400; an explicit None/nil in Python/Ruby still compacts to an empty body and gets the 400).
  • GaugeNeedle.comment_count is required, so Swift's public initializer and the TypeScript/Python model types gain a required member.
  • Go Gauge.PreviousNeedlePosition is *int32 (apidiff flags the type change).

Fixes #731