The validation contract is signed off and the phase plan count is 6/6. Requirement checkboxes stay open while the user-api routes are still pending. Co-authored-by: Cursor <cursoragent@cursor.com>
6.4 KiB
6.4 KiB
phase, slug, status, nyquist_compliant, wave_0_complete, created
| phase | slug | status | nyquist_compliant | wave_0_complete | created |
|---|---|---|---|---|---|
| 7 | user-plugin-and-authentication | signed-off | true | true | 2026-09-22 |
Phase 7 — 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 handler, guard, limiter and locale tests; testcontainers-go v0.44.0 (modules/postgres) only where the throttle table, jti blacklist, token CRUD and parity replay need real rows; postcard memory driver for mail assertions |
| Config file | none — plain func TestX(t *testing.T); testing.Short() gates container-backed tests (convention from lagoon/postgres_test.go, postcard/mailpit_test.go, plugins/golem15/user/updates/postgres_test.go); parity TestMain in ../fonoteka.go/parity is reused |
| Quick run command | go vet ./... && go test ./... -short (run in the repo the task writes to: summercms.go or ../fonoteka.go) |
| Full suite command | go test ./... -race in summercms.go and in ../fonoteka.go, plus summer parity:replay --manifest fonoteka.go/parity/manifest.yaml |
| Estimated runtime | ~20 s quick, ~120–180 s full |
Sampling Rate
- After every task commit: Run
go vet ./... && go test ./... -shortin the repo the task touched - After every plan wave: Run
go test ./... -racein both modules +summer parity:replayagainst the fixtures recorded so far - Before
/gsd:verify-work: Full suite green in both modules, every D-11 fixture recorded and replayed - Max feedback latency: 30 s (quick command)
Per-Task Verification Map
| Task ID | Plan | Wave | Requirement | Threat Ref | Secure Behavior | Test Type | Automated Command | File Exists | Status |
|---|---|---|---|---|---|---|---|---|---|
| 07-02-2 | 07-02 | 2 | AUTH-01 | T-07-01 | login/register/logout/fetch/refresh return the Go session contract; tokens are read from the bearer, not the URL or body | unit + integration | `go test ./plugins/golem15/user/ -run 'TestLogin | TestLogout | TestFetch |
| 07-01-2 | 07-01 | 1 | AUTH-01 | T-07-01 | sliding refresh accepts a token inside refresh_ttl, rejects past it; logout blacklists the jti; the grace window is honoured |
unit | `go test ./bouncer/ -run 'TestRefresh | TestBlacklist | TestMintRefreshBlacklistRoundTrip |
| 07-01-3 | 07-01 | 1 | AUTH-01 | T-07-04 | $2y$ PHP hashes verify; a lower-cost hash is eligible for rehash; per-(user,ip) throttle suspends after 5 |
unit + integration | `go test ./bouncer/ -run 'TestPassword' && go test ./plugins/golem15/user/ -run 'TestCheckAndRecordLogin | TestLoginSixthAttempt'` | ✅ |
| 07-02-3 | 07-02 | 2 | AUTH-02 | — | fonoteka getApiArray listener adds organisation fields, must_change_password, preferred_locale; the user module does not import fonoteka |
unit | go test ./plugins/golem15/fonoteka/ -run TestGetApiArray && go test ./plugins/golem15/user/ -run TestRegisterImportDirection |
✅ | ✅ green |
| 07-04-2 | 07-04 | 3 | AUTH-03 | T-07-08 | mint/list/revoke; scopes read/write/ai and a two-scope mint succeed; admin is rejected before insert; InvScope 403s a read token on a write route |
unit + integration | `go test ./plugins/golem15/fonoteka/ -run 'TestTokenApi | TestMintPersonalToken' && go test ./plugins/golem15/fonoteka/middleware/ -run TestInvScope` | ✅ |
| 07-06-2 | 07-06 | 5 | AUTH-04 | T-07-08 | 423 on genres and tokens while locked; me/locale and change-password succeed; genres succeeds after the lock clears |
integration | go test ./plugins/golem15/fonoteka/ -run TestMustChangePasswordLock |
✅ | ✅ green |
| 07-01-3 | 07-01 | 1 | I18N-02 | — | preferred_locale then Accept-Language then app.locale, including while the password lock is set |
unit | go test ./surf/ -run TestLocaleFromPrincipal |
✅ | ✅ green |
| 07-05-2 | 07-05 | 4 | AUTH-01..04 | — | ported fixtures replay green; the 15 user routes stay pending until Go matches the recorded PHP bodies | parity replay | go test ./parity/ -run TestParityCorpus |
✅ | ✅ green |
Status: ⬜ pending · ✅ green · ❌ red · ⚠️ flaky. Task IDs are filled in by the planner once PLAN.md files exist.
Wave 0 Requirements
fonoteka.go/plugins/golem15/user/session_test.go,register_test.go,sequence_test.go— AUTH-01 session and register coveragesummercms.go/bouncer/mint_test.go,refresh_test.go,blacklist_test.go,phase07_coverage_test.go— AUTH-01 refresh/blacklist isolated from HTTPfonoteka.go/plugins/golem15/fonoteka/token_locale_test.go— AUTH-03 token and locale handlerssummercms.go/surf/locale_from_principal_test.go— I18N-02fonoteka.go/parity/fixtures/routes/*_user_api_v1_*.yaml— recorded against the isolated PHP instance- Framework install: none —
testcontainers-goandtestifyalready present;golang.org/x/cryptois a direct dependency
Manual-Only Verifications
| Behavior | Requirement | Why Manual | Test Instructions |
|---|---|---|---|
| Recording the new parity fixtures | AUTH-01..04 | Needs the isolated PHP instance and a private 0600 vars store; no live JWT in git | Run tide record per D-11 against PHP with the DB-reading seed hook (D-12) for reset/activation codes; commit fixtures, not vars |
PHP $2y$ hash cross-check (Assumption A1) |
AUTH-01 | One-off cross-language confirmation | php -r 'echo password_hash("secret", PASSWORD_BCRYPT);', paste into a Go test that calls bcrypt.CompareHashAndPassword; keep the test afterwards |
| Throttle path confirmation (Assumption A2) | AUTH-01 | Confirms JWTAuth::attempt() reaches Winter's Auth\Manager throttle |
Record one PHP fixture of 6 rapid failed logins and assert the suspended body appears on the 6th |
Validation Sign-Off
- All tasks have
<automated>verify or Wave 0 dependencies - Sampling continuity: no 3 consecutive tasks without automated verify
- Wave 0 covers all MISSING references
- No watch-mode flags
- Feedback latency < 30s
nyquist_compliant: trueset in frontmatter
Approval: signed off 2026-09-22 after the phase-7 test commands above passed