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.
path
provides
tide/diff_contract_test.go
Complete parity-class negative tests
path
provides
tide/proxy_security_test.go
Loopback, bounds, scrub and traversal regression tests
path
provides
../fonoteka.go/parity/parity_contract_test.go
Pending/ported and SQL-hook integration assertions
path
provides
scripts/check-phase2.sh
Repeatable root/app vet/test/race and corpus audit gate
**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.
@.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.
<threat_model>
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.
</threat_model>
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.
<success_criteria>
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.
</success_criteria>
Create `.planning/phases/02-api-parity-harness-bootstrap/02-05-SUMMARY.md` after completion.