The six plans are in, and three of the four success criteria hold. AUTH-01 stays blocked because the 15 user API routes are still pending against the recorded PHP bodies. Co-authored-by: Cursor <cursoragent@cursor.com>
5.6 KiB
phase, verified, status, score, overrides_applied
| phase | verified | status | score | overrides_applied |
|---|---|---|---|---|
| 07-user-plugin-and-authentication | 2026-09-22T17:26:00Z | gaps_found | 3/4 must-haves verified | 0 |
Phase 7: User plugin and authentication Verification Report
Phase Goal: The user plugin is ported with registration, login, JWT issue/refresh, organizations, personal API tokens and the must-change-password lock. Verified: 2026-09-22T17:26:00Z Status: gaps_found
Goal Achievement
Observable Truths
| # | Truth | Status | Evidence |
|---|---|---|---|
| 1 | A user can register, log in, log out, reset a password, verify email, and receive a JWT the Nuxt app can use unchanged | ✗ FAILED | Handlers and mail tests exist. The 15 /_user/api/v1 routes are recorded and pending. TestParityCorpus is 7 ported, 162 pending, 0 failing. Wrong-code activate is HTML 500 in PHP and 200 in Go. Fetch after logout is 200 in PHP and 401 in Go. |
| 2 | Organization fields arrive through a fire-and-collect event, and the user module does not import fonoteka | ✓ VERIFIED | TestGetApiArray and TestRegisterImportDirection passed under -race. |
| 3 | Personal tokens mint, list, and revoke inside the read/write/ai ceiling, and a read token is rejected on a write route | ✓ VERIFIED | TestTokenApi and TestInvScope passed under -race. The ported token and locale routes replay green. |
| 4 | The password lock returns 423 except locale and change-password, and locale still resolves while locked | ✓ VERIFIED | TestMustChangePasswordLock and TestLocaleFromPrincipal passed under -race. |
Score: 3/4 truths verified
Required Artifacts
| Artifact | Expected | Status | Details |
|---|---|---|---|
bouncer JWT mint/refresh/blacklist |
Session tokens | ✓ EXISTS + SUBSTANTIVE | Round-trip and concurrent blacklist tests passed |
plugins/golem15/user session and account handlers |
Register through reset and activation | ✓ EXISTS + SUBSTANTIVE | Covered by session, mail, and sequence tests. Two responses differ from PHP |
plugins/golem15/fonoteka tokens and locale |
AUTH-03 and the lock exemption | ✓ EXISTS + SUBSTANTIVE | Ported routes replay green |
parity user-api fixtures |
Byte-identical replay | ✗ RECORDED, NOT REPLAYED | 15 routes stay pending |
Artifacts: 3/4 verified
Key Link Verification
| From | To | Via | Status | Details |
|---|---|---|---|---|
fonoteka getApiArray listener |
user payload | fire-and-collect | ✓ WIRED | TestGetApiArray |
InvScope |
token routes | Phase 6 middleware | ✓ WIRED | TestInvScope returns 403 for a missing write scope |
| user API handlers | Nuxt contract | parity replay | ✗ NOT WIRED | Pending routes are not sent to the Go handler |
Wiring: 2/3 connections verified
Requirements Coverage
| Requirement | Status | Blocking Issue |
|---|---|---|
| AUTH-01 | ✗ BLOCKED | User API responses are not the PHP contract the Nuxt app calls |
| AUTH-02 | ✓ SATISFIED | Event payload and import direction are tested. Checkbox stays open until this phase passes |
| AUTH-03 | ✓ SATISFIED | Mint, list, revoke, and scope ceiling are tested |
| AUTH-04 | ✓ SATISFIED | 423 exemption and lock clear are tested |
| I18N-02 | ✓ SATISFIED | LocaleFromPrincipal is tested, including the locked path |
Coverage: 4/5 requirements satisfied. AUTH-01 blocks phase sign-off. None of the five checkboxes were marked in REQUIREMENTS.md.
Anti-Patterns Found
| File | Line | Pattern | Severity | Impact |
|---|---|---|---|---|
plugins/golem15/user/controllers/api_controller.go |
322 | VerifyActivationCode error discarded |
⚠️ Warning | Wrong code still returns 200 |
jwt_blacklist on logout |
— | Stricter than PHP | ⚠️ Warning | Logged-out bearer is 401 in Go and 200 in PHP |
Anti-patterns: 2 found (0 blockers, 2 warnings)
Human Verification Required
None — the failing comparison is already in the recorded fixtures.
Gaps Summary
Critical Gaps (Block Progress)
- User API is not the PHP contract
- Missing: a green replay of the 15
/_user/api/v1routes - Impact: the Nuxt app would notice activate and fetch-after-logout, and the other pending bodies were not accepted as matches
- Fix: change the Go handlers to the recorded PHP status and body, then flip those routes from
pendingtoportedonly when replay is green
- Missing: a green replay of the 15
Non-Critical Gaps (Can Defer)
None. The schema allow-list for user_throttle and jwt_blacklist is an intentional snapshot gap, not a missing migration.
Recommended Fix Plans
07-07-PLAN.md: Close the user API parity gaps
Objective: Make the 15 recorded /_user/api/v1 routes replay against Go.
Tasks:
- Match authenticated activate and activate-by-code failure to the recorded HTML 500.
- Match fetch-after-logout to the recorded 200, or record an explicit decision that Go's blacklist is the contract and update the fixtures.
- Diff the remaining pending bodies and close each one, then flip the route to
portedonly after replay is green.
Estimated scope: Medium
Verification Metadata
Verification approach: Goal-backward from the ROADMAP success criteria
Must-haves source: ROADMAP.md Phase 7 success criteria
Automated checks: go test ./... -race passed in both modules after the schema-gate fix. Schema drift check returned drift_detected: false.
Human checks required: 0
Regression gate: prior-phase suites are in the same go test ./... -race run and passed
Verified: 2026-09-22T17:26:00Z Verifier: inline goal check after 07-06