--- phase: 6 slug: http-routing-auth-groups-and-rate-limiting status: planned nyquist_compliant: true wave_0_complete: false created: 2026-09-19 --- # Phase 6 — Validation Strategy > Per-phase validation contract for feedback sampling during execution. --- ## Test Infrastructure | Property | Value | |----------|-------| | **Framework** | Go stdlib `testing` + `testify` (assert/require); `net/http/httptest` for router, limiter, CORS and fetch-helper tests; `testcontainers-go` Postgres only where the `inv_token` guard and parity harness need real rows | | **Config file** | none — plain `func TestX(t *testing.T)`; parity `TestMain` in `../fonoteka.go/parity` is reused | | **Quick run command** | `go vet ./... && go test ./... -short` (run in the repo the task writes to) | | **Full suite command** | `go test ./... -race` in `summercms.go` and in `../fonoteka.go` | | **Estimated runtime** | ~20 s quick, ~120-180 s full | --- ## Sampling Rate - **After every task commit:** Run `go vet ./... && go test ./... -short` - **After every plan wave:** Run `go test ./...` in both repos - **Before `/gsd:verify-work`:** Full suite green in both repos with `-race`, parity harness green on genres under both auth groups, swag + `openapi-typescript` gate green - **Max feedback latency:** 120 seconds --- ## Per-Task Verification Map | Task ID | Plan | Wave | Requirement | Threat Ref | Secure Behavior | Test Type | Automated Command | File Exists | Status | |---------|------|------|-------------|------------|-----------------|-----------|-------------------|-------------|--------| | 06-01-T1 | 06-01 | 1 | HTTP-05 | T-06-01 | Router verbs + parameterized middleware factories; guard registry primitives unit-tested in isolation | unit | `go test ./surf/... ./bouncer/... ./pact/... -short` (summercms.go) | ✅ created by plan | ⬜ pending | | 06-01-T2 | 06-01 | 1 | HTTP-05 | T-06-04, T-06-05 | Real inv_token guard + inv.scope exact 401/403 bodies; jwt guard behaviorally unchanged | unit + testcontainers | `go test ./plugins/golem15/fonoteka/... ./plugins/golem15/user/... -short` (fonoteka.go) | ✅ created by plan | ⬜ pending | | 06-01-T3 | 06-01 | 1 | HTTP-03 | T-06-02 | Same handler serves JWT and personal-token genres; parity-green on both | integration + parity | `go test ./plugins/golem15/fonoteka/... -run TestGenresSharedHandler -short && go test ./parity/... -run TestParityCorpus` (fonoteka.go) | ✅ created by plan | ⬜ pending | | 06-02-T1 | 06-02 | 2 | HTTP-04 | T-06-06 | Fixed-window Store/FixedWindowLimiter (distinct from the pre-existing surf.Limiter interface seam) matching Laravel wire contract; trusted-proxy ClientIP | unit | `go test ./surf/... -run 'TestFixedWindowLimiter\|TestClientIP' -short` (summercms.go) | ✅ created by plan | ⬜ pending | | 06-02-T2 | 06-02 | 2 | HTTP-04 | T-06-07 | Five named buckets registered; token group throttled; PublicShareHeaders 429 rewrite | unit + integration | `go test ./plugins/golem15/fonoteka/... -run TestPublicShareHeaders -short` (fonoteka.go) | ✅ created by plan | ⬜ pending | | 06-02-T3 | 06-02 | 2 | HTTP-04 | T-06-09 | All six declared route groups boot cleanly; APP_DEBUG=false pinned for future fixture recordings | integration | `go test ./plugins/golem15/fonoteka/... -run TestAllRouteGroupsBoot -short` (fonoteka.go) | ✅ created by plan | ⬜ pending | | 06-03-T1 | 06-03 | 3 | HTTP-06 | T-06-10, T-06-11 | Raw group refuses house-envelope middleware (declared only via the pact.HasHouseMiddleware plugin capability) at registration; bare 500 on raw panic; route table + route:list | unit | `go test ./surf/... -run 'TestRawGroup\|TestRouteTable\|TestHouseMiddleware' -short` (summercms.go) | ✅ created by plan | ⬜ pending | | 06-03-T2 | 06-03 | 3 | HTTP-06, HTTP-08 | — | wire response helpers (JSON writer, +00:00 time, tri-state bool, never-nil slice); swag→openapi-typescript pipeline | unit + build-gate | `go test ./wire/... -short && bash scripts/check-openapi.sh` (fonoteka.go) | ✅ created by plan | ⬜ pending | | 06-03-T3 | 06-03 | 3 | HTTP-09 | T-06-12, T-06-13 | Path-scoped CORS matches config/cors.php; body limits enforced with operator-confirmed production numbers; oauth group raw | integration + human-verify | `go test ./surf/... -run 'TestCORS\|TestBodyLimit' -short` (summercms.go); blocking checkpoint for production body-size numbers | ✅ created by plan | ⬜ pending | | 06-04-T1 | 06-04 | 1 | HTTP-07 | T-06-14 | Private/reserved/CGNAT/metadata IP table matches ManualCoverUrlFetcher.php exactly | unit | `go test ./fetchguard/... -run TestIsReservedOrPrivate -v` (summercms.go) | ✅ created by plan | ⬜ pending | | 06-04-T2 | 06-04 | 1 | HTTP-07 | T-06-14, T-06-15, T-06-16, T-06-17, T-06-18 | Dial-time SSRF guard, https-only, no redirects, streaming byte cap, typed failure reasons | unit (httptest) | `go test ./fetchguard/... -short -race` (summercms.go) | ✅ created by plan | ⬜ pending | | 06-05-T1 | 06-05 | 4 | HTTP-03..09 | all (coverage sweep) | Framework-side coverage gaps closed (bouncer/surf/wire/fetchguard) | unit | `go test ./... -race -short` (summercms.go) | ✅ created by plan | ⬜ pending | | 06-05-T2 | 06-05 | 4 | HTTP-03..09 | T-06-02, T-06-10 (full completion) | App-side coverage gaps; full route-table mutual-exclusivity over every route, not just genres | unit + parity | `go test ./... -race -short && go test ./parity/... -run TestParityCorpus` (fonoteka.go) | ✅ created by plan | ⬜ pending | | 06-05-T3 | 06-05 | 4 | HTTP-03..09 | T-06-19, T-06-20 | Every T-06-xx threat mapped to a passing test or restated acceptance | doc + grep-gate | `grep -c "^\| T-06-" 06-SECURITY-REVIEW.md` | ✅ created by plan | ⬜ pending | *Status: ⬜ pending · ✅ green · ❌ red · ⚠️ flaky* --- ## Wave 0 Requirements - [x] `surf/limiter_test.go` — fixed-window Store semantics, named-bucket resolution, inline throttle keys, stacking — scaffolded and filled by Plan 06-02 Task 1 (no pre-existing file; the plan creates it, satisfying the Nyquist "create the scaffold" rule since there is no separate Wave 0 in this phase's plan set) - [x] `bouncer/registry_test.go` — guard register / duplicate-fail / resolve-by-name — scaffolded and filled by Plan 06-01 Task 1 - [x] Fetch-helper package `fetch_test.go`/`ip_test.go` — httptest servers for each typed failure reason — scaffolded and filled by Plan 06-04 Tasks 1-2 - [x] `surf/routetable_test.go` — raw-group middleware refusal at registration, route table contents — scaffolded and filled by Plan 06-03 Task 1 - [x] Existing infra reused: `surf` router tests, `../fonoteka.go/parity` TestMain and `seedHooks` (token insertion for the `inv_token` guard, extended by Plan 06-01 Task 3) - [x] No new test framework No standalone Wave 0 plan is used in this phase's plan set — each implementation plan (06-01 through 06-04) creates and fills its own test files in the same wave as the production code, and every task in every plan carries a concrete `` verify command (confirmed via `verify.plan-structure` on all 5 plans: `hasVerify: true` on every task). Plan 06-05 (wave 4) is the dedicated coverage-closing plan required by this project's lean-mode "unit tests are always the last plan" rule. --- ## Manual-Only Verifications | Behavior | Requirement | Why Manual | Test Instructions | |----------|-------------|------------|-------------------| | Production `client_max_body_size` / `post_max_size` / `upload_max_filesize` values | HTTP-09 | The production nginx vhost and php.ini are operator-managed and not in any repo (06-RESEARCH.md A2) | Plan 06-03 Task 3 is a `checkpoint:human-verify` (`autonomous: false`): operator reads the values from the production host; they are recorded in `fonoteka.go/config/http.yaml` replacing the INTERIM defaults, and the existing body-limit tests (which assert against config-loaded values) are re-run to confirm no regression | --- ## Validation Sign-Off - [x] All tasks have `` verify or Wave 0 dependencies — confirmed via `gsd-sdk query verify.plan-structure` on all 5 PLAN.md files (`hasVerify: true` on every task, including the one `checkpoint:human-verify` task, which also carries an `` block for its automatable portion) - [x] Sampling continuity: no 3 consecutive tasks without automated verify — every task across all 5 plans has one - [x] Wave 0 covers all MISSING references — no file listed as `❌ W0` in the original requirement→test map remains missing; every referenced test file is created by an explicit plan task - [x] No watch-mode flags — all commands are one-shot `go test`/`go vet`/`bash` invocations - [x] Feedback latency < 120s — quick-run commands are package-scoped `-short` runs - [x] `nyquist_compliant: true` set in frontmatter **Approval:** planned — ready for `/gsd:execute-phase 06`