docs(02): create verified phase plans

This commit is contained in:
Jakub Zych
2026-09-17 01:57:41 +02:00
parent e3076950c3
commit acd31ba28c
7 changed files with 777 additions and 8 deletions

View File

@@ -0,0 +1,157 @@
---
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
---
<objective>
**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.
</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/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
<interfaces>
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.
</interfaces>
</context>
<tasks>
<task type="auto">
<name>Task 1: Lock down every response parity class with negative tests</name>
<files>tide/flow_contract_test.go, tide/diff_contract_test.go, tide/manifest_contract_test.go</files>
<read_first>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</read_first>
<action>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.</action>
<verify><automated>go vet ./... &amp;&amp; go test ./tide -run 'TestFlowContract|TestDiffContract|TestManifestContract' -count=1 &amp;&amp; go test ./...</automated></verify>
<acceptance_criteria>
- 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.
</acceptance_criteria>
<done>Formatting, types and missing-key distinctions cannot be silently normalized away.</done>
</task>
<task type="auto">
<name>Task 2: Cover proxy, scrub, CLI and Go pending-state boundaries</name>
<files>tide/proxy_security_test.go, cmd/summer/parity_contract_test.go, ../fonoteka.go/parity/parity_contract_test.go</files>
<read_first>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</read_first>
<action>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.</action>
<verify><automated>go vet ./... &amp;&amp; go test ./tide ./cmd/summer -run 'TestProxySecurity|TestParityCommandContract' -count=1 &amp;&amp; (cd ../fonoteka.go &amp;&amp; go vet ./... &amp;&amp; go test ./parity -run 'TestParitySynthetic|TestParityCorpus|TestParityContract' -count=1)</automated></verify>
<acceptance_criteria>
- 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.
</acceptance_criteria>
<done>The harness cannot pass by leaking a credential, misrouting proxy traffic, or calling pending routes green.</done>
</task>
<task type="auto">
<name>Task 3: Run and document the repeatable phase gate</name>
<files>scripts/check-phase2.sh, .planning/phases/02-api-parity-harness-bootstrap/02-VALIDATION.md</files>
<read_first>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</read_first>
<action>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_<run-id>` 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.</action>
<verify><automated>bash scripts/check-phase2.sh --fresh-php</automated></verify>
<acceptance_criteria>
- 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.
</acceptance_criteria>
<done>A single documented gate proves the full Phase 2 harness and corpus are ready for Phase 3.</done>
</task>
</tasks>
<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>
<verification>
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.
</verification>
<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>
<output>
Create `.planning/phases/02-api-parity-harness-bootstrap/02-05-SUMMARY.md` after completion.
</output>