docs(02): create verified phase plans

This commit is contained in:
Jakub Zych
2026-09-17 01:57:41 +02:00
parent e3076950c3
commit acd31ba28c
7 changed files with 777 additions and 8 deletions

View File

@@ -0,0 +1,137 @@
---
phase: 02-api-parity-harness-bootstrap
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
- go.mod
- go.sum
- tide/flow.go
- tide/fixture.go
- tide/record.go
- tide/replay.go
- tide/diff.go
- tide/testdata/one-route-spec.yaml
- tide/roundtrip_test.go
- cmd/summer/main.go
- cmd/summer/parity.go
- cmd/summer/parity_test.go
autonomous: true
requirements: [QA-01, QA-02, QA-03]
must_haves:
truths:
- "D-04: A developer can invoke `summer parity:record` and `summer parity:replay` through bonfire against one HTTP route."
- "D-05 D-06: The recorded versioned YAML flow contains an ordered request/response step with verbatim JSON body text."
- "D-12 D-13: Replaying that fixture against an arbitrary httptest HTTP backend succeeds or reports an exact structural JSON path or raw byte mismatch."
artifacts:
- path: tide/flow.go
provides: Framework-owned, versioned flow and step contracts
- path: tide/fixture.go
provides: Validated YAML load and atomic save
- path: tide/record.go
provides: HTTP recording engine
- path: tide/replay.go
provides: Backend-independent replay engine
- path: cmd/summer/parity.go
provides: Bonfire parity commands
key_links:
- from: cmd/summer/main.go
to: cmd/summer/parity.go
via: toolCommands registration
- from: cmd/summer/parity.go
to: tide/record.go
via: RecordFlow API
- from: cmd/summer/parity.go
to: tide/replay.go
via: ReplayFlow API
---
<objective>
**As a** port developer, **I want to** record one HTTP route and replay it against another backend, **so that** I can see the first parity result before porting an endpoint.
Purpose: Establish a complete fixture-to-diff path on the Phase 1 command kernel.
Output: Generic `tide` flow schema, fixture IO, one-route recorder/replayer, and two working `summer parity:*` commands.
</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/ROADMAP.md
@.planning/REQUIREMENTS.md
@.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-PATTERNS.md
@bonfire/command.go
@cmd/summer/main.go
<interfaces>
`bonfire.Command` is a value with `Name`, `Description`, `Flags []bonfire.Flag`, `Args []bonfire.Arg`, and `Run(context.Context, bonfire.Input, bonfire.Output) error`. Flags are strings; `bonfire.Input.Flag(name)` retrieves them. `cmd/summer/main.go` assembles values in `toolCommands()` and `main()` maps command errors to exit status 1.
Define the public `tide` contract here for later plans: `Flow{Version, Name, Description, SeedHook, Steps}`, `Step{ID, RouteID, Request, Response, Capture, Normalize, Headers}`, `Request{Method, Path, Query, Headers, Body}` where Query preserves the raw query string, and `Response{Status, Headers, Body, BodyFile, SHA256}`. Use `LoadFlow`, `SaveFlow`, `RecordFlow`, `ReplayFlow`, and a `Result` carrying per-step differences. Keep transport/target, fixture filesystem and output injected; neither `tide` nor its tests import Fonoteka.
</interfaces>
</context>
<tasks>
<task type="auto" tdd="true">
<name>Task 1: Record and replay one route through the summer CLI</name>
<files>go.mod, go.sum, tide/flow.go, tide/fixture.go, tide/record.go, tide/replay.go, tide/diff.go, tide/testdata/one-route-spec.yaml, tide/roundtrip_test.go, cmd/summer/main.go, cmd/summer/parity.go, cmd/summer/parity_test.go</files>
<read_first>bonfire/command.go, bonfire/root.go, cmd/summer/main.go, cmd/summer/main_test.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 local httptest server answers GET /sample with JSON. `parity:record --spec one-route-spec.yaml --target URL --output PATH` produces one valid flow; `parity:replay --fixtures PATH --target URL` succeeds; a changed response fails with `$.data` and expected/actual values. A non-JSON response changed by one byte reports the byte offset.</behavior>
<action>In one committing task, write a version-1 one-step YAML request spec and black-box tests through `bonfire.NewRoot("summer", toolCommands(), writer)` plus `httptest.Server` (D-04, D-05, D-06). Run the focused tests once to observe RED without committing, then implement enough `tide.Flow`, validated YAML load/save, HTTP record/replay and `bonfire` command registration to make the complete round trip GREEN. The fixture keeps request/response body bytes as YAML literal block scalars, records status and Content-Type, and saves atomically; changed JSON scalar or non-JSON byte returns a nonzero CLI error with expected/actual diagnostics. Include both original and changed handlers in the tests. Before adding direct goccy/go-yaml to `go.mod`, review RESEARCH.md Package Legitimacy Audit and run `/home/jin/.local/bin/slopcheck scan go.mod`; after module resolution inspect `git diff -- go.mod go.sum` and scan again. Keep the package generic, preserve `toolchain go1.27.0`, and commit only after full root vet/test is green. Plan 05 expands behavior coverage.</action>
<verify><automated>/home/jin/.local/bin/slopcheck scan go.mod &amp;&amp; go vet ./... &amp;&amp; go test ./... &amp;&amp; go test ./tide ./cmd/summer -run 'TestParityRoundTrip|TestParityCommands' -count=1</automated></verify>
<acceptance_criteria>
- RED is observed before implementation within the task, then all tests are GREEN before its single commit.
- A real one-route fixture records and replays through `summer` against httptest; changed JSON or bytes report a mismatch.
- Full root `go vet ./...` and `go test ./...` pass at commit; tests use temp fixtures and injected writers.
</acceptance_criteria>
<done>A developer can already record and replay one HTTP route through the CLI after this task commits.</done>
</task>
<task type="auto">
<name>Task 2: Make the one-route record/replay path work</name>
<files>go.mod, go.sum, tide/flow.go, tide/fixture.go, tide/record.go, tide/replay.go, tide/diff.go, cmd/summer/main.go, cmd/summer/parity.go, tide/roundtrip_test.go, cmd/summer/parity_test.go</files>
<read_first>go.mod, bonfire/command.go, bonfire/root.go, cmd/summer/main.go, tide/testdata/one-route-spec.yaml, tide/roundtrip_test.go, cmd/summer/parity_test.go, .planning/phases/02-api-parity-harness-bootstrap/02-PATTERNS.md</read_first>
<action>Refine the working Plan 01 Task 1 path into the full public `tide` contract above: strict `version: 1` goccy/go-yaml decode rejects unknown fields, empty flow names, unsafe paths and duplicate step ids (D-05, D-06); atomic save syncs before rename. Inject `http.Client` and context, bound request/response body reads, and reject truncation before committing a fixture. In `diff.go`, decode JSON with `UseNumber`, recursively compare exact key sets, array lengths, scalar values and token types while ignoring object key order; report `$.path` with expected and actual values. For non-JSON content types, compare bytes and report offset plus printable escaped bytes. Keep `parity:record` and `parity:replay` in `toolCommands()` with string flags `--spec`, `--target`, `--output`, `--fixtures`; errors return through bonfire so main exits nonzero (D-04, D-12, D-13). Reinspect the `go.mod`/`go.sum` diff and rerun slopcheck if dependency metadata changes. Do not add an app route, PHP-specific field or standalone CLI.</action>
<verify><automated>go vet ./... &amp;&amp; go test ./... &amp;&amp; go test ./tide ./cmd/summer -run 'TestParityRoundTrip|TestParityCommands' -count=1</automated></verify>
<acceptance_criteria>
- `parity:record` writes one replayable YAML flow and `parity:replay` accepts it.
- Identical JSON with changed object key order passes; missing keys and changed string/number types fail at named paths.
- Non-JSON changes fail with byte diagnostics; malformed YAML and oversized bodies fail before committing a fixture.
- Root `go vet ./...` and `go test ./...` exit 0.
</acceptance_criteria>
<done>A developer can record and replay one fixture using the installed summer tool.</done>
</task>
</tasks>
<threat_model>
## Trust Boundaries
| Boundary | Description |
|---|---|
| YAML spec and HTTP target → local process | Untrusted paths, headers and bodies become outbound requests and fixture files. |
## STRIDE Threat Register
| Threat ID | Category | Severity | Component | Disposition | Mitigation Plan |
|---|---|---|---|---|---|
| T-02-01 | Tampering/Denial of service | high | `tide.LoadFlow`, recorder and fixture writer | mitigate | Strict schema, bounded request/response body, context cancellation, safe output path and atomic writes; reject partial recordings. |
| T-02-SC | Tampering | high | Go module changes | mitigate | Apply RESEARCH.md Package Legitimacy Audit before module resolution and run slopcheck; block any package marked ASSUMED/SUS pending human verification. |
</threat_model>
<verification>
Run the one-route CLI smoke against httptest, then root `go vet ./...` and `go test ./...`. Confirm the written YAML can be loaded and replayed without changing the input spec.
</verification>
<success_criteria>
One route fixture records and replays through bonfire, a modified JSON field produces a structural path diff, and modified non-JSON bytes produce a byte diff.
</success_criteria>
<output>
Create `.planning/phases/02-api-parity-harness-bootstrap/02-01-SUMMARY.md` after completion.
</output>