--- phase: 06-http-routing-auth-groups-and-rate-limiting plan: 12 subsystem: surf, bouncer tags: [gap-closure, body-limit, rate-limit, jwt, fail-closed] requirements: [HTTP-04, HTTP-09, HTTP-05, HTTP-06] key-files: modified: [surf/router.go, surf/limiter.go, bouncer/registry.go, bouncer/jwt.go, surf/bodylimit_test.go, surf/limiter_test.go, surf/cors_test.go, bouncer/registry_test.go, bouncer/jwt_test.go] metrics: tasks: 3 completed: 2026-09-21 --- # Phase 6 Plan 12: surf/bouncer fail-closed gap closure Summary Body cap now bounds every named middleware (inside recovery), limiter definitions and body config fail boot instead of failing open, mux conflicts return errors, and bouncer rejects typed-nil guards and fractional JWT subjects. ## Commits - a50e09b: router ordering, factory cache, body-config validation, mux conflict error - 4ad2ad2: fail-closed limiter definitions - 8a9449d: typed-nil guard and integer-only JWT subject ## Deviations from Plan - [Rule 3] surf/cors_test.go TestCORSAssembleUsesConfig lacked body_limits keys; added them (stricter validation legitimately requires them). - Plan verification said fonoteka.go buckets must satisfy validation: `go test ./... -short` there is green. ## Self-Check: PASSED go vet and go test ./... -race -short green in summercms.go; fonoteka.go tests green.