Files
summercms/.planning/phases/13-p-ytarium-api-wishlist-notifications-csv-credentials-public/13-VERIFICATION.md
2026-10-03 12:04:54 +02:00

27 KiB
Raw Blame History

phase, verified, status, score, covered_files, covered_digest, covered_files_note, behavior_unverified, overrides_applied, mvp_mode_note, deferred, human_verification
phase verified status score covered_files covered_digest covered_files_note behavior_unverified overrides_applied mvp_mode_note deferred human_verification
13-p-ytarium-api-wishlist-notifications-csv-credentials-public 2026-10-03T10:02:27Z human_needed 5/5 roadmap success criteria verified; plan truths 87/89 verified, 1 deviates from its literal text (D-03 queue names, routed to human for an override decision), 1 backstop truth holds by control flow with no dedicated race test (insufficient_spec, routed to human)
.planning/phases/13-p-ytarium-api-wishlist-notifications-csv-credentials-public/13-01-PLAN.md
.planning/phases/13-p-ytarium-api-wishlist-notifications-csv-credentials-public/13-01-SUMMARY.md
.planning/phases/13-p-ytarium-api-wishlist-notifications-csv-credentials-public/13-02-PLAN.md
.planning/phases/13-p-ytarium-api-wishlist-notifications-csv-credentials-public/13-02-SUMMARY.md
.planning/phases/13-p-ytarium-api-wishlist-notifications-csv-credentials-public/13-03-PLAN.md
.planning/phases/13-p-ytarium-api-wishlist-notifications-csv-credentials-public/13-03-SUMMARY.md
.planning/phases/13-p-ytarium-api-wishlist-notifications-csv-credentials-public/13-04-PLAN.md
.planning/phases/13-p-ytarium-api-wishlist-notifications-csv-credentials-public/13-04-SUMMARY.md
.planning/phases/13-p-ytarium-api-wishlist-notifications-csv-credentials-public/13-05-PLAN.md
.planning/phases/13-p-ytarium-api-wishlist-notifications-csv-credentials-public/13-05-SUMMARY.md
.planning/phases/13-p-ytarium-api-wishlist-notifications-csv-credentials-public/13-06-PLAN.md
.planning/phases/13-p-ytarium-api-wishlist-notifications-csv-credentials-public/13-06-SUMMARY.md
modules/conga/conga.go
modules/lagoon/validate_rules.go
modules/surf/overlap.go
modules/surf/router.go
modules/tide/centrifugo_golden.go
modules/tide/diff.go
scripts/check-phase13.sh
v2:sha256:78681e983350c9a3f226b9d36a65ce24aa1fc92a9387d72798ffc30d5aee688c verification.fingerprint refuses paths outside the summercms.go root, so the fonoteka.go and sm-user-plugin implementation files (the bulk of this phase) are not in the digest; their state at verification is fonoteka.go 40c575e (clean tree) and sm-user-plugin 8a65890 (clean tree). 0 0 ROADMAP marks Phase 13 mode: mvp, but the goal is not a User Story (user-story.validate: valid=false) and no 13-*-PLAN.md carries one. Following the Phase 1/3/5/8-12 precedent, the five ROADMAP success criteria are the contract, User Flow Coverage is derived from them, and plan must_haves are supporting evidence.
truth addressed_in evidence
Wishlist match/apply-release routes, ai-credential/test and discogs-credential/test pass the parity diff Phase 14 Phase 14 SC4: 'the wishlist wishlist/albums/{id}/match and apply-release routes, discogs-credential/test and the CSV row-edit Discogs pick (selected_discogs_id) pass the parity diff'; SC5: 'ai-credential/test passes the parity diff, and the AI resolver's admin tier uses the backend global vision model'
truth addressed_in evidence
CSV import/match jobs and the wishlist digest job are worked (bodies, digest mail, queue-row delete) Phase 14 Phase 14 SC1: 'The CSV import write job and the self-redispatching Discogs match job ... both complete correctly'; SC2: 'The wishlist digest job coalesces a 30-minute window and deletes its queue row on completion'
truth addressed_in evidence
Notification pruning (fonoteka:prune-notifications) Phase 14 Phase 14 SC6: 'the ported oauth-client/prune-notifications/reindex commands all run correctly'
test expected why_human
Decide on the D-03 queue-name deviation: the 13-01 truth pins queues fonoteka.csv.import, fonoteka.csv.match, fonoteka.wishlist.digest; the code (classes/job_contract.go:33-46) uses fonoteka_csv_import, fonoteka_csv_match, fonoteka_wishlist_digest because River rejects dotted queue names. STATE.md line 486 still records this as 'pending user confirmation'. Accept an override (kinds, labels, args and delay are unchanged and are what Phase 14 workers and summer_jobs parity consume; the dotted names survive as summer_jobs labels), or ask for a different spelling before any production row carries these queues. D-03 is marked costly to reverse. D-03 is a signed-off, costly-to-reverse contract; only the user can accept a change to it.
test expected why_human
Decide on the 13-01 backstop truth 'a Dispatch of an unregistered kind racing a worker start never routes through the worker client' Accept on the control-flow evidence: modules/conga/conga.go clientFor returns insertOnlyLocked() for every kind not in m.jobs, with no path to m.worker; TestUnregisteredKindWithWorker/WithoutWorker cover both states but no test races a worker start against a Dispatch. Or ask for a race test. The truth is tagged verification: backstop; code reading is not the explicit test evidence a backstop truth requires.
test expected why_human
Read code-review warning WR-04 and decide whether 13-SECURITY-REVIEW.md T-13-03 and T-13-06 should record the residual risk A user who subscribed via wishlist/token/{token}/subscribe keeps peer-album read, reserve and notifications after the owner disables or regenerates the share (classes/wishlist_resolver.go ReservableWishlistIDs). This matches PHP AlbumReservationService::reservableWishlistIds, so parity holds; the security review currently says 'None known'. Either amend the residual-risk cells or open a product decision for revocation (a PHP behaviour change). Product/security judgment; changing behaviour would break the parity rule and needs a decision note.
test expected why_human
Triage code-review warnings WR-01, WR-02, WR-03 (13-REVIEW-DISPOSITION.md: all still 'open') Each is fixed, deferred with a reason, or skipped. None blocks a Phase 13 success criterion: WR-01 is latent (no catch-all under the family prefix today; the app boots and routes dispatch), WR-02 is a mapping/row/cancel-vs-commit race PHP shares (commit-vs-commit CAS holds, TestCsvCommitCAS), WR-03 is a double-click purchase double-notify. WR-02 matters before Phase 14 starts CSV workers. Disposition of review findings is a human decision; the review disposition file records none.
test expected why_human
Push sm-user-plugin master (5 commits ahead of origin/master, including d80d799 RegisterEvent.Payload and 8a65890) to its origin origin/master contains 8a65890, so a fresh clone of fonoteka.go can check out its submodule pointer Pushing a core-plugin repo is a user action; executors were told not to push.

