docs(02-01): complete record-and-replay one fixture plan

Tasks completed: 2/2
- Record and replay one route through the summer CLI
- Lock the one-route tide record and replay contract

SUMMARY: .planning/phases/02-api-parity-harness-bootstrap/02-01-SUMMARY.md
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Jakub Zych
2026-09-17 12:22:30 +02:00
parent f669d056c9
commit f9ba69c663

View File

@@ -0,0 +1,162 @@
---
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*