docs(14.1): revise plans from checker

This commit is contained in:
Jakub Zych
2026-10-05 19:40:40 +02:00
parent 3bd461ec68
commit 57fdaa0d85
2 changed files with 13 additions and 7 deletions

View File

@@ -26,7 +26,7 @@ estimate:
tokens: 320000
raw_tokens: 320000
tasks: 4
confidence: low
confidence: low # uncalibrated (sample_count 0); locked 2-plan lean split — do not split
must_haves:
truths:
- "Per D-02, a JWT caller can GET `/_fonoteka/api/v1/oauth-identities` and receive `{\"data\":[]}` when they have no rows, and `{\"data\":[{\"provider\",\"linked_at\"},...]}` ordered by provider when they have rows, with `linked_at` as Carbon `+00:00` or JSON null (HTTP-06)."
@@ -150,7 +150,7 @@ Repos: fonoteka.go and the sm-user-plugin submodule at `plugins/golem15/user`. D
- MeToken D-09: `collection_ids` JSON null when `!Valid` or empty.
- Parity extras `oauth-identities-linked` and `me-unrestricted`; new recorded cases; three routes `ported`; `expectedPortedRoutes = 175`; rewritten `assertPortedMismatch`.
- sm-user-plugin README: table row, exported-constructor subsection (host-chosen path; no consuming-application name), models list `OAuthIdentity`.
- Smoke: `TestOAuthIdentitiesIndexEmptyList` (plan 02 expands coverage).
- Smoke: `TestOAuthIdentitiesIndexEmptyList` and `TestOAuthIdentitiesDestroyNoContentKeepsSibling` (plan 02 expands the full D-11 matrix).
## Assumptions
@@ -159,6 +159,7 @@ Repos: fonoteka.go and the sm-user-plugin submodule at `plugins/golem15/user`. D
- Unauthenticated unknown provider is 401 in Go (`jwt.auth` first); D-11 401 tests use `/google` (research A1). Not a recorded parity case.
- `profile_data` is SQL `jsonb` per D-07 even though `Jsonable.GormDataType` is `text` (research A2).
- No new Go modules. Discretion: constructors + host mount, not a `Mount` helper (that helper would import fonoteka's `api` package into the user plugin).
- Token estimates are uncalibrated (sample_count 0, confidence low) under the locked 2-plan lean split; do not split this phase.
<tasks>
@@ -229,7 +230,7 @@ Repos: fonoteka.go and the sm-user-plugin submodule at `plugins/golem15/user`. D
<task type="auto">
<name>Task 3: Unlink one identity — 204, Winter HTML 404, 409 last-method, throttle:10,1</name>
<files>../fonoteka.go/plugins/golem15/user/controllers/oauth_identities.go, ../fonoteka.go/plugins/golem15/user/lang/en/lang.yaml, ../fonoteka.go/plugins/golem15/user/lang/pl/lang.yaml, ../fonoteka.go/plugins/golem15/fonoteka/routes.go, ../fonoteka.go/plugins/golem15/fonoteka/phase08_coverage_test.go</files>
<files>../fonoteka.go/plugins/golem15/user/controllers/oauth_identities.go, ../fonoteka.go/plugins/golem15/user/oauth_identities_test.go, ../fonoteka.go/plugins/golem15/user/lang/en/lang.yaml, ../fonoteka.go/plugins/golem15/user/lang/pl/lang.yaml, ../fonoteka.go/plugins/golem15/fonoteka/routes.go, ../fonoteka.go/plugins/golem15/fonoteka/phase08_coverage_test.go</files>
<read_first>../fonoteka.go/plugins/golem15/user/controllers/oauth_identities.go (Task 2 Index), ../fonoteka.go/plugins/golem15/user/controllers/api_tokens.go (owner-scoped Destroy First), ../fonoteka.go/plugins/golem15/user/controllers/api_controller.go (writeJSON, appTranslator, accountMessage phrasebook Get), ../fonoteka.go/plugins/golem15/user/lang/en/lang.yaml, ../fonoteka.go/plugins/golem15/user/lang/pl/lang.yaml, ../fonoteka.go/plugins/golem15/fonoteka/controllers/api/http_errors.go (WriteWinterHTTPError), ../fonoteka.go/plugins/golem15/fonoteka/routes.go (JWT group, throttle:10,1 on CSV/switch, request_id Where loosening ~238-254), ../fonoteka.go/plugins/golem15/fonoteka/phase08_coverage_test.go (assertRouteSurfaces), /media/nvme/dev/golem15/fonoteka/plugins/golem15/fonoteka/controllers/api/OAuthIdentityApiController.php (destroy), .planning/phases/14.1-oauth-identities-and-fonoteka-me-routes/14.1-RESEARCH.md (Patterns 3-4, Pitfalls 1-2)</read_first>
<action>Per D-02, D-03, D-04, D-06, HTTP-01, HTTP-04, I18N-01.
@@ -240,10 +241,12 @@ Repos: fonoteka.go and the sm-user-plugin submodule at `plugins/golem15/user`. D
(3) JWT group: `g.Delete("/oauth-identities/{provider}", userctrl.OAuthIdentitiesDestroy(p.app, userctrl.OAuthIdentitiesOptions{WriteNotFound: func(w http.ResponseWriter, r *http.Request) { api.WriteWinterHTTPError(w, p.app, http.StatusNotFound) }}), "throttle:10,1")`. Leave the path value unconstrained so the handler 404 is reachable (Pitfall 1; oauth request_id precedent). Personal-token group unchanged.
(4) phase08: `assertRouteSurfaces(t, rt, http.MethodDelete, "/oauth-identities/{provider}", true, false)` in the same oauth-identity subtest. Assert the DELETE route's middleware list contains `throttle:10,1` (CSV import is the analog).
(5) Smoke `TestOAuthIdentitiesDestroyNoContentKeepsSibling` in plugin-root `oauth_identities_test.go` (package `user`): `sessionApp`, `insertUser`, insert two `OAuthIdentity` rows for that user (`facebook` and `google`), `bouncer.WithUser`, `OAuthIdentitiesDestroy(app, OAuthIdentitiesOptions{WriteNotFound: stub that fatals if called})` on DELETE `/_fonoteka/api/v1/oauth-identities/facebook`. Assert `http.StatusNoContent` (204), empty body, and the google row still exists for that `user_id`. Status 200 with a JSON body fails this test. Full D-11 matrix (Winter HTML 404, 409 last-method, 401) stays in plan 02.
</action>
<verify>
<automated>go -C ../fonoteka.go vet ./plugins/golem15/user/... ./plugins/golem15/fonoteka/... &amp;&amp; go -C ../fonoteka.go test ./plugins/golem15/fonoteka -count=1 -run 'test_oauth_identity_routes_exist_on_jwt_surface_only'</automated>
<fails_when>Non-zero exit; the fonoteka run prints "--- FAIL" or "no tests to run" for the oauth-identity surface subtest.</fails_when>
<automated>go -C ../fonoteka.go vet ./plugins/golem15/user/... ./plugins/golem15/fonoteka/... &amp;&amp; go -C ../fonoteka.go test ./plugins/golem15/user -count=1 -v -run '^(TestOAuthIdentitiesDestroyNoContentKeepsSibling)$' &amp;&amp; go -C ../fonoteka.go test ./plugins/golem15/fonoteka -count=1 -run 'test_oauth_identity_routes_exist_on_jwt_surface_only'</automated>
<fails_when>Non-zero exit; the verbose user-plugin run prints "--- FAIL", "no tests to run" or "--- SKIP", or lacks "--- PASS: TestOAuthIdentitiesDestroyNoContentKeepsSibling"; status 200 JSON would fail that test; the fonoteka run fails the oauth-identity surface subtest.</fails_when>
</verify>
<acceptance_criteria>
- `grep -c 'func OAuthIdentitiesDestroy(' ../fonoteka.go/plugins/golem15/user/controllers/oauth_identities.go` prints at least 1.
@@ -254,6 +257,8 @@ Repos: fonoteka.go and the sm-user-plugin submodule at `plugins/golem15/user`. D
- `grep -c 'OAuthIdentitiesDestroy' ../fonoteka.go/plugins/golem15/fonoteka/routes.go` prints at least 1.
- `grep -c 'WriteWinterHTTPError' ../fonoteka.go/plugins/golem15/fonoteka/routes.go` prints at least 1.
- `git -C ../fonoteka.go/plugins/golem15/user diff -- routes.go` prints nothing.
- `grep -c 'TestOAuthIdentitiesDestroyNoContentKeepsSibling' ../fonoteka.go/plugins/golem15/user/oauth_identities_test.go` prints at least 1.
- `grep -c 'StatusNoContent' ../fonoteka.go/plugins/golem15/user/oauth_identities_test.go` prints at least 1.
</acceptance_criteria>
<done>A JWT caller can unlink a non-last identity (204), is blocked on the last one (409 localized error), and sees Winter HTML 404 for unknown/missing/foreign providers, with DELETE rate-limited 10/1.</done>
</task>

View File

@@ -16,7 +16,7 @@ estimate:
tokens: 280000
raw_tokens: 280000
tasks: 3
confidence: low
confidence: low # uncalibrated (sample_count 0); locked 2-plan lean split — do not split
must_haves:
truths:
- "Per D-11, Go tests ported from OAuthIdentityApiTest.php prove unlink 204 keeps the other row, last-method 409 EN and PL texts match the user-plugin lang strings, missing/foreign/unknown-provider Winter HTML 404 bodies are byte-identical, and both identity routes return 401 without a JWT on `/google`."
@@ -120,7 +120,8 @@ Repos: fonoteka.go / sm-user-plugin. Never add co-author tags.
## Assumptions
- Assumption-delta remains closed: D-06/D-13 already answered second-method lockout; tests prove count-only 409, they do not add social login.
- Plan 01 flipped GET (and DELETE) surfaces; this plan asserts them fail-closed and adds throttle contains-check if Task 3 of 01 left a gap.
- Plan 01 flipped GET (and DELETE) surfaces and shipped `TestOAuthIdentitiesDestroyNoContentKeepsSibling` (204 + sibling remains); this plan keeps the full D-11 matrix and adds throttle contains-check if Task 3 of 01 left a gap.
- Token estimates are uncalibrated (sample_count 0, confidence low) under the locked 2-plan lean split; do not split this phase.
- Do not retarget `scripts/check-phase14.sh` (`EXPECTED_PENDING=3` is a Phase 14 artifact).
<tasks>