Phase 13: Płytarium API — wishlist, notifications, CSV, credentials, public routes Verification Report

Phase Goal: The remaining core API surface — wishlist, notifications, CSV import/export, per-user/org credentials, and onboarding/public/invitation routes — is ported with byte-compatible shapes and their own public rate-limit buckets. Verified: 2026-10-03T10:02:27Z Status: human_needed Re-verification: No — initial verification

MVP note: ROADMAP marks this phase mode: mvp, but the goal is not a User Story and no plan carries one. As in Phases 1, 3, 5 and 8 to 12, the five ROADMAP success criteria are the contract.

User Flow Coverage

Step (from SC) Expected Evidence in codebase Status
Nuxt user curates, shares, subscribes to, reserves, reveals and purchases wishlist items PHP bodies, bell rows, digest-queue rows, publications 30 wishlist routes (fonoteka.go routes.go 137-179, 283-287); TestFonotekaNuxtFlows/nuxt-wishlist and /mcp-wishlist PASS (verifier run); TestBroadcastGoldens/{wishlist-item-added,reservation-revealed,wishlist-purchased} in the gate VERIFIED
User reads the bell list, unread count, marks one or all read PHP bodies; Winter 404 page for foreign ids routes.go 123-126; notifications_controller.go; recorded 404 fixtures are Winter HTML; TestNotificationsRoutes PASS VERIFIED
User uploads a CSV, polls, remaps, edits rows, commits, cancels; exports on both groups PHP bodies, queued job rows equal to PHP's routes.go 54-67, 294; TestFonotekaNuxtFlows/nuxt-csv (job rows diffed against nuxt-csv.rows.json) PASS VERIFIED
User stores personal/org AI and Discogs credentials Encrypted at rest, PHP bodies, resolver order routes.go 198-204; lagoon.Encrypted model fields with json:"-"; TestCredentialsCRUD, TestCredentialSecretsNeverSerialized, TestResolveAIConfigPrecedence, TestDiscogsSharedMirror PASS VERIFIED
Anonymous visitor opens onboarding, an invitation, a shared collection or wishlist No auth, PHP headers, own throttles and pubfail lockout routes.go 306-334; TestFonotekaNuxtFlows/{onboarding,public-anonymous,public-pubfail} PASS; TestPublicBucketsPerRoute PASS VERIFIED

