diff --git a/.planning/phases/14.1-oauth-identities-and-fonoteka-me-routes/14.1-01-PLAN.md b/.planning/phases/14.1-oauth-identities-and-fonoteka-me-routes/14.1-01-PLAN.md index a771c06..12becd2 100644 --- a/.planning/phases/14.1-oauth-identities-and-fonoteka-me-routes/14.1-01-PLAN.md +++ b/.planning/phases/14.1-oauth-identities-and-fonoteka-me-routes/14.1-01-PLAN.md @@ -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. @@ -229,7 +230,7 @@ Repos: fonoteka.go and the sm-user-plugin submodule at `plugins/golem15/user`. D Task 3: Unlink one identity — 204, Winter HTML 404, 409 last-method, throttle:10,1 - ../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 + ../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 ../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) 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. - go -C ../fonoteka.go vet ./plugins/golem15/user/... ./plugins/golem15/fonoteka/... && go -C ../fonoteka.go test ./plugins/golem15/fonoteka -count=1 -run 'test_oauth_identity_routes_exist_on_jwt_surface_only' - Non-zero exit; the fonoteka run prints "--- FAIL" or "no tests to run" for the oauth-identity surface subtest. + go -C ../fonoteka.go vet ./plugins/golem15/user/... ./plugins/golem15/fonoteka/... && go -C ../fonoteka.go test ./plugins/golem15/user -count=1 -v -run '^(TestOAuthIdentitiesDestroyNoContentKeepsSibling)$' && go -C ../fonoteka.go test ./plugins/golem15/fonoteka -count=1 -run 'test_oauth_identity_routes_exist_on_jwt_surface_only' + 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. - `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. 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. diff --git a/.planning/phases/14.1-oauth-identities-and-fonoteka-me-routes/14.1-02-PLAN.md b/.planning/phases/14.1-oauth-identities-and-fonoteka-me-routes/14.1-02-PLAN.md index 988bd4c..ac85272 100644 --- a/.planning/phases/14.1-oauth-identities-and-fonoteka-me-routes/14.1-02-PLAN.md +++ b/.planning/phases/14.1-oauth-identities-and-fonoteka-me-routes/14.1-02-PLAN.md @@ -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).