docs(14.1): revise plans from checker
This commit is contained in:
@@ -26,7 +26,7 @@ estimate:
|
|||||||
tokens: 320000
|
tokens: 320000
|
||||||
raw_tokens: 320000
|
raw_tokens: 320000
|
||||||
tasks: 4
|
tasks: 4
|
||||||
confidence: low
|
confidence: low # uncalibrated (sample_count 0); locked 2-plan lean split — do not split
|
||||||
must_haves:
|
must_haves:
|
||||||
truths:
|
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)."
|
- "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.
|
- 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`.
|
- 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`.
|
- 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
|
## 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.
|
- 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).
|
- `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).
|
- 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>
|
<tasks>
|
||||||
|
|
||||||
@@ -229,7 +230,7 @@ Repos: fonoteka.go and the sm-user-plugin submodule at `plugins/golem15/user`. D
|
|||||||
|
|
||||||
<task type="auto">
|
<task type="auto">
|
||||||
<name>Task 3: Unlink one identity — 204, Winter HTML 404, 409 last-method, throttle:10,1</name>
|
<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>
|
<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.
|
<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.
|
(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).
|
(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>
|
</action>
|
||||||
<verify>
|
<verify>
|
||||||
<automated>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'</automated>
|
<automated>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'</automated>
|
||||||
<fails_when>Non-zero exit; the fonoteka run prints "--- FAIL" or "no tests to run" for the oauth-identity surface subtest.</fails_when>
|
<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>
|
</verify>
|
||||||
<acceptance_criteria>
|
<acceptance_criteria>
|
||||||
- `grep -c 'func OAuthIdentitiesDestroy(' ../fonoteka.go/plugins/golem15/user/controllers/oauth_identities.go` prints at least 1.
|
- `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 '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.
|
- `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.
|
- `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>
|
</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>
|
<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>
|
</task>
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ estimate:
|
|||||||
tokens: 280000
|
tokens: 280000
|
||||||
raw_tokens: 280000
|
raw_tokens: 280000
|
||||||
tasks: 3
|
tasks: 3
|
||||||
confidence: low
|
confidence: low # uncalibrated (sample_count 0); locked 2-plan lean split — do not split
|
||||||
must_haves:
|
must_haves:
|
||||||
truths:
|
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`."
|
- "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
|
## 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.
|
- 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).
|
- Do not retarget `scripts/check-phase14.sh` (`EXPECTED_PENDING=3` is a Phase 14 artifact).
|
||||||
|
|
||||||
<tasks>
|
<tasks>
|
||||||
|
|||||||
Reference in New Issue
Block a user