Goal Achievement

Roadmap Success Criteria (the contract)

# Success criterion Status Evidence
SC1 Wishlist items, subscriptions, public-wishlist/{token} views, reservations (reserve/reveal) and purchase/digest triggers pass the parity diff; match/apply-release are Phase 14; the digest trigger queues its job here ✓ VERIFIED All 28 JWT and 4 token-group wishlist routes are mounted (routes.go 137-179, 283-287) and status: ported with recorded cases (reserve 5 cases incl. 422/409/409/404; reveal 4; purchase 3; token subscribe GET 5). The 3 public-wishlist routes are ported (routes.go 332-334). Purchase enqueues WishlistPurchasedMailArgs in the transaction (TestPurchaseMailAfterCommit, TestPurchaseSideEffects PASS). Item-added runs from albumAddedCallback and upserts wishlist_digest_queue with ON CONFLICT, dispatching WishlistDigestArgs (1800 s) only on first insert (TestDigestCoalescing PASS). The verifier ran the nuxt-wishlist and mcp-wishlist flows: PASS. wishlist/albums/{id}/match and apply-release are pending in the manifest and absent from the router (TestRouteTablePhase13/phase14-routes-absent).
SC2 Notifications list, unread count and mark-read (one and all) pass the parity diff; pruning is a Phase 14 command ✓ VERIFIED 4 routes (routes.go 123-126), all ported, with recorded cases ({id}/read 200, 200, 404, 404; the 404s are the Winter production HTML page). TestNotificationsRoutes PASS (50-row cap, empty list, foreign id). No prune route exists.
SC3 CSV import as a multi-step session (store, show/poll, mapping patch, per-row edit, commit, cancel) and CSV export on both groups pass the parity diff; commit and mapping enqueue jobs whose bodies are Phase 14 ✓ VERIFIED 6 import routes plus export on JWT and token groups (routes.go 54-67, 294), all ported (for example store 9 cases, show 11, rows 11, commit 11). csv_import_service.go dispatches CsvMatchArgs (line 571) and CsvImportArgs (line 860) through conga with their labels. Commit is an UPDATE ... WHERE status='preview' compare-and-swap (TestCsvCommitCAS PASS). jobs.go registers only the invitation and purchase-mail workers, so the CSV jobs stay queued (TestCsvJobRows PASS). The selected_discogs_id branch answers discogs_unavailable through the seam (TestCsvRowPickSeam PASS). The export fixture body (BOM, HEADERS, formula guard, fputcsv quoting) replays green. The verifier ran nuxt-csv (job rows diffed against PHP's golem15_apparatus_jobs dump): PASS.
SC4 Per-user and per-org Discogs/AI credentials CRUD store encrypted values, honor the org-lock flag, and resolve correctly; the /test routes are Phase 14 ✓ VERIFIED The 7 CRUD routes (routes.go 198-204) are ported (POST ai-credential 7 cases, POST discogs-credential 10, POST org-ai-credential 6). Secrets are lagoon.Encrypted with json:"-" (models/*_credential.go), written only via lagoon.NewEncrypted (credential_write_service.go 98, 130, 225). ResolveAIConfig matches PHP AiConfigResolver::resolve tier by tier: admin, then org-lock for org members, then user, then org, else ErrNoAICredential. ResolveDiscogsConfig (gates.go 136) follows admin/env, then user, then org. AIAllowed applies the org-lock on me/context. TestResolveAIConfigPrecedence, TestCredentialsCRUD, TestCredentialSecretsNeverSerialized and TestDiscogsSharedMirror PASS. The /test routes are pending and absent from the router. Info: ResolveAIConfig has no route caller yet. In PHP its only caller is RecognizeApiController (Phase 14), so this matches.
SC5 Onboarding, public and invitation-inspection routes, including public/{token}, .../albums, .../albums/{id}, are reachable without auth and enforce their own public rate-limit buckets ✓ VERIFIED The onboarding group (routes.go 308) and invitation inspection (315) carry only throttle:10,1, with no auth. The public group (325-334) carries public.share-headers and nothing else. The resolve routes carry throttle:10,1; the four albums routes carry throttle:fonoteka-public-token (60/min, pubtok:<token>) and then throttle:fonoteka-public-ip (120/min, client IP), exactly as in routes.php 399-428 and Plugin.php RateLimiter::for. The middleware sets X-Robots-Tag and Cache-Control: no-store, private and rewrites 429 bodies to JSON, as the PHP PublicShareHeaders does. The pubfail:<ip> counter is shared by all 6 routes. TestPublicBucketsPerRoute, TestPublicResolve, TestPubfailCounter (including goroutine concurrency), TestPublicAlbumFieldSet and the flows public-anonymous, public-pubfail (10x 404 then 429) and onboarding PASS in the verifier run.

Score: 5/5 roadmap success criteria verified (0 present, behavior-unverified).

Plan must-haves (supporting)

89 plan truths across 13-01 to 13-06. The gate's --named stage ran each by exact name with skips refused, and -race was on for the fonoteka package. The verifier re-ran 22 of the load-bearing named tests plus the parity corpus and every Nuxt flow:

Group Status Notes
13-01 framework (surf overlap, conga workerless kinds, lagoon prohibited, tide masks, parity scaffolding, planning rewording) 15/17 VERIFIED, 1 deviation, 1 insufficient_spec Deviation: the job-contract truth names dotted queues, but the code uses underscores because River refuses dots. Kinds, labels, args and delay match. Routed to human with an override suggestion. insufficient_spec: the backstop race truth holds by control flow (clientFor has no worker path for unregistered kinds), but no race test exists. Routed to human.
13-02 notifications, credentials, onboarding, invitation inspect, sm-user-plugin Payload and exports 18/18 VERIFIED TestBootstrapConcurrent, TestRegisterInvitationListener and TestRegisterEventPayload pass (gate). The sm-user-plugin diff c258e9f..HEAD is additive: events.go gains only Payload, no exported func is removed, and /register replays in TestUserAPINuxtFlows. The backstop truth on org-less provisioning is covered by credentials_smoke_test.go:186-198.
13-03 wishlist 18/18 VERIFIED The backstop truth on concurrent first-read provisioning is covered by wishlist_smoke_test.go subtest concurrent-first-reads.
13-04 CSV 15/15 VERIFIED The backstop truth on export ordering and header columns is proven by the parity replay of the PHP-recorded export bodies (GET___fonoteka_api_v1_export_csv_jwt*.yaml).
13-05 public 11/11 VERIFIED The backstop truth on concurrent pubfail is covered by the goroutine block in TestPubfailCounter.
13-06 tests, gate, sign-off 10/10 VERIFIED FuzzWriteEndpoints and TestPhase13Threats pass under -race in the gate. Coverage floors are met (gate log). The --removal mutation evidence (RC rows) is the 13-06 executor's run; it is not part of --all, and the verifier did not re-run it because it edits tracked source.

Prohibitions (all verification: test)

Each of the 17 prohibitions has a wired, passing test, so none are flagged:

Prohibition Enforcing evidence
No Phase 13 job worked or faked (D-04), for CSV and digest TestJobContractDispatchWhileWorkerRuns, TestCsvJobRows, TestDigestCoalescing; jobs.go registers no CSV or digest worker
Overlap dispatcher keeps every route in the table TestOverlappingConstrainedRoutes, TestRouteTablePhase13
Framework code does not name the application No new mentions in surf/conga/lagoon/tide or the 4 docs pages. The existing mentions in lagoon/*_test.go and tide/capture_test.go come from Phase 10.2. TestDocsTree PASS
Payload carries no password events.go doc and TestRegisterEventPayload
sm-user-plugin additive only diff review above, plus TestUserAPINuxtFlows
No secret in a response, log, job arg or fixture TestCredentialSecretsNeverSerialized; check_corpus --check-secrets (gate)
/test, match and apply-release routes not mounted TestRouteTablePhase13/phase14-routes-absent; manifest pending
Owner cannot learn the reserver before reveal TestReservationMask, TestPhase13ReservationMask
No mail from a rolled-back purchase TestPurchaseMailAfterCommit
selected_discogs_id never fabricates TestCsvRowPickSeam
Uploaded CSV is not publicly served The bucket is file://./storage/app under the fonoteka-csv/ prefix, and the static uploads root is storage/app/uploads/public (config/storage.yaml). Also covered by TestPhase13Threats T-13-1x
Public view exposes no private fields TestPublicAlbumFieldSet (12-key allow-list)
Disabled, regenerated or wrong-kind token does not resolve TestPublicResolve, TestPhase13Threats/T-13-03, the public-anonymous regenerate step
No threat marked mitigated without a removal-checked test; pending routes never count as passing gate --evidence, corpus_coverage (passing 157 + pending 14 = 171)

Deferred Items

# Item Addressed In Evidence
1 Wishlist match/apply-release, ai-credential/test, discogs-credential/test, CSV selected_discogs_id success Phase 14 SC4, SC5
2 CSV import/match and wishlist digest job bodies Phase 14 SC1, SC2 (JOBS-02, JOBS-03)
3 fonoteka:prune-notifications Phase 14 SC6

Required Artifacts

Artifact Status Details
summercms.go/modules/surf/overlap.go ✓ VERIFIED Contains SetPathValue, wired from router.go compile, and the fonoteka overlap pairs dispatch (TestWishlistOverlapRoutesAssembled)
summercms.go/modules/conga/conga.go ✓ VERIFIED ErrUnregisteredKindQueue, clientFor/knownQueues
fonoteka.go/.../classes/job_contract.go ✓ VERIFIED (queue spelling deviation) Kinds, labels and args are pinned by TestJobContract; queues use underscores
fonoteka.go/.../controllers/api/{notifications,credentials,wishlist_*,csv_*,public_share,onboarding}_controller.go ✓ VERIFIED Substantive (75 to 395 lines each), routed in routes.go, and exercised by the parity replay
fonoteka.go/.../classes/{ai_config_resolver,wishlist_resolver,reservations,wishlist_notifications,csv_import_service,public_share}.go, classes/csv/* ✓ VERIFIED Wired from the handlers. ResolveAIConfig has no Phase 13 caller; it matches PHP, where the only caller is recognize (Phase 14)
fonoteka.go/plugins/golem15/user/classes/events.go ✓ VERIFIED Payload map[string]any; listener wired in plugin.go
fonoteka.go/parity/fixtures/nuxt/{nuxt-wishlist,nuxt-csv,onboarding,public-anonymous,public-pubfail}.yaml, mcp/mcp-wishlist.yaml, *.rows.json ✓ VERIFIED Present, replayed green. Fixtures carry Winter HTML 404 pages and Laravel header shapes, which points to PHP recording
summercms.go/scripts/check-phase13.sh ✓ VERIFIED EXPECTED_PORTED=157, fail-closed detectors; the orchestrator's --all run exited 0
From To Via Status
routes.go surf overlap families 4 PHP wishlist overlap pairs on one router WIRED (TestWishlistOverlapRoutesAssembled PASS)
notification_service.go albumAddedCallback wishlist_notifications.go NotifyWishlistItemAdded on the insert tx WIRED (TestWishlistItemAddedOncePerPath in gate)
wishlist_notifications.go / csv_import_service.go job_contract.go conga Dispatch with labels and queues WIRED
plugin.go sm-user-plugin RegisterEvent HandleRegisterEvent listener WIRED (TestRegisterInvitationListener, onboarding flow)
public_share_controller.go share_service.go ResolvePublic after the pubfail check WIRED
routes.go public albums routes plugin.go Buckets() throttle:fonoteka-public-token, throttle:fonoteka-public-ip WIRED (TestPublicBucketsPerRoute)
credential write service lagoon.NewEncrypted api_key and token WIRED

Behavioral Spot-Checks (verifier's own runs; TMPDIR/GOTMPDIR on the home disk, FORCE_COLOR unset)

Behavior Command Result Status
Parity corpus replays against Go go test -count=1 ./parity -run '^(TestFonotekaNuxtFlows|TestParityCorpus)$' recorded 171/171 passing 157 failing 0 unrecorded 0 pending 14; ok 44.4s ✓ PASS
All 8 Nuxt flows same run, -v nuxt-collections, nuxt-albums, onboarding, nuxt-wishlist, mcp-wishlist, nuxt-csv, public-anonymous, public-pubfail all PASS ✓ PASS
22 load-bearing named tests go test -count=1 ./plugins/golem15/fonoteka -run '^(TestRouteTablePhase13|...|TestPhase13Threats)$' all 22 PASS, ok 20.1s ✓ PASS
Pending routes are only Phase 14 or out of scope manifest parse 14 pending: 4 Phase 13 to 14 (D-01, D-02) plus 10 album/oauth/recognize routes outside this phase ✓ PASS
Full gate orchestrator scripts/check-phase13.sh --all exit 0 (gate-all.log) ✓ PASS (orchestrator evidence)

Probe Execution

Step 7c: no scripts/*/tests/probe-*.sh is declared by the phase. The phase gate scripts/check-phase13.sh --all is the runnable check (orchestrator run, exit 0). --removal was not re-run because it mutates tracked source.

Requirements Coverage

Requirement Source Plan Description Status Evidence
API-03 13-01, 13-03, 13-05, 13-06 Wishlist items, subscriptions, public-wishlist, reservations, purchase and digest triggers ✓ SATISFIED SC1
API-04 13-01, 13-02, 13-06 Notifications list, unread, mark read ✓ SATISFIED SC2
API-05 13-01, 13-04, 13-06 CSV session and export on both groups ✓ SATISFIED SC3
API-06 13-01, 13-02, 13-06 Credentials CRUD, encryption, org-lock, resolution ✓ SATISFIED SC4
API-07 13-01, 13-02, 13-05, 13-06 Onboarding, public, invitation inspection with public buckets ✓ SATISFIED SC5

All 5 IDs appear in plan frontmatter and are marked Complete in REQUIREMENTS.md (lines 78-82, 217-221). No orphaned requirement maps to Phase 13.

Anti-Patterns Found

File Line Pattern Severity Impact
fonoteka.go/.../classes/csv_import_service.go 519-587, 639-705, 889-901 Status check before an unlocked write (WR-02) ⚠️ Warning A mapping, row or cancel write can race a commit and leave a second import job queued. PHP has the same race. It matters once Phase 14 starts the workers
fonoteka.go/.../controllers/api/wishlist_albums_controller.go 366-383 Purchase re-check outside the tx (WR-03) ⚠️ Warning A double submit sends duplicate bell rows and mail jobs
summercms.go/modules/surf/overlap.go 224-288 Method-less family pattern (WR-01) ⚠️ Warning Latent boot failure when a family sits beside a method-specific catch-all; does not affect fonoteka today
13-SECURITY-REVIEW.md T-13-03, T-13-06 rows Residual risk "None known" (WR-04) ⚠️ Warning Under-states the PHP-parity subscriber-retains-access behaviour
classes/job_contract.go 33-46 D-03 queue spelling differs from the signed-off text ℹ️ Info Forced by River; pending user confirmation per STATE.md:486
debt markers none No TBD, FIXME or XXX in Phase 13 files none none

Human Verification Required

1. D-03 queue-name deviation

Test: Compare classes/job_contract.go queues (fonoteka_csv_import, fonoteka_csv_match, fonoteka_wishlist_digest) with D-03's dotted names. Expected: Accept an override, or choose another River-valid spelling, before production rows carry them. Why human: D-03 is a costly, signed-off contract, and STATE.md still lists it as pending user confirmation.

Suggested override if accepted:

overrides:
  - must_have: "the job contract lives in one file, classes/job_contract.go, and TestJobContract pins every string: ... queues fonoteka.csv.import, fonoteka.csv.match, fonoteka.wishlist.digest, mail ..."
    reason: "River rejects dotted queue names; the dotted names remain the summer_jobs labels (PHP parity), kinds/args/delay unchanged; queues spelled with underscores"
    accepted_by: "<name>"
    accepted_at: "<ISO timestamp>"

2. Backstop truth: unregistered-kind dispatch racing a worker start

Test: Read modules/conga/conga.go clientFor. Expected: Every unregistered kind reaches insertOnlyLocked(), and no branch returns m.worker. Accept, or ask for a race test. Why human: A backstop truth requires explicit test evidence; this one only has control-flow evidence.

3. WR-04 residual risk

Test: Read 13-REVIEW.md WR-04 next to 13-SECURITY-REVIEW.md T-13-03 and T-13-06. Expected: Amend the residual-risk cells, or record a product decision on revoking token subscribers. Why human: This is a security and product judgment, and any change departs from PHP parity.

4. Review findings disposition

Test: Set a disposition for WR-01, WR-02 and WR-03 (and the IN items) in 13-REVIEW-DISPOSITION.md. Expected: Each is fixed, deferred with a reason, or skipped. WR-02 should be resolved before the Phase 14 CSV workers. Why human: Triage decision.

5. Push sm-user-plugin

Test: git -C fonoteka.go/plugins/golem15/user push (5 commits ahead of origin/master). Expected: origin contains 8a65890. Why human: Pushing a core-plugin repo is a user action.

Gaps Summary

No blocking gaps. All five roadmap success criteria hold in the code and in the verifier's own runs:

  • 58 Phase 13 routes are mounted and ported.
  • The parity corpus passes 157 of 171 recorded routes with 0 failing; the 14 pending are the 4 deliberate Phase 14 routes plus 10 routes outside this phase.
  • Every Phase 13 Nuxt and MCP flow replays green.
  • Credentials are encrypted, and the resolvers follow PHP tier by tier.
  • The public surface carries PHP's exact throttle buckets, headers and pubfail lockout.

The four review warnings do not defeat a success criterion:

  • WR-01 is latent.
  • WR-02 and WR-03 are concurrency edges outside the recorded contract; WR-02 is shared with PHP.
  • WR-04 matches PHP, so the issue is a documentation gap.

Status is human_needed for five reasons: the signed-off D-03 queue spelling still needs the user's confirmation, one backstop truth has only control-flow evidence, the WR-04 residual risk needs a decision, the review findings have no disposition, and the sm-user-plugin commits are unpushed.


Verified: 2026-10-03T10:02:27Z Verifier: Claude (gsd-verifier)