Refresh the rubric audit against the SDK as it is now

Merged
JE
8d ago
+1368-566

Stacked on #215. #215 moves every SDK's magic-link flow onto Fizzy's real contract and the generated routes and types. With it underneath, 1B.2, 1C.1 and 1C.3 pass, and make audit-check is green: "fresh and must-pass criteria verified". Merge #215 first. GitHub then retargets this PR to main.

The history below records how the audit found those failures.


The Rubric Check fails on main because rubric-audit.json is dated 2026-09-09, which make audit-check reports as 38 days old (the limit is 30). The repo has no generator for this file. audit-check and #161, the last refresh, treat a refresh as re-checking each criterion against the code and updating date, and that is what this PR does.

This PR does not turn Rubric Check green, and that is deliberate. The re-check found two must-pass criteria false, both since the Go SDK landed and both for the same reason: the hand-written magic-link flows. 1B.2 ("Body types generated") fails because Go, Swift and Kotlin declare their own magic-link request and response shapes. 1C.3 is quoted here:

"All paths generated from OpenAPI spec; no manual fmt.Sprintf or string interpolation for paths"

Generated service paths hold in every language. The hand-written magic-link flows in five languages build their own paths, though, and those paths aren't in openapi.json. The spec has /session.json, /session/magic_link.json and /signup/completion.json:

LanguagePath the flow buildsWhere
Go/api/v1/sessions, /api/v1/sessions/redeemgo/pkg/fizzy/magic_link.go:49,98
TypeScript/sessions, /sessions/redeemtypescript/src/auth/magic-link.ts:72,97
Ruby/sessions, /sessions/redeemruby/lib/fizzy/magic_link_flow.rb:34,43
Swift/sessions.json, /sessions/redeem.jsonswift/Sources/Fizzy/Auth/MagicLinkFlow.swift:72,111
Kotlin/api/sessions, /api/sessions/redeem, /api/sessions/signupkotlin/sdk/src/commonMain/kotlin/com/basecamp/fizzy/auth/MagicLinkFlow.kt:89,114,143

Only Swift's signup path matches the spec. Rust uses routes::CREATE_SESSION and routes::REDEEM_MAGIC_LINK. Turning the check green by re-dating the file with 1C.3: pass: true would certify a must-pass criterion that the code fails. So the audit records it as failing, and the check stays red on 1C.3 instead of on the date.

Decision for @jeremy:

  • (a) Fix the five magic-link flows to use the generated routes, as a follow-up PR. That is a behavior change to an auth flow in five SDKs, so it isn't a small CI fix. The flows look like they would 404 against Fizzy today.
  • (b) Or accept a known-red Rubric Check until then.

Other notes corrected. Every criterion was rechecked against main at 828edf3, so nothing is carried forward unread. Where the notes and the rubric's criterion names disagreed, I used the names from the sibling hey-sdk audit, which follows the same rubric. That re-read changed 2D.5, 3A.5 and 3A.6.

Now failing:

  • 1C.1, because of the same magic-link paths.
  • 1B.4. Go's generated types use plain values with omitempty, not pointers, so an absent field reads as zero. The other five languages have real optionals.
  • 2A.6. Only Rust keeps a 10 KB error-body prefix. Go's client reads 1 MB, but its magic-link flow reads up to 50 MB. Ruby skips parsing past 1 MB. TypeScript, Swift and Kotlin have no cap.
  • 3C.2. Go and Ruby cap success bodies at 50 MB and Rust at 10 MiB. TypeScript, Swift and Kotlin have no cap.
  • 3A.5 (keyring integration) and 3A.6 (no-keyring fallback). Both exist in Go only.
  • 3C.3. TypeScript falls back to an untruncated statusText, and Go's 404 message quotes the full URL.
  • 4B.5. Of 112 operations, 39 have no conformance case of their own. The recount reproduces #161's 127/69 and now gives 134 cases across 6 runners, naming 73 operations.
  • 4C.4. Kotlin's release treats any 409 as a finished publish, so a rerun after a partial upload stops early. The other workflows are re-run safe.

Still passing, with corrected notes:

  • 1B.5. Rust parses *_at fields as chrono::DateTime<Utc>. The other languages keep ISO 8601 strings, and openapi.json has no format: date-time.
  • 2D.5 (resilience primitives configurable). Go and Rust apply them per client. The other four languages ship them, but the client doesn't apply them. Separately, Go's and Ruby's retry budgets ignore the per-operation retry metadata.
  • 3C.4. Hooks never receive headers.
  • 1A.6, 1B.2 and 3A.4 were rechecked and still hold.