Files
summercms/.planning/phases/02-api-parity-harness-bootstrap/02-04-PLAN.md
2026-09-17 01:57:41 +02:00

11 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
phase plan type wave depends_on files_modified autonomous requirements must_haves
02-api-parity-harness-bootstrap 04 execute 4
02-03
../fonoteka.go/go.mod
../fonoteka.go/go.sum
../fonoteka.go/parity/parity_test.go
../fonoteka.go/parity/synthetic_test.go
../fonoteka.go/parity/testdata/synthetic-seed.yaml
../fonoteka.go/parity/testdata/synthetic-read.yaml
../fonoteka.go/parity/README.md
true
QA-02
QA-03
truths artifacts key_links
D-09 D-10: A recorded seed and a named temporary seed hook can establish matching state before a route fixture is replayed.
D-12: `go test ./parity` starts a testcontainers Postgres and serves a synthetic SQL-backed handler through httptest.
D-16: Each flow is a selectable subtest, reports path differences and route coverage, and never counts an unported PHP route as a Go pass.
The Phase 3 genres route has a clear handler and seed-hook seam without claiming a Phase 2 Go implementation of PHP endpoints.
path provides
../fonoteka.go/parity/parity_test.go App-owned corpus runner, pending/ported subtests and coverage
path provides
../fonoteka.go/parity/synthetic_test.go SQL-backed httptest handler and Postgres lifecycle
path provides
../fonoteka.go/parity/testdata/synthetic-seed.yaml Integration seed flow with one real HTTP write
path provides
../fonoteka.go/parity/testdata/synthetic-read.yaml Integration read flow and named seed hook
from to via
../fonoteka.go/parity/parity_test.go ../fonoteka.go/parity/manifest.yaml Ordered flow subtests and pending/ported status
from to via
../fonoteka.go/parity/parity_test.go ../fonoteka.go/parity/synthetic_test.go Injected handler factory and seed-hook registry
from to via
../fonoteka.go/parity/synthetic_test.go tide/replay.go httptest target replaying SQL-backed fixtures
**As a** port developer, **I want to** run the recorded corpus from the app test suite with real database state, **so that** later routes turn green one at a time without hiding unported coverage.

Purpose: Prove the in-process Go replay path before the first Fonoteka handler exists. Output: App-owned testcontainers Postgres integration, synthetic write/read handler, seed-hook seam, selectable flow subtests and honest pending coverage.

<execution_context> @/home/jin/.codex/get-shit-done/workflows/execute-plan.md @/home/jin/.codex/get-shit-done/templates/summary.md </execution_context>

@.planning/phases/02-api-parity-harness-bootstrap/02-CONTEXT.md @.planning/phases/02-api-parity-harness-bootstrap/02-RESEARCH.md @.planning/phases/02-api-parity-harness-bootstrap/02-VALIDATION.md @.planning/phases/02-api-parity-harness-bootstrap/02-03-SUMMARY.md @../fonoteka.go/go.mod @../fonoteka.go/parity/manifest.yaml @../fonoteka.go/parity/fixtures/seed/bootstrap.yaml `tide` exposes `LoadFlow`, `ReplayFlow`, manifest loading and a `Result`/coverage report. The app owns its `parity/` directory and imports `git.golem15.com/golem15/summercms/tide`, using a local `replace` in app `go.mod` if the framework module is unpublished. Testcontainers Postgres is a test-only dependency in the app module. `parity_test.go` should centralize a `newTarget(t, db) http.Handler` function and `seedHooks` map that Phase 3 can replace or extend without changing the generic harness. Task 1: Replay a synthetic write/read flow against Postgres ../fonoteka.go/go.mod, ../fonoteka.go/go.sum, ../fonoteka.go/parity/parity_test.go, ../fonoteka.go/parity/synthetic_test.go, ../fonoteka.go/parity/testdata/synthetic-seed.yaml, ../fonoteka.go/parity/testdata/synthetic-read.yaml ../fonoteka.go/go.mod, ../fonoteka.go/parity/manifest.yaml, ../fonoteka.go/parity/fixtures/seed/bootstrap.yaml, tide/flow.go, tide/replay.go, tide/variables.go, .planning/phases/02-api-parity-harness-bootstrap/02-CONTEXT.md, .planning/phases/02-api-parity-harness-bootstrap/02-RESEARCH.md A testcontainers Postgres starts; an httptest handler writes a row through POST /synthetic/items, a later GET reads it, and the replay compares fixture response bodies. A declared seed hook inserts a second row through SQL before its read fixture. A wrong row value produces a JSON-path diff. The test does not pass if Docker or Postgres is unavailable. Review RESEARCH.md Package Legitimacy Audit, then add test-only `github.com/testcontainers/testcontainers-go/modules/postgres` and `github.com/jackc/pgx/v5/stdlib` for `database/sql`, both named in project research; scan `../fonoteka.go/go.mod` with `/home/jin/.local/bin/slopcheck scan` before dependency download and again after resolution, and inspect `git -C ../fonoteka.go diff -- go.mod go.sum` before commit. Keep versions from Go module resolution and no dependency in framework production code (QA-03). Start one Postgres container/database for the integration suite, clean it up with `t.Cleanup`, and create a small synthetic table. Implement a temporary `http.Handler` that persists POST then reads GET through that `*sql.DB`; serve it with `httptest.NewServer`. Add two app-owned synthetic fixtures separate from PHP corpus: seed write and read, with captured id variable and a named `synthetic-item` seed hook inserting through SQL (D-09, D-10, D-11, D-12). Register only declared hook names; unknown hooks fail. First write a failing integration test and observe RED without committing, then implement the synthetic handler and replay call; commit only when both modules pass full vet/test. Make a single `newTarget`/hook registry seam in `parity_test.go` that Phase 3 can replace with the real app handler and temporary genres hook. Do not add a fake PHP route response or mark a PHP route ported. /home/jin/.local/bin/slopcheck scan ../fonoteka.go/go.mod && go vet ./... && go test ./... && (cd ../fonoteka.go && go vet ./... && go test ./parity -run TestParitySynthetic -count=1 && go test ./...) - `go test ./parity -run TestParitySynthetic` starts real Postgres via testcontainers and passes HTTP write/read replay. - The declared SQL hook is exercised and an unknown hook is rejected. - Altered SQL-backed response fails at a named JSON path; unavailable Docker does not create a false green skip. The app module has a real DB-backed in-process parity integration path. Task 2: Expose the full corpus as honest selectable subtests ../fonoteka.go/parity/parity_test.go, ../fonoteka.go/parity/README.md ../fonoteka.go/parity/parity_test.go, ../fonoteka.go/parity/manifest.yaml, ../fonoteka.go/parity/fixtures/seed/bootstrap.yaml, tide/manifest.go, tide/replay.go, tide/report.go, .planning/phases/02-api-parity-harness-bootstrap/02-CONTEXT.md Load and validate all 154 manifest route ids and their fixtures in `TestParityCorpus`; create stable `t.Run` names from flow ids so `go test ./parity -run 'TestParityCorpus/...'` selects one (D-08, D-12, D-16). Before ported route fixtures, replay their seed flow against the Go handler; for writes not yet ported, invoke only a manifest-declared temporary named hook, with its replacing route id and removal condition documented (D-09, D-10). In Phase 2, the real PHP seed endpoints and all 154 PHP routes are unported, so classify them `pending`, show them as recorded/pending in the coverage report, and do not send them to the synthetic handler or count them as passing. The separate synthetic seed/read subtests prove replay semantics. As Phase 3 provides the real genres handler, `newTarget` switches to it and its manifest row becomes `ported`; temporary genres SQL hook gives it the row until POST genres is ported. A `ported` missing fixture, missing hook, response mismatch or capture failure must fail its subtest and preserve path diagnostics. Print recorded/passing/failing/unrecorded/pending totals without equating pending with pass. Keep PHP self-replay strict over all fixtures independent of Go status. go vet ./... && go test ./... && (cd ../fonoteka.go && go vet ./... && go test ./parity -run 'TestParitySynthetic|TestParityCorpus' -count=1 && go test ./...) - All 154 route ids appear as recorded and pending; zero PHP routes appear as Go passing in Phase 2. - Synthetic write/read subtests pass, and `-run` can select one named flow. - A deliberately `ported` route without matching Go behavior fails with the actual path diff. - Both root and sibling Go modules pass vet/test after the task commit. The corpus is wired into CI with accurate pending/ported semantics and a Phase 3 handler seam.

<threat_model>

Trust Boundaries

Boundary Description
Fixture/manifest → app test and SQL seed hooks Data files select HTTP requests and named privileged DB setup functions.
Test suite → container runtime Testcontainers creates and destroys a disposable database.

STRIDE Threat Register

Threat ID Category Severity Component Disposition Mitigation Plan
T-02-04 Tampering high parity_test.go status gate mitigate Require exact manifest route set, distinguish pending from passing, fail ported mismatches and unknown hooks.
T-02-06 Elevation of privilege high SQL seed-hook dispatch mitigate Allow only named functions registered in test code; never execute SQL text from YAML, and use parameterized inserts.
T-02-SC Tampering high App Go module installs mitigate Apply RESEARCH.md Package Legitimacy Audit and slopcheck before testcontainers module addition; block ASSUMED/SUS packages for human verification.
</threat_model>
Run app `go vet ./...`, `go test ./...`, the selected synthetic subtest and full corpus subtests; run root vet/test too. Confirm the report says 154 recorded/pending and zero passing PHP routes.

<success_criteria> An actual Postgres-backed synthetic flow passes through the same replay engine; the full PHP corpus is visible as selectable pending subtests without false passes. </success_criteria>

Create `.planning/phases/02-api-parity-harness-bootstrap/02-04-SUMMARY.md` after completion.