docs(02): create verified phase plans
This commit is contained in:
130
.planning/phases/02-api-parity-harness-bootstrap/02-04-PLAN.md
Normal file
130
.planning/phases/02-api-parity-harness-bootstrap/02-04-PLAN.md
Normal file
@@ -0,0 +1,130 @@
|
||||
---
|
||||
phase: 02-api-parity-harness-bootstrap
|
||||
plan: 04
|
||||
type: execute
|
||||
wave: 4
|
||||
depends_on: [02-03]
|
||||
files_modified:
|
||||
- ../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
|
||||
autonomous: true
|
||||
requirements: [QA-02, QA-03]
|
||||
must_haves:
|
||||
truths:
|
||||
- "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."
|
||||
artifacts:
|
||||
- path: ../fonoteka.go/parity/parity_test.go
|
||||
provides: App-owned corpus runner, pending/ported subtests and coverage
|
||||
- path: ../fonoteka.go/parity/synthetic_test.go
|
||||
provides: SQL-backed httptest handler and Postgres lifecycle
|
||||
- path: ../fonoteka.go/parity/testdata/synthetic-seed.yaml
|
||||
provides: Integration seed flow with one real HTTP write
|
||||
- path: ../fonoteka.go/parity/testdata/synthetic-read.yaml
|
||||
provides: Integration read flow and named seed hook
|
||||
key_links:
|
||||
- from: ../fonoteka.go/parity/parity_test.go
|
||||
to: ../fonoteka.go/parity/manifest.yaml
|
||||
via: Ordered flow subtests and pending/ported status
|
||||
- from: ../fonoteka.go/parity/parity_test.go
|
||||
to: ../fonoteka.go/parity/synthetic_test.go
|
||||
via: Injected handler factory and seed-hook registry
|
||||
- from: ../fonoteka.go/parity/synthetic_test.go
|
||||
to: tide/replay.go
|
||||
via: httptest target replaying SQL-backed fixtures
|
||||
---
|
||||
|
||||
<objective>
|
||||
**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.
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@/home/jin/.codex/get-shit-done/workflows/execute-plan.md
|
||||
@/home/jin/.codex/get-shit-done/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<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
|
||||
<interfaces>
|
||||
`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.
|
||||
</interfaces>
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto" tdd="true">
|
||||
<name>Task 1: Replay a synthetic write/read flow against Postgres</name>
|
||||
<files>../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</files>
|
||||
<read_first>../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</read_first>
|
||||
<behavior>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.</behavior>
|
||||
<action>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.</action>
|
||||
<verify><automated>/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 ./...)</automated></verify>
|
||||
<acceptance_criteria>
|
||||
- `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.
|
||||
</acceptance_criteria>
|
||||
<done>The app module has a real DB-backed in-process parity integration path.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: Expose the full corpus as honest selectable subtests</name>
|
||||
<files>../fonoteka.go/parity/parity_test.go, ../fonoteka.go/parity/README.md</files>
|
||||
<read_first>../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</read_first>
|
||||
<action>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.</action>
|
||||
<verify><automated>go vet ./... && go test ./... && (cd ../fonoteka.go && go vet ./... && go test ./parity -run 'TestParitySynthetic|TestParityCorpus' -count=1 && go test ./...)</automated></verify>
|
||||
<acceptance_criteria>
|
||||
- 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.
|
||||
</acceptance_criteria>
|
||||
<done>The corpus is wired into CI with accurate pending/ported semantics and a Phase 3 handler seam.</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<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>
|
||||
|
||||
<verification>
|
||||
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.
|
||||
</verification>
|
||||
|
||||
<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>
|
||||
|
||||
<output>
|
||||
Create `.planning/phases/02-api-parity-harness-bootstrap/02-04-SUMMARY.md` after completion.
|
||||
</output>
|
||||
Reference in New Issue
Block a user