--- phase: 02-api-parity-harness-bootstrap plan: 05 type: execute wave: 5 depends_on: [02-01, 02-02, 02-03, 02-04] files_modified: - tide/flow_contract_test.go - tide/diff_contract_test.go - tide/proxy_security_test.go - tide/manifest_contract_test.go - cmd/summer/parity_contract_test.go - ../fonoteka.go/parity/parity_contract_test.go - scripts/check-phase2.sh - .planning/phases/02-api-parity-harness-bootstrap/02-VALIDATION.md autonomous: true requirements: [QA-01, QA-02, QA-03] must_haves: truths: - "QA-01: A source-to-corpus audit proves 154/154 PHP routes have fixtures and named real Nuxt/MCP sessions exist." - "QA-02: Negative contract tests detect nil versus [], Carbon +00:00 versus Z, null versus absent dates, tri-state booleans, envelope/conditional keys and string versus number money." - "QA-03: Both Go modules pass vet/test/race, and the app integration suite actually starts testcontainers Postgres." - "D-07 D-16: Security and continuation tests prove secrets never persist and failures produce full path-level and coverage reports." artifacts: - path: tide/diff_contract_test.go provides: Complete parity-class negative tests - path: tide/proxy_security_test.go provides: Loopback, bounds, scrub and traversal regression tests - path: ../fonoteka.go/parity/parity_contract_test.go provides: Pending/ported and SQL-hook integration assertions - path: scripts/check-phase2.sh provides: Repeatable root/app vet/test/race and corpus audit gate - path: .planning/phases/02-api-parity-harness-bootstrap/02-VALIDATION.md provides: Measured phase verification evidence key_links: - from: scripts/check-phase2.sh to: ../fonoteka.go/parity/parity_test.go via: App module go test with Postgres - from: scripts/check-phase2.sh to: ../fonoteka.go/parity/check_corpus.go via: Exact PHP route and credential audit - from: tide/diff_contract_test.go to: tide/diff.go via: Behavioral negative cases and path diagnostics --- **As a** port developer, **I want to** trust the parity corpus and its failure signals, **so that** future endpoint work cannot pass by masking a response difference or skipping an unported route. Purpose: Finish dedicated unit and integration coverage after all feature slices exist, as required by project guidance. Output: Contract tests, security regression tests, cross-module phase gate and validation evidence. @/home/jin/.codex/get-shit-done/workflows/execute-plan.md @/home/jin/.codex/get-shit-done/templates/summary.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-01-SUMMARY.md @.planning/phases/02-api-parity-harness-bootstrap/02-02-SUMMARY.md @.planning/phases/02-api-parity-harness-bootstrap/02-03-SUMMARY.md @.planning/phases/02-api-parity-harness-bootstrap/02-04-SUMMARY.md @tide/flow.go @tide/diff.go @tide/normalize.go @tide/proxy.go @tide/manifest.go @../fonoteka.go/parity/parity_test.go @../fonoteka.go/parity/manifest.yaml Use the public `tide` Record/Replay/Manifest APIs and CLI values established in Plans 01–04. The root and sibling app are separate Go modules; `go test ./...` in one does not traverse the other. The app's `TestParitySynthetic` must run with a real testcontainers Postgres and `TestParityCorpus` must report all 154 PHP routes as recorded/pending until actual Go handlers are ported. Task 1: Lock down every response parity class with negative tests tide/flow_contract_test.go, tide/diff_contract_test.go, tide/manifest_contract_test.go tide/flow.go, tide/fixture.go, tide/diff.go, tide/normalize.go, tide/replay.go, tide/manifest.go, tide/report.go, .planning/research/PITFALLS.md, .planning/phases/02-api-parity-harness-bootstrap/02-CONTEXT.md Write table-driven public-behavior tests over `RecordFlow`, `ReplayFlow` and manifest reporting. Include one good baseline and individually mutated fixtures for `null` versus `[]` (including empty object), Carbon ISO-8601 `+00:00` versus `Z` and non-date text, date key present-null versus absent, `true`/`false`/`null`, top-level `{data,meta}` envelope and nested conditional-key presence, fixed-decimal money JSON string versus number, integer id versus string/fraction, exact slug, capture-by-reference mismatch, unknown/missing variable and per-step normalization disable (D-11, D-13, D-15). Assert each failure names a stable JSON path and expected/actual type/value; object key reorder must pass. Add status and allow-listed header cases for OAuth `Cache-Control`/`Pragma`, 401 `WWW-Authenticate`/protected-resource-metadata, CSV `Content-Disposition`, plus ignored Date/Server/request id. Add CSV/image exact-byte mismatch and safe binary sidecar digest/path cases. Add two-flow continuation cases: comparison failure allows later steps/flows; capture failure skips only its flow remainder (D-14, D-16). Do not write tests that merely assert private helper structure. go vet ./... && go test ./tide -run 'TestFlowContract|TestDiffContract|TestManifestContract' -count=1 && go test ./... - Every named QA-02 parity class has a passing baseline and at least one failing mutation with a path-level diagnostic. - Header, binary and flow-continuation cases exercise observable Record/Replay results. - Root vet/test remain green after the task commit. Formatting, types and missing-key distinctions cannot be silently normalized away. Task 2: Cover proxy, scrub, CLI and Go pending-state boundaries tide/proxy_security_test.go, cmd/summer/parity_contract_test.go, ../fonoteka.go/parity/parity_contract_test.go tide/proxy.go, tide/variables.go, tide/fixture.go, cmd/summer/parity.go, cmd/summer/main.go, ../fonoteka.go/parity/parity_test.go, ../fonoteka.go/parity/synthetic_test.go, ../fonoteka.go/parity/manifest.yaml, .planning/phases/02-api-parity-harness-bootstrap/02-VALIDATION.md Test fixed loopback proxy target, rejection of client-selected upstream/non-loopback bind, request and response size cap, cookie/redirect passthrough, separate concurrent sessions, atomic write on error, path traversal and binary sidecar digest (T-02-01). Test credential replacement in Authorization, cookie, query and JSON/form body; scan written YAML and CLI error text for JWT, `inv_`, client secret, OAuth code and `auth_token`, then test unknown credential rejection and missing placeholder failure (T-02-02). Run bonfire commands with injected writers and assert `parity:record`, `parity:proxy`, `parity:replay` discovery and nonzero error propagation. In app tests, verify 154 manifest ids match source, all remain pending in Phase 2, a deliberately marked `ported` failing route fails its subtest, unknown seed hooks fail, synthetic SQL read/write and hook are backed by testcontainers Postgres, and pending is never counted as passing (D-08, D-09, D-10, D-12, D-16). Keep Docker absence as an explicit failure in the integration gate; use `-short` only for a separately named fast loop if needed, never for the phase sign-off. go vet ./... && go test ./tide ./cmd/summer -run 'TestProxySecurity|TestParityCommandContract' -count=1 && (cd ../fonoteka.go && go vet ./... && go test ./parity -run 'TestParitySynthetic|TestParityCorpus|TestParityContract' -count=1) - Proxy and fixture security regressions fail under the named attack cases. - CLI errors produce nonzero exit status and do not print secrets. - App test runs real Postgres and reports 154 recorded/pending, zero Go-passing PHP routes. The harness cannot pass by leaking a credential, misrouting proxy traffic, or calling pending routes green. Task 3: Run and document the repeatable phase gate scripts/check-phase2.sh, .planning/phases/02-api-parity-harness-bootstrap/02-VALIDATION.md scripts/check-phase1.sh, .planning/phases/02-api-parity-harness-bootstrap/02-VALIDATION.md, ../fonoteka.go/parity/README.md, ../fonoteka.go/parity/manifest.yaml, .planning/phases/02-api-parity-harness-bootstrap/02-RESEARCH.md Create `scripts/check-phase2.sh` that checks root and `../fonoteka.go` separately with `go vet ./...`, `go test ./...`, `go test -race ./...`, an explicit `TestParitySynthetic` Postgres run, corpus validator `--require-recorded --require-clients --check-secrets`, and CLI synthetic record/replay smoke. In required `--fresh-php` mode, provision a temporary MariaDB container on an ephemeral loopback port with a unique `fonoteka_parity_` database and process-local credentials, never the PHP checkout's `.env` or developer DB. Before migrations, query the active PHP DB connection through artisan, assert it equals that unique name and has zero application tables; then run existing PHP migrations. Read `../fonoteka.go/parity/README.md` and execute its exact documented artisan admin-user bootstrap and `oauth-client` commands against this same verified disposable DB using the same process-local DB environment (D-03); fail if either command fails, and redact generated credentials from logs while passing them only through the private variable store. Start `php artisan serve --host=127.0.0.1 --port=8423` as a child with that DB env. The script itself sets the only permitted replay target `http://127.0.0.1:8423`; reject a caller-supplied `PHP_PARITY_TARGET` or non-loopback target, and use a trap to stop PHP and remove the disposable container. Replay the API seed, all 154 routes and both client flows against that fresh instance, requiring zero failures/unrecorded. Fail on missing Docker, failed DB identity/freshness check, either artisan bootstrap failure, missing route fixture/client flow or any nonzero command; do not skip. Record actual commands, test output summary, route totals, fresh DB preflight and PHP self-replay result in `02-VALIDATION.md` without credentials; mark `nyquist_compliant: true` only when all gates pass. Keep planning doc and code changes in separate commits per CLAUDE.md; preserve the Phase 1 gate. Confirm root and app vet/test were green at each prior implementation commit as QA-03 requires. bash scripts/check-phase2.sh --fresh-php - Both modules pass vet, test and race; the app test visibly starts testcontainers Postgres. - Corpus audit reports exactly 154 recorded routes, zero unrecorded, and Nuxt/MCP flow presence with no secrets. - Fresh PHP self-replay passes every fixture; validation file records command/evidence and only then sets `nyquist_compliant: true`. - The gate proves a unique empty `fonoteka_parity_*` DB before migrations and starts its own PHP child on `127.0.0.1:8423`; caller URL overrides are rejected. - The documented artisan admin-user and OAuth-client bootstrap both run successfully against that same DB before seed replay, with generated secrets absent from output. A single documented gate proves the full Phase 2 harness and corpus are ready for Phase 3. ## Trust Boundaries | Boundary | Description | |---|---| | Malformed fixtures/proxy requests → public harness APIs | Contract tests must prove fail-closed behavior. | | CI → Docker and isolated PHP | Integration gate must distinguish missing infrastructure from a passing test. | ## STRIDE Threat Register | Threat ID | Category | Severity | Component | Disposition | Mitigation Plan | |---|---|---|---|---|---| | T-02-01 | Spoofing/Denial of service | high | Proxy and fixture IO | mitigate | Regression tests for fixed loopback upstream, bounded bodies, path traversal and atomicity. | | T-02-02 | Information disclosure | high | Recorder and CLI output | mitigate | Scrub tests across headers/query/body/cookies and committed corpus scan. | | T-02-03 | Tampering | high | Normalizer and diff | mitigate | Mutation tests for token type, date shape, conditional keys, selected headers and sidecar digest. | | T-02-04 | Tampering | high | Route coverage gate | mitigate | Source-derived 154-id audit and pending/ported negative tests. | | T-02-05 | Denial of service/Data loss | high | Fresh PHP self-replay gate | mitigate | Provision unique disposable DB, assert active connection and empty schema, pin loopback PHP origin, clean up child and container. | | T-02-06 | Elevation of privilege | high | SQL seed hooks | mitigate | Unknown-hook rejection and parameterized SQL behavior tests. | Run `bash scripts/check-phase2.sh --fresh-php`; the script provisions its own isolated PHP DB/server and executes self-replay. Treat an environmental block as an incomplete gate and record it; do not mark the phase compliant without the run. Both modules are vet/test/race green, all 154 PHP routes and real client flows are recorded and self-replay cleanly, Postgres integration executes, and every named parity class has a failing mutation test. Create `.planning/phases/02-api-parity-harness-bootstrap/02-05-SUMMARY.md` after completion.