138 lines
9.5 KiB
Markdown
138 lines
9.5 KiB
Markdown
---
|
|
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 && go vet ./... && go test ./... && 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 ./... && go test ./... && 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>
|