fix(08): revise plans based on checker feedback
This commit is contained in:
@@ -5,49 +5,39 @@ type: execute
|
||||
wave: 5
|
||||
depends_on: [08-04]
|
||||
files_modified:
|
||||
- ../fonoteka.go/parity/oauth_flow_test.go
|
||||
- ../fonoteka.go/parity/capture_clients.mjs
|
||||
- ../fonoteka.go/parity/capture-rules.yaml
|
||||
- ../fonoteka.go/parity/fixtures/mcp/mcp-lifecycle.yaml
|
||||
- ../fonoteka.go/parity/manifest.yaml
|
||||
- ../fonoteka.go/parity/check_corpus.go
|
||||
- scripts/check-phase8.sh
|
||||
- ../fonoteka.go/plugins/golem15/fonoteka/controllers/api/oauth_consent_controller.go
|
||||
- ../fonoteka.go/plugins/golem15/fonoteka/controllers/api/oauth_consent_controller_test.go
|
||||
- ../fonoteka.go/plugins/golem15/fonoteka/plugin.go
|
||||
- ../fonoteka.go/plugins/golem15/fonoteka/routes.go
|
||||
- ../fonoteka.go/plugins/golem15/fonoteka/oauth_connect_test.go
|
||||
- scripts/check-phase8-ui.mjs
|
||||
autonomous: true
|
||||
requirements: [AUTH-05, AUTH-06, AUTH-07]
|
||||
must_haves:
|
||||
truths:
|
||||
- "All four raw OAuth routes and five JWT OAuth management routes replay their recorded PHP contracts against Go and count as ported only after passing."
|
||||
- "A clean recorded lifecycle proves DCR, authorize, consent, token, refresh, replay kill, list, revoke, post-revoke failure, deny, confidential Basic auth, and scope ceiling."
|
||||
- "The unchanged real fonoteka-mcp process completes discovery, DCR, PKCE, JWT consent, token bootstrap, an MCP tool call, and refresh against the Go backend."
|
||||
- "D-08: JWT consent returns exact unchanged-Nuxt payloads and derives scopes and collection ids server-side."
|
||||
- "D-09: Authorize/token remain raw while consent routes remain in the JWT group."
|
||||
- "Invalid handles make no request, login uses the closed return-path allow-list, and English/Polish consent copy resolves."
|
||||
- "Consent state, keyboard/focus, 44px target, and mobile stacking matrices are proven without changing Nuxt source."
|
||||
artifacts:
|
||||
- path: "../fonoteka.go/parity/oauth_flow_test.go"
|
||||
provides: "Projected existing flows and full lifecycle replay"
|
||||
- path: "../fonoteka.go/parity/fixtures/mcp/mcp-lifecycle.yaml"
|
||||
provides: "Secret-scrubbed PHP lifecycle source-of-truth fixture"
|
||||
- path: "scripts/check-phase8.sh"
|
||||
provides: "Two-repository, parity, secret, Postgres, real-MCP phase gate"
|
||||
- path: "../fonoteka.go/plugins/golem15/fonoteka/controllers/api/oauth_consent_controller.go"
|
||||
provides: "Owner-bound consent show/allow/deny API"
|
||||
- path: "scripts/check-phase8-ui.mjs"
|
||||
provides: "Read-only browser harness for the approved UI-SPEC"
|
||||
key_links:
|
||||
- from: "oauth_flow_test.go"
|
||||
to: "newConfiguredTarget"
|
||||
via: "replay through the assembled Go app and real Postgres"
|
||||
pattern: "newConfiguredTarget"
|
||||
- from: "scripts/check-phase8.sh"
|
||||
to: "/media/nvme/dev/golem15/fonoteka/fonoteka-mcp"
|
||||
via: "three configured URLs and scripted MCP SDK lifecycle"
|
||||
pattern: "FONOTEKA_(API_URL|MCP_PUBLIC_URL|MCP_AUTH_SERVER)"
|
||||
- from: "scripts/check-phase8-ui.mjs"
|
||||
to: "/media/nvme/dev/golem15/fonoteka/vue-fonoteka-app"
|
||||
via: "existing Playwright dependency, mocked backend responses, and no source writes"
|
||||
pattern: "playwright"
|
||||
---
|
||||
|
||||
<objective>
|
||||
Prove the completed server through recorded PHP parity and the unchanged real MCP client rather than only implementation-local tests.
|
||||
Wire browser consent and prove the complete approved UI contract against the unchanged Nuxt checkout.
|
||||
|
||||
Purpose: Turn exact route bytes, lifecycle security semantics, protected-resource discovery ownership, and actual SDK compatibility into one repeatable acceptance gate.
|
||||
Output: Lifecycle fixture/capture policy, projected replay tests, nine ported manifest entries, and `scripts/check-phase8.sh`.
|
||||
Purpose: Separate browser-facing API/state compatibility from protocol internals and make preservation objectively executable.
|
||||
Output: JWT consent controllers/routes, assembled flow tests, and an external read-only browser/UI gate.
|
||||
</objective>
|
||||
|
||||
## Phase Goal
|
||||
|
||||
**As an** unchanged MCP client, **I want to** complete the full OAuth lifecycle against Go exactly as I did against PHP, **so that** discovery, tool use, refresh, replay defense, and revocation are proven together.
|
||||
|
||||
<execution_context>
|
||||
@/home/jin/.codex/get-shit-done/workflows/execute-plan.md
|
||||
@/home/jin/.codex/get-shit-done/templates/summary.md
|
||||
@@ -58,114 +48,57 @@ Output: Lifecycle fixture/capture policy, projected replay tests, nine ported ma
|
||||
@.planning/ROADMAP.md
|
||||
@.planning/STATE.md
|
||||
@.planning/phases/08-oauth2-1-authorization-server/08-CONTEXT.md
|
||||
@.planning/phases/08-oauth2-1-authorization-server/08-RESEARCH.md
|
||||
@.planning/phases/08-oauth2-1-authorization-server/08-UI-SPEC.md
|
||||
@.planning/phases/08-oauth2-1-authorization-server/08-VALIDATION.md
|
||||
@.planning/phases/08-oauth2-1-authorization-server/08-04-SUMMARY.md
|
||||
|
||||
<interfaces>
|
||||
Existing parity seams:
|
||||
- `newConfiguredTarget(t, db)` boots the same assembled handler used by the app.
|
||||
- `tide.LoadFlow`, `tide.OpenStore`, and `tide.ReplayFlow` execute captured request/response sequences with private variables.
|
||||
- `parity/manifest.yaml` status becomes `ported` only when the selected replay subtest passes.
|
||||
|
||||
Unchanged MCP inputs:
|
||||
- `FONOTEKA_API_URL` points to the Go app personal-token API.
|
||||
- `FONOTEKA_MCP_PUBLIC_URL` points to the MCP resource server.
|
||||
- `FONOTEKA_MCP_AUTH_SERVER` points to the Go authorization server.
|
||||
</interfaces>
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto" tdd="true">
|
||||
<name>Task 1: Specify recorded and real-client OAuth acceptance before changing fixtures</name>
|
||||
<files>../fonoteka.go/parity/oauth_flow_test.go, scripts/check-phase8.sh</files>
|
||||
<read_first>
|
||||
.planning/phases/08-oauth2-1-authorization-server/08-CONTEXT.md
|
||||
.planning/phases/08-oauth2-1-authorization-server/08-VALIDATION.md
|
||||
../fonoteka.go/parity/nuxt_flow_test.go
|
||||
../fonoteka.go/parity/parity_test.go
|
||||
../fonoteka.go/parity/fixtures/mcp/mcp-oauth.yaml
|
||||
../fonoteka.go/parity/fixtures/mcp/mcp-tools.yaml
|
||||
../fonoteka.go/parity/manifest.yaml
|
||||
../fonoteka.go/parity/capture_clients.mjs
|
||||
scripts/check-phase3.sh
|
||||
/media/nvme/dev/golem15/fonoteka/fonoteka-mcp/src/http.ts
|
||||
/media/nvme/dev/golem15/fonoteka/fonoteka-mcp/src/config.ts
|
||||
</read_first>
|
||||
<name>Task 1: Specify assembled consent and route behavior in executable RED</name>
|
||||
<files>../fonoteka.go/plugins/golem15/fonoteka/controllers/api/oauth_consent_controller_test.go, ../fonoteka.go/plugins/golem15/fonoteka/oauth_connect_test.go</files>
|
||||
<behavior>
|
||||
- Existing broad flows are projected to named OAuth/MCP prerequisite steps and fail if an expected step disappears; unrelated later-phase calls are not replayed.
|
||||
- The clean lifecycle flow is required and every terminal security action is asserted before routes can be marked ported.
|
||||
- The gate starts real Postgres, Go app, and unchanged Node MCP; it verifies MCP-owned protected-resource metadata and Bearer hint separately from backend-owned metadata and Basic invalid-client challenge.
|
||||
- Show/allow/deny cover exact 200/404/422 payloads, host-only redirect, canonical scopes, active collection, ownership, and ordered redirects.
|
||||
- Tests compile and fail only through `PHASE8_RED:consent`.
|
||||
</behavior>
|
||||
<action>Per D-12, D-13, D-14, D-15, D-16, D-18, and the MVP test-first rule, create failing parity tests and the fail-closed gate skeleton before recording/changing status. Project `mcp-oauth` and `mcp-tools` by stable named step IDs and require the exact OAuth prerequisites plus `/me` and one tool call. Require full `mcp-lifecycle`. In the gate, declare stages for both repo vet/test/race, real-Postgres app boot, real MCP startup with all three environment variables, resource-server discovery/401 challenge, backend metadata/DCR/PKCE/login/consent/token, `/me`, MCP tool call, refresh/replay/revoke, and corpus/secret checks. Plan 08-06 adds the security-review validation stage after its review artifact exists. Do not edit or patch the MCP checkout.</action>
|
||||
<action>D-18: add controller and assembled PKCE flow tests against 08-04 interfaces. Use `PHASE8_RED:consent` only for absent controller/route behavior and reject syntax/setup/missing-test failures via the RED verifier. Cover T-08-CROSS-USER, SCOPE-CEILING, REQUEST-LEAK, and SURFACE, including duplicate action single-use.</action>
|
||||
<verify>
|
||||
<automated>test -f ../fonoteka.go/parity/oauth_flow_test.go && test -x scripts/check-phase8.sh</automated>
|
||||
<automated>scripts/check-phase8-red.sh consent bash -lc "cd ../fonoteka.go && go test ./plugins/golem15/fonoteka/... -run 'TestOAuth(Authorize|Consent|Deny|CodeExchange|Surface)' -count=1"</automated>
|
||||
</verify>
|
||||
<acceptance_criteria>
|
||||
- `oauth_flow_test.go` names projections for `mcp-oauth`, `mcp-tools`, and the complete `mcp-lifecycle`, and missing expected steps fail.
|
||||
- `scripts/check-phase8.sh` uses `set -euo pipefail`, fail-closed dependency/Docker checks, cleanup traps, and all three MCP environment variables.
|
||||
- The gate distinguishes MCP RFC 9728 metadata/rich Bearer challenge from backend exact Basic invalid-client challenge and unchanged token-surface 401.
|
||||
- Tests/gate fail because the lifecycle fixture/status/evidence is not yet complete, not because of shell or Go syntax errors.
|
||||
</acceptance_criteria>
|
||||
<done>The acceptance harness demands the exact recorded and real-client lifecycle before any route can be claimed ported.</done>
|
||||
<done>Consent tests compile, execute, and fail only on the intended missing behavior.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto" tdd="true">
|
||||
<name>Task 2: Record, scrub, replay, and promote the complete OAuth lifecycle</name>
|
||||
<files>../fonoteka.go/parity/capture_clients.mjs, ../fonoteka.go/parity/capture-rules.yaml, ../fonoteka.go/parity/fixtures/mcp/mcp-lifecycle.yaml, ../fonoteka.go/parity/oauth_flow_test.go, ../fonoteka.go/parity/manifest.yaml, ../fonoteka.go/parity/check_corpus.go</files>
|
||||
<read_first>
|
||||
../fonoteka.go/parity/oauth_flow_test.go
|
||||
../fonoteka.go/parity/capture_clients.mjs
|
||||
../fonoteka.go/parity/capture-rules.yaml
|
||||
../fonoteka.go/parity/php_parity.sh
|
||||
../fonoteka.go/parity/fixtures/mcp/mcp-oauth.yaml
|
||||
../fonoteka.go/parity/fixtures/mcp/mcp-tools.yaml
|
||||
../fonoteka.go/parity/manifest.yaml
|
||||
tide/flow.go
|
||||
tide/replay.go
|
||||
</read_first>
|
||||
<name>Task 2: Implement owner-bound JWT consent and raw route wiring</name>
|
||||
<files>../fonoteka.go/plugins/golem15/fonoteka/controllers/api/oauth_consent_controller.go, ../fonoteka.go/plugins/golem15/fonoteka/controllers/api/oauth_consent_controller_test.go, ../fonoteka.go/plugins/golem15/fonoteka/plugin.go, ../fonoteka.go/plugins/golem15/fonoteka/routes.go, ../fonoteka.go/plugins/golem15/fonoteka/oauth_connect_test.go</files>
|
||||
<behavior>
|
||||
- Lifecycle records DCR → authorize → consent → token → refresh → spent-token replay → list → revoke → refresh failure → deny, plus confidential Basic and ceiling/invalid-scope cases.
|
||||
- Every request id, code, verifier, client secret, access token, and refresh token is represented only by a typed placeholder in committed fixtures; private vars are mode 0600.
|
||||
- Nine manifest entries become ported only after their exact route replay passes; pending never increments passing.
|
||||
- Show returns sanitized client, host-only redirect, ordered mintable scopes, active collection name, and ISO expiry.
|
||||
- Allow grants submitted ∩ requested ∩ ceiling ∩ mintable; deny consumes pending state; both return nonblank ordered redirect_to.
|
||||
</behavior>
|
||||
<action>Extend the existing capture script/rules and use the Phase 2 isolated-PHP process to record D-16's lifecycle. Issue the confidential client through `fonoteka:oauth-client`; exercise scope ceiling truncation and invalid-scope redirect with `client_secret_basic`. Capture all secret-bearing values with explicit pkce/token/credential categories into the private store, confirm both vars files are 0600, and commit only symbolic variable references. Add full and projected replays through `newConfiguredTarget` with real Postgres. After each of the four raw and five JWT route subtests passes, change only those manifest entries to `status: ported`; keep honest corpus accounting.</action>
|
||||
<action>D-08: implement show/allow/deny in the JWT+locale+must-change-password group using `bouncer.User`, `ResolveActiveCollection`, `lagoon.Validate`, and wristband operations; never accept collection IDs or extra scopes. Collapse missing/stale/used/foreign handles to exact 404 and empty/no-longer-grantable intersection to exact 422. D-09: mount authorize/token in raw routes and only token receives its throttle. D-10 and D-12: add no oauth guard, house middleware, backend Bearer challenge, or RFC 9728 document.</action>
|
||||
<verify>
|
||||
<automated>cd ../fonoteka.go && go test ./parity -run 'TestOAuthFlows|TestParityCorpus|TestParityContract' -count=1</automated>
|
||||
<automated>cd ../fonoteka.go && go test ./plugins/golem15/fonoteka/... -run 'TestOAuth(Authorize|Consent|Deny|CodeExchange|Surface)' -count=1</automated>
|
||||
</verify>
|
||||
<acceptance_criteria>
|
||||
- `mcp-lifecycle.yaml` contains the locked sequence and placeholder references, not recoverable credential values.
|
||||
- `go run ./parity/check_corpus.go --manifest parity/manifest.yaml --fixtures parity/fixtures --check-secrets` exits 0.
|
||||
- All nine OAuth manifest entries are `ported`; replay reports them passing with zero failing and does not count any pending route as passing.
|
||||
- Existing `mcp-oauth`/`mcp-tools` projections fail if a required named step is removed and ignore only explicitly enumerated later-phase steps.
|
||||
</acceptance_criteria>
|
||||
<done>The Go app passes the PHP-recorded OAuth route and lifecycle contracts without committing live secrets.</done>
|
||||
<done>The unchanged consent client can inspect, allow, or deny one owner-bound request and complete exact PKCE code exchange.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 3: Complete the real unchanged-MCP phase gate</name>
|
||||
<files>scripts/check-phase8.sh</files>
|
||||
<name>Task 3: Prove the approved consent and connected-app UI contract read-only</name>
|
||||
<files>scripts/check-phase8-ui.mjs</files>
|
||||
<read_first>
|
||||
scripts/check-phase8.sh
|
||||
scripts/check-phase3.sh
|
||||
../fonoteka.go/parity/oauth_flow_test.go
|
||||
/media/nvme/dev/golem15/fonoteka/fonoteka-mcp/package.json
|
||||
/media/nvme/dev/golem15/fonoteka/fonoteka-mcp/src/http.ts
|
||||
/media/nvme/dev/golem15/fonoteka/fonoteka-mcp/src/config.ts
|
||||
/media/nvme/dev/golem15/fonoteka/fonoteka-mcp/src/install.ts
|
||||
.planning/phases/08-oauth2-1-authorization-server/08-UI-SPEC.md
|
||||
/media/nvme/dev/golem15/fonoteka/vue-fonoteka-app/package.json
|
||||
/media/nvme/dev/golem15/fonoteka/vue-fonoteka-app/app/pages/connect.vue
|
||||
/media/nvme/dev/golem15/fonoteka/vue-fonoteka-app/app/components/fonoteka/ConsentScopePicker.vue
|
||||
/media/nvme/dev/golem15/fonoteka/vue-fonoteka-app/app/components/fonoteka/ConnectedAppsManager.vue
|
||||
/media/nvme/dev/golem15/fonoteka/vue-fonoteka-app/app/stores/fonoteka.ts
|
||||
</read_first>
|
||||
<action>Finish D-14's executable gate using the existing gate family and installed Node MCP dependencies. Allocate loopback ports, start disposable Postgres and the assembled Go app, start the unchanged MCP with its three URLs pointed at the test services, and drive the actual SDK discovery/DCR/PKCE flow. Obtain a JWT only through the app's real login route, consent through the JWT API, exchange, call an MCP tool after `/me` bootstrap, refresh, replay/revoke, and assert failures. Verify MCP emits the rich Bearer `resource_metadata` challenge and protected-resource document; verify the backend emits only exact Basic on token invalid-client and unchanged no-challenge token-surface 401. Run both modules' vet/test/race, parity/corpus/secret checks, and require a verified security-review artifact stage to be satisfiable by 08-06. Preserve cleanup on success, error, and interruption; never print secrets.</action>
|
||||
<action>Create a read-only harness outside the Nuxt checkout using its already-installed Playwright runtime. First run `pnpm verify:oauth-return-path` and `pnpm verify:oauth-i18n`. Then boot the unchanged app and intercept API responses to prove: invalid/missing/repeated-first-invalid handles send no oauth/request call; logged-out entry preserves only a validated localized return path; 200, 404, network/error, empty-scope, allow-pending, deny-pending, and one-redirect outcomes render correctly; connected-app error/empty/manual-count/populated/cancel/revoke-pending/success/failure/identical-404 states render correctly. Assert keyboard order, visible 2px focus, dialog trap/Escape/restore, native disabled semantics, checkbox/revoke targets at least 44px, narrow/mobile stacking and no horizontal overflow, and both English/Polish strings with no raw keys. Snapshot the OAuth-related Nuxt paths before/after and fail on any diff; do not write fixtures, snapshots, generated files, or source inside Nuxt.</action>
|
||||
<verify>
|
||||
<automated>scripts/check-phase8.sh</automated>
|
||||
<automated>cd /media/nvme/dev/golem15/fonoteka/vue-fonoteka-app && pnpm verify:oauth-return-path && pnpm verify:oauth-i18n && cd /media/nvme/dev/golem15/summercms.io/summercms/summercms.go && node scripts/check-phase8-ui.mjs --focused</automated>
|
||||
</verify>
|
||||
<acceptance_criteria>
|
||||
- The gate starts the real unchanged MCP checkout and completes metadata, DCR, PKCE, JWT consent, token, `/me`, one MCP tool call, and refresh against the Go backend.
|
||||
- The gate proves spent refresh replay and connected-app revoke kill the lineage and later access/refresh attempts fail.
|
||||
- Both repositories pass `go vet ./...`, `go test ./...`, and `go test -race ./...`; corpus and secret scans exit 0.
|
||||
- `git -C /media/nvme/dev/golem15/fonoteka/fonoteka-mcp status --short` and the Nuxt equivalent show no Phase 8 diff.
|
||||
</acceptance_criteria>
|
||||
<done>The actual connector stack, including RFC 9728 resource-server behavior, runs unchanged through the complete Go authorization lifecycle.</done>
|
||||
<done>The full UI-SPEC state/accessibility/return-path/i18n matrix passes against unchanged Nuxt files, and the harness is callable from the final gate.</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
@@ -175,36 +108,29 @@ Unchanged MCP inputs:
|
||||
|
||||
| Boundary | Description |
|
||||
|----------|-------------|
|
||||
| PHP capture → committed fixtures | Live credentials must become typed placeholders before entering git. |
|
||||
| Scripted SDK → Go backend/MCP | External client parsing and redirects exercise public network-facing contracts. |
|
||||
| Gate process → child services | Secrets and cleanup state cross shell/Node/Go process boundaries. |
|
||||
| Browser JWT principal → consent API | Authenticated input crosses ownership/scope/tenant boundaries. |
|
||||
| Backend data → unchanged Nuxt | Untrusted names and protocol state select rendered UI states. |
|
||||
|
||||
## STRIDE Threat Register
|
||||
|
||||
| Threat ID | Category | Component | Disposition | Mitigation Plan |
|
||||
|-----------|----------|-----------|-------------|-----------------|
|
||||
| T-08-PKCE | Spoofing/Elevation | real SDK flow | mitigate | Actual SDK S256 authorize/exchange plus wrong-verifier rejection in gate. |
|
||||
| T-08-CODE-REPLAY | Spoofing | lifecycle replay | mitigate | Recorded and real repeated code/spent state fail. |
|
||||
| T-08-REFRESH-REPLAY | Spoofing/Elevation | lifecycle replay/gate | mitigate | Spent replay kills lineage; post-replay DB/API evidence required. |
|
||||
| T-08-OPEN-REDIRECT | Spoofing/Disclosure | recorded authorize cases | mitigate | PHP fixture and Go replay assert local errors versus trusted ordered redirects. |
|
||||
| T-08-SECRET-TIMING | Information Disclosure | confidential Basic flow | mitigate | Actual confidential flow uses the constant-time implementation; final source audit in 08-06. |
|
||||
| T-08-SCOPE-CEILING | Elevation | confidential lifecycle | mitigate | Recorded ceiling truncation and invalid-scope redirect. |
|
||||
| T-08-CROSS-USER | Elevation | consent/list/revoke replay | mitigate | JWT ownership cases and indistinguishable 404 replay. |
|
||||
| T-08-REQUEST-LEAK | Information Disclosure | fixtures/logs | mitigate | Capture categories, 0600 stores, placeholder-only fixtures, check-secrets and quiet gate. |
|
||||
| T-08-DCR-FLOOD | Denial of Service | register route | mitigate | Recorded native errors plus focused rate/body/cap tests run by gate. |
|
||||
| T-08-SURFACE | Elevation | MCP/backend boundary | mitigate | Gate asserts correct RFC 9728 ownership and exact backend challenges. |
|
||||
| T-08-SC | Tampering | reused Node dependencies | mitigate | No install; use checked-in lockfile/node_modules and dependency preflight. |
|
||||
| T-08-SCOPE-CEILING | Elevation | consent | mitigate | Four-way scope intersection and server-derived collection. |
|
||||
| T-08-CROSS-USER | Elevation | consent | mitigate | Principal-bound lookup/consume and indistinguishable 404. |
|
||||
| T-08-REQUEST-LEAK | Information Disclosure | browser/errors | mitigate | Opaque handle, no-referrer behavior, no request on invalid handle. |
|
||||
| T-08-SURFACE | Elevation | route groups | mitigate | Raw/JWT isolation and browser contract harness. |
|
||||
| T-08-SC | Tampering | Playwright reuse | mitigate | Reuse installed locked dependency; no package install. |
|
||||
</threat_model>
|
||||
|
||||
<verification>
|
||||
- `cd ../fonoteka.go && go test ./parity -run 'TestOAuthFlows|TestParityCorpus|TestParityContract' -count=1`
|
||||
- `scripts/check-phase8.sh`
|
||||
- Focused consent/app tests pass under 30 seconds where possible.
|
||||
- UI harness runs at the wave boundary and is invoked again by the final gate.
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- Nine OAuth routes and the full lifecycle replay pass against Go with no leaked fixture secret.
|
||||
- The real unchanged MCP discovers, authorizes, initializes, executes a tool, refreshes, and observes replay/revoke failure.
|
||||
- Resource-server and authorization-server header ownership is proven exactly, not conflated.
|
||||
- Consent API matches every unchanged client state selector.
|
||||
- Return-path, no-invalid-request, i18n, state, accessibility, and responsive matrices have runnable evidence.
|
||||
- Nuxt source remains unchanged.
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
|
||||
Reference in New Issue
Block a user