--- phase: 02-api-parity-harness-bootstrap plan: 01 subsystem: testing tags: [tide, parity, yaml, httptest, bonfire, goccy, json-diff] requires: - phase: 01-framework-kernel-foundation provides: bonfire.Command kernel and cmd/summer toolCommands() provides: - Version-1 tide Flow/Step request/response contracts - Validated YAML load and atomic fixture save - HTTP RecordFlow/ReplayFlow against an injected target - summer parity:record and parity:replay commands affects: [02-02, 02-03, 02-04, 02-05] tech-stack: added: - github.com/goccy/go-yaml v1.19.2 patterns: - Fixture bodies are YAML literal block scalars; JSON compares structurally with UseNumber - Record/replay inject http.Client, context, target URL and a body size cap - Bonfire string flags --spec --target --output --fixtures; errors exit through cobra key-files: created: - 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/parity.go - cmd/summer/parity_test.go modified: - cmd/summer/main.go - go.mod - go.sum key-decisions: - "tide is the framework-owned parity library; it does not import Fonoteka" - "goccy/go-yaml v1.19.2 decodes fixtures with DisallowUnknownField; save uses a dedicated encoder so literal bodies keep nested indent" - "JSON diffs ignore object key order and fail on missing keys or token-type changes at $.path" - "Non-JSON bodies compare raw bytes and report offset plus printable escaped windows" - "toolchain go1.27.0 is restored after go mod tidy drops it" patterns-established: - "Pattern: CLI tests drive bonfire.NewRoot with an injected writer and httptest.Server, never a real TTY" - "Pattern: SaveFlow writes a temp file, Syncs, then rename; RecordFlow errors never create the destination" requirements-completed: [QA-01, QA-02, QA-03] duration: 7min completed: 2026-09-17 --- # Phase 2 Plan 1: Record and Replay One Fixture Summary **A developer can record GET /sample through `summer parity:record` and replay it with `summer parity:replay` against httptest, with JSON path diffs at `$.data` and non-JSON byte-offset diffs** ## Performance - **Duration:** 7 min - **Started:** 2026-09-17T10:14:10Z - **Completed:** 2026-09-17T10:21:51Z - **Tasks:** 2 - **Files modified:** 12 ## Accomplishments - Generic `tide` package owns version-1 Flow/Step contracts, validated YAML IO, HTTP record/replay and structural JSON/byte diffs - `parity:record` and `parity:replay` register on the Phase 1 bonfire kernel with string flags `--spec`, `--target`, `--output`, `--fixtures` - One-route testdata spec records and replays against httptest; changed JSON or bytes fail the CLI with expected/actual diagnostics ## Task Commits Each task was committed atomically: 1. **Task 1: Record and replay one route through the summer CLI** - `f6e1b89` (feat) 2. **Task 2: Make the one-route record/replay path work** - `f669d05` (feat) **Plan metadata:** pending `docs(02-01)` commit _Note: TDD RED was observed as compile/command discovery failure inside Task 1 and was not committed separately, per the plan's single-green-commit instruction._ ## Files Created/Modified - `tide/flow.go` - Flow/Step/Request/Response types, Result, MismatchError, validation - `tide/fixture.go` - LoadFlow/ParseFlow/SaveFlow with unknown-field rejection and atomic sync+rename - `tide/record.go` - RecordFlow with injected client, context and bounded bodies - `tide/replay.go` - ReplayFlow against an arbitrary HTTP target - `tide/diff.go` - UseNumber JSON compare and non-JSON byte-offset diffs - `tide/testdata/one-route-spec.yaml` - version-1 GET /sample request spec - `tide/roundtrip_test.go` - TestParityRoundTrip library round trip and contract cases - `cmd/summer/parity.go` - parity:record and parity:replay commands - `cmd/summer/parity_test.go` - TestParityCommands through bonfire.NewRoot - `cmd/summer/main.go` - registers the parity commands on toolCommands() - `go.mod` / `go.sum` - direct github.com/goccy/go-yaml v1.19.2, toolchain go1.27.0 kept ## Decisions Made - Named the harness library `tide` as allowed by CONTEXT.md discretion - Decode with goccy; encode fixtures with a small dedicated writer so `body: |-` content is indented under nested mappings (goccy BytesMarshaler dropped indent) - Compare JSON structurally (key sets, array lengths, scalars, json.Number token types) and ignore object key order - Inject `http.Client`, context and MaxBody; default cap is 8 MiB; truncation fails before SaveFlow - Keep the package generic: no Fonoteka route names, PHP fields, or standalone CLI ## Deviations from Plan ### Auto-fixed Issues **1. [Rule 2 - Missing Critical] Restored toolchain go1.27.0 after go get/tidy dropped it** - **Found during:** Task 1 (module resolution) - **Issue:** `go get github.com/goccy/go-yaml@v1.19.2` and `go mod tidy` removed the `toolchain go1.27.0` directive the plan requires - **Fix:** Re-inserted `toolchain go1.27.0` in go.mod after tidy - **Files modified:** go.mod - **Verification:** `grep toolchain go.mod` shows `toolchain go1.27.0`; go vet/test pass - **Committed in:** f6e1b89 (Task 1 commit) **2. [Rule 1 - Bug] Dedicated YAML encoder for literal nested bodies** - **Found during:** Task 1 (GREEN of SaveFlow) - **Issue:** goccy BytesMarshaler parsed `|-` scalars but re-emitted content without nested indent, so LoadFlow failed with `unexpected map key` - **Fix:** SaveFlow writes version-1 YAML with correctly indented block scalars; LoadFlow still uses goccy with DisallowUnknownField - **Files modified:** tide/fixture.go - **Verification:** TestParityRoundTrip records YAML containing `body: |-` and reloads it - **Committed in:** f6e1b89 (Task 1 commit) --- **Total deviations:** 2 auto-fixed (1 missing critical, 1 bug) **Impact on plan:** Required for the pinned toolchain and for round-tripping recorded JSON bodies. No scope creep. ## Issues Encountered - slopcheck on the updated go.mod reported goccy as OK. Pre-existing SUS findings on `golang.org/x/term` and `golang.org/x/sys` were unchanged and not newly added - QA-01/QA-02/QA-03 remain phase-level: this plan delivers the one-route CLI path, not the 154-route corpus, normalizer classes, or testcontainers. Frontmatter copies the plan's requirements list; they should not be treated as milestone-complete ## User Setup Required None - no external service configuration required. ## Next Phase Readiness Ready for 02-02-PLAN.md (proxy sessions and manifest-driven record with strict diffs). Public APIs: `LoadFlow`, `SaveFlow`, `RecordFlow`, `ReplayFlow`, `Flow`, `Result`. ## Verification - RED observed: `go test ./tide ./cmd/summer -run 'TestParityRoundTrip|TestParityCommands'` failed with undefined LoadFlow/RecordFlow and missing `parity:record` before implementation - GREEN: same focused tests pass; `go vet ./...` and `go test ./...` exit 0 - slopcheck: goccy VERIFIED/OK; toolchain pin preserved ## Self-Check: PASSED - [x] Key files exist on disk - [x] `git log --grep=02-01` returns Task 1 and Task 2 commits - [x] Task acceptance criteria re-run and pass - [x] Plan verification commands pass --- *Phase: 02-api-parity-harness-bootstrap* *Completed: 2026-09-17*