Files
summercms/.planning/phases/06-http-routing-auth-groups-and-rate-limiting/06-VALIDATION.md

8.7 KiB

phase, slug, status, nyquist_compliant, wave_0_complete, created
phase slug status nyquist_compliant wave_0_complete created
6 http-routing-auth-groups-and-rate-limiting planned true false 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

  • 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)
  • bouncer/registry_test.go — guard register / duplicate-fail / resolve-by-name — scaffolded and filled by Plan 06-01 Task 1
  • 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
  • surf/routetable_test.go — raw-group middleware refusal at registration, route table contents — scaffolded and filled by Plan 06-03 Task 1
  • 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)
  • 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 <automated> 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

  • All tasks have <automated> 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 <automated> block for its automatable portion)
  • Sampling continuity: no 3 consecutive tasks without automated verify — every task across all 5 plans has one
  • 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
  • No watch-mode flags — all commands are one-shot go test/go vet/bash invocations
  • Feedback latency < 120s — quick-run commands are package-scoped -short runs
  • nyquist_compliant: true set in frontmatter

Approval: planned — ready for /gsd:execute-phase 06