diff --git a/docs/plans/2026-05-07-1650-go-rewrite-m5-1-api-structs-schemas.md b/docs/plans/2026-05-07-1650-go-rewrite-m5-1-api-structs-schemas.md new file mode 100644 index 0000000..5c6b097 --- /dev/null +++ b/docs/plans/2026-05-07-1650-go-rewrite-m5-1-api-structs-schemas.md @@ -0,0 +1,240 @@ +# M5.1 — Hand-author Go API structs + emit JSON Schemas + +Companion to: +- [2026-05-03-2349-go-backend-rewrite.md](2026-05-03-2349-go-backend-rewrite.md) (master design) +- [2026-05-03-2349-go-backend-rewrite-progress.md](2026-05-03-2349-go-backend-rewrite-progress.md) (progress tracker — M5.1 row) +- [2026-05-07-1431-m5-json-api-parity.md](2026-05-07-1431-m5-json-api-parity.md) (Python view-model extraction prep — already merged as `b562ce3` / `32a16ff` / `59223c0`) + +## Context + +M4 (IO layer behind interfaces) just landed. M5 is the JSON-parity contract phase — byte-equal JSON between Python and Go for `/api/adults`, `/api/juniors`, `/api/payments`, `/api/version`. Within M5, the work splits four ways: + +- **M5.1 — this plan.** Define the wire contract: hand-authored Go structs with explicit `json:` tags matching Python keys, plus committed JSON Schemas generated by `github.com/invopop/jsonschema`. **Schemas only — no handlers, no Python `/api/X` routes, no parity tool.** +- M5.2 — Implement Go handlers that compose `services/*` results into these structs. +- M5.3 — Add Python `/api/X` shadow endpoints in [app.py](app.py). +- M5.4 — `cmd/parity/main.go` + `make parity` target. + +The recent Python view-model extraction (`scripts/views.py`) lays the groundwork: every Python builder now returns a plain dict that an `/api/X` shadow can `jsonify` (with one minor unwrap step — see decision #1 below). M5.1 is the matching Go side: types and schemas that pin down the contract before any code writes JSON to a wire. + +## Key design decisions + +1. **Wire format is nested objects, not strings-of-JSON.** The Python view-model dicts contain three template-only fields that are pre-serialized JSON strings: `member_data`, `month_labels_json`, `raw_payments_json`. Those exist purely to feed inline `