Files
summercms/.planning/phases/02-api-parity-harness-bootstrap/02-DISCUSSION-LOG.md
2026-09-16 12:47:27 +02:00

204 lines
9.5 KiB
Markdown

# Phase 2: API parity harness bootstrap - Discussion Log
> **Audit trail only.** Do not use as input to planning, research, or execution agents.
> Decisions are captured in CONTEXT.md — this log preserves the alternatives considered.
**Date:** 2026-09-16
**Phase:** 02-api-parity-harness-bootstrap
**Areas discussed:** Recording strategy, Fixture format and layout, Replay auth and data state, Diff strictness and reporting
---
## Recording strategy
### How should fixtures be captured from the PHP backend?
| Option | Description | Selected |
|--------|-------------|----------|
| Both: proxy + scripted | Recording reverse proxy in front of :8422 captures real Nuxt and MCP sessions; a manifest-driven runner fills coverage for all 154 routes | ✓ |
| Recording proxy only | Point Nuxt and MCP at the proxy and record whatever they send | |
| Scripted runner only | A Go program issues each request from a manifest with seeded auth | |
**User's choice:** Both: proxy + scripted
### Which PHP instance and dataset should recordings come from?
| Option | Description | Selected |
|--------|-------------|----------|
| Local :8422 with a seeded parity dataset | Deterministic dataset built on a fresh DB before recording | ✓ |
| Local :8422 with existing dev database | Record against whatever is in the dev DB | |
| Production plytarium.com (read-only routes) | GET routes from the live site, writes locally | |
**User's choice:** Local :8422 with a seeded parity dataset
### How should the seeded parity dataset be built on the PHP side?
| Option | Description | Selected |
|--------|-------------|----------|
| Seed through the API itself | Recorded setup flow calls the PHP API; artisan only for what the API cannot create | ✓ |
| A PHP seeder command in the fonoteka plugin | `fonoteka:parity-seed` artisan command with fixed ids | |
| A checked-in SQL dump | Restore a hand-prepared dump before recording | |
**User's choice:** Seed through the API itself
### What shape should the recorder tool take, given Phase 1's CLI kernel may not exist yet?
| Option | Description | Selected |
|--------|-------------|----------|
| Standalone `parity` binary now, wrapped later | `cmd/parity` on plain flag/cobra, exposed as `summer parity:*` after Phase 1 | |
| go test driven only | Recording and replay as `go test` runs with env-var switches | |
| Wait for Phase 1 and build `summer parity:*` directly | Serialize Phase 2 after Phase 1; recorder is a bonfire command from day one | ✓ |
**User's choice:** Wait for Phase 1 and build `summer parity:*` directly
**Notes:** This makes Phase 2 depend on Phase 1 and drops the roadmap's "parallel with Phase 1" note; ROADMAP.md needs updating.
---
## Fixture format and layout
### How should recorded fixtures be organized on disk?
| Option | Description | Selected |
|--------|-------------|----------|
| One file per flow, steps inside | Ordered steps per flow; manifest runner emits one-step flows per route | ✓ |
| One file per request/response pair | Every exchange its own file | |
| One file per route with all cases | 154 files each holding every case | |
**User's choice:** One file per flow, steps inside
### What file format should a flow fixture use?
| Option | Description | Selected |
|--------|-------------|----------|
| Custom YAML via goccy/go-yaml | Small schema, human-editable, same YAML library as config and admin schemas | ✓ |
| Custom JSON | Same schema as JSON | |
| HAR files | Browser-standard archive with a sidecar for rules | |
**User's choice:** Custom YAML via goccy/go-yaml
### How should credentials and volatile headers be handled in stored fixtures?
| Option | Description | Selected |
|--------|-------------|----------|
| Scrub to named placeholders at record time | `{{jwt:alice}}`-style placeholders from a credential map; replay resolves them | ✓ |
| Store real values, gitignore the fixture directory | Never commit fixtures | |
| Strip auth entirely, re-add at replay from the manifest | Manifest says which identity each step runs as | |
**User's choice:** Scrub to named placeholders at record time
### Where should the fixture corpus and the Płytarium manifest live?
| Option | Description | Selected |
|--------|-------------|----------|
| fonoteka.go/parity/ with fixtures beside the manifest | manifest.yaml, fixtures/{nuxt,mcp,routes}, replay test; framework ships library + synthetic corpus | ✓ |
| fonoteka.go/plugins/fonoteka/testdata/parity/ | Inside the fonoteka plugin module | |
| A third repo for the corpus | Shared by PHP and Go sides | |
**User's choice:** fonoteka.go/parity/ with fixtures beside the manifest
---
## Replay auth and data state
### How should the Go backend's database reach the same state the recording was made against?
| Option | Description | Selected |
|--------|-------------|----------|
| Replay the recorded seed flow against Go first | Same API-driven seed flow on both sides | ✓ |
| Direct Postgres seed from the fixture corpus | Insert rows from a seed file | |
| Hybrid: SQL seed now, API seed once writes exist | Two mechanisms during the port | |
**User's choice:** Replay the recorded seed flow against Go first
### When a replay needs state that no ported write endpoint can create yet, what fills the gap?
| Option | Description | Selected |
|--------|-------------|----------|
| Per-flow Go seed hook, deleted once the write route lands | Named hook registered by the app test, listed as temporary in the manifest | ✓ |
| Port the matching write endpoint in the same phase | Phase 3 would grow beyond one route | |
| Permanent SQL seed files per flow | Second source of truth for state | |
**User's choice:** Per-flow Go seed hook, deleted once the write route lands
### How does a replay obtain credentials to substitute for placeholders?
| Option | Description | Selected |
|--------|-------------|----------|
| Capture rules on seed-flow steps | JSON path from response into a variable store; same for ids, share tokens, OAuth codes | ✓ |
| Harness-level identity registry | Harness logs identities in outside any flow | |
| Mint tokens directly against the Go DB | Sign JWTs and insert tokens in Postgres | |
**User's choice:** Capture rules on seed-flow steps
### How is the Go backend hosted while fixtures replay against it?
| Option | Description | Selected |
|--------|-------------|----------|
| Both: in-process httptest.Server in tests, any URL from the CLI | Hermetic go test with testcontainers Postgres; `summer parity:replay --target` for any server including PHP as self-check | ✓ |
| In-process httptest.Server only | Replay exists only as a Go test | |
| External URL only | Always target a running server | |
**User's choice:** Both: in-process httptest.Server in tests, any URL from the CLI
---
## Diff strictness and reporting
### What counts as a body mismatch for JSON responses?
| Option | Description | Selected |
|--------|-------------|----------|
| Structural equality plus token-type checks | Key sets, values and JSON token types; key order ignored | ✓ |
| Raw byte equality after canonicalization | Sorted keys, fixed whitespace, compare bytes | |
| Strict byte equality, key order included | PHP key order becomes part of the contract | |
**User's choice:** Structural equality plus token-type checks
### Which response headers should the diff compare?
| Option | Description | Selected |
|--------|-------------|----------|
| Global allow-list plus per-route additions | Status, Content-Type, pagination/CORS always; OAuth cache headers, WWW-Authenticate, Content-Disposition per route | ✓ |
| Status and Content-Type only | Ignore all other headers | |
| All headers except a deny-list | Compare everything not on a volatile list | |
**User's choice:** Global allow-list plus per-route additions
### How should nondeterministic values be normalized before the diff?
| Option | Description | Selected |
|--------|-------------|----------|
| Global rules by field pattern plus per-step overrides | Pattern-keyed normalizers with format assertions on masked fields; captured variables compare by reference | ✓ |
| Per-step ignore lists only | Each step lists JSON paths to skip | |
| Mask by value type at record time | Recorder rewrites dates/ids/tokens into placeholders | |
**User's choice:** Global rules by field pattern plus per-step overrides
### How should a parity run report its results?
| Option | Description | Selected |
|--------|-------------|----------|
| Run everything, report per step, summarize per route | Path-level diff per failing step; coverage table over 154 routes; flows as go test subtests | ✓ |
| Fail fast on first mismatch | Stop at the first failing step | |
| Report only, never fail | JSON report, exit zero until cutover | |
**User's choice:** Run everything, report per step, summarize per route
---
## Claude's Discretion
- Harness library package name (summer-themed; `tide` suggested).
- Expected-failure marking for unported routes so CI stays green on ported routes.
- Fresh DB per flow vs one seeded DB per run (default: one per run, manifest-ordered flows).
- Capturing the MCP OAuth PKCE flow through the proxy and into variables.
- Binary response storage (base64 block vs sidecar file).
- Proxy transport details (plain HTTP on localhost, cookie passthrough, buffered bodies).
- Whether `summer parity:record` has an `--update` mode or writes to a new directory.
## Deferred Ideas
- ROADMAP.md dependency update for Phase 2 (now depends on Phase 1) via `/gsd-phase edit 2`.
- Recording read-only routes against production as a late sanity check before cutover (Phase 15 may consider).
- Reusing the harness for the keios.eu port when it starts.