Deploy to K8s / deploy (push) Successful in 14s
Task list from the 2026-10-08 repo audit, plus the plan for the two parity fixes found while validating the legacy/ move. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
113 lines
5.3 KiB
Markdown
113 lines
5.3 KiB
Markdown
# Fix the two Go/Python parity differences
|
|
|
|
## Context
|
|
|
|
Running `make parity` against both containers on 2026-10-08 reported
|
|
`0/3 routes match`. Two causes, neither related to the move to `legacy/`:
|
|
|
|
1. **VS rendered in scientific notation by Go.** The payments sheet is read
|
|
with `UNFORMATTED_VALUE`, so a numeric VS cell arrives as `float64`. Go
|
|
stringifies cells with `fmt.Sprint`, which prints any float ≥ 1e6 in
|
|
exponent form (`2.609767878e+09`). Python already converts integer floats
|
|
to plain digits for VS (`get_str` in `fetch_sheet_data`). VS must always be
|
|
a plain string.
|
|
2. **`months_to_show` missing from the Python JSON API.** Go returns it in
|
|
`/api/adults` and `/api/juniors` (and it is `required` in the committed
|
|
schemas). Python already has the feature in its HTML pages
|
|
(`MONTHS_TO_SHOW` is passed to the templates) but never added the key to
|
|
the API responses. Backport the key so the API contract matches.
|
|
|
|
Outcome: `make parity` reports 3/3 routes matching.
|
|
|
|
## Branching
|
|
|
|
Both are bug fixes that touch `legacy/` paths, so they depend on the
|
|
uncommitted `feat/python-to-legacy` work. Commit that branch first (still
|
|
awaiting confirmation), then branch `fix/api-parity-vs-months-to-show` from
|
|
it, with one commit per task. Open the MR with `tea` after the legacy move is
|
|
merged, or target it at that branch.
|
|
|
|
## Task 1 — VS (and other text cells) always a plain string in Go
|
|
|
|
Root cause is `getVal` in `parseTransactionRows`
|
|
(`go/internal/services/membership/sources.go`, the `fmt.Sprint(row[i])`
|
|
closure). The same pattern exists in `get` in
|
|
`go/internal/services/banksync/infer.go`.
|
|
|
|
1. Add `CellString(v any) string` to `go/internal/io/sheets` (new small file
|
|
`cell.go`; both services already import this package):
|
|
- `nil` → `""`
|
|
- `float64` → `strconv.FormatFloat(x, 'f', -1, 64)` (never exponent form;
|
|
`2609767878.0` → `"2609767878"`, `12.5` → `"12.5"`)
|
|
- `string` → unchanged
|
|
- anything else → `fmt.Sprint(v)`
|
|
2. Use it in `getVal` in `sources.go`. This fixes VS and protects the other
|
|
text columns (Message, Bank ID, Sender, …) from the same failure.
|
|
- `getVal` currently returns `"<nil>"` for nil cells and the inferred-amount
|
|
code checks for that literal; simplify that check to `iv != ""`.
|
|
- Leave `getRaw`/`FormatDate` and `parseFloat` for Date and Amount as they
|
|
are — those must stay numeric.
|
|
3. Use it in `get` in `banksync/infer.go` (keeping the `TrimSpace`), so
|
|
inference sees the same text the dashboard shows.
|
|
4. Tests:
|
|
- `go/internal/io/sheets/cell_test.go`: table test for `CellString`
|
|
(large integer float, small integer float, fractional, string with
|
|
leading zeros, nil).
|
|
- Extend `TestLoadTransactions` in
|
|
`go/internal/services/membership/sources_test.go` with a row whose VS is
|
|
`2609767878.0` and whose Bank ID is an 11-digit float; assert plain
|
|
digit strings.
|
|
5. Python: no code change needed — `get_str` already does this for VS. Add one
|
|
regression test for `fetch_sheet_data` in
|
|
`legacy/tests/test_match_payments.py` (mock the Sheets service, numeric VS
|
|
cell → `"2609767878"`) so the two sides are pinned to the same behaviour.
|
|
|
|
Not included (note for later): VS is *written* to the sheet with
|
|
`USER_ENTERED` by both sync implementations, so Sheets stores it as a number
|
|
and drops leading zeros. Fixing that means writing VS as text and is a
|
|
separate change to the sheet's data.
|
|
|
|
## Task 2 — backport `months_to_show` to the Python JSON API
|
|
|
|
`legacy/app.py` only; `MONTHS_TO_SHOW` is already imported there.
|
|
|
|
1. In `api_adults()` and `api_juniors()`, add the key to the response:
|
|
build `out = _unwrap_view_model_for_api(vm)`, set
|
|
`out["months_to_show"] = MONTHS_TO_SHOW`, return `jsonify(out)`.
|
|
- Do not add it inside `build_*_view_model` or `_unwrap_view_model_for_api`:
|
|
the HTML routes already pass `months_to_show=` to `render_template`
|
|
alongside `**vm`, and a duplicate key would raise `TypeError`.
|
|
- `/api/payments` is unchanged; Go does not return the key there.
|
|
2. Tests in `legacy/tests/test_app.py`:
|
|
- Add `"months_to_show"` to the expected key list used by
|
|
`test_api_adults` / `test_api_juniors` (the tuple near the top of the
|
|
file), and assert the value is an `int`.
|
|
3. Go: no change. The key and schemas
|
|
(`go/tests/fixtures/api-schema/{adults,juniors}.schema.json`) already
|
|
contain it.
|
|
|
|
## Wrap-up
|
|
|
|
- `CHANGELOG.md` entry at the top (after confirmation it works), per repo
|
|
convention.
|
|
- Copy this plan to
|
|
`docs/plans/2026-10-08-HHMM-api-parity-vs-months-to-show.md`, per the repo's
|
|
plan-location rule.
|
|
- Add both items to
|
|
`docs/plans/2026-10-08-1026-pre-feature-cleanup-tasks.md` as done tasks so
|
|
the audit list stays complete.
|
|
|
|
## Verification
|
|
|
|
1. `make go-test-all` — unit tests (including the new ones) and parity
|
|
fixtures pass.
|
|
2. `make test` — Python tests pass, including the new API key and VS tests.
|
|
3. `cd go && go test ./internal/web/api/...` — schema golden test still
|
|
matches (no schema change expected).
|
|
4. End to end: start `make web-py` and `make web-go`, then `make parity`;
|
|
expect `3/3 routes match`. Alternatively repeat the two-container run used
|
|
to find the bugs (`make image`, Go image from `go/build/Dockerfile`, then
|
|
`go run ./cmd/parity -py … -go …`).
|
|
5. Open `/payments` on :8080 and check the affected transaction's VS shows as
|
|
plain digits.
|