Two real defects surfaced by the gate's first live run against the real
fonoteka-mcp SDK:
- stage_revoke looked up the connected app by a.name; ConnectedAppsIndex
actually serializes client_name (confirmed against
controllers/api/connected_app_controller.go serializeConnectedApp).
- Even with that fixed, stage_revoke ran after stage_replay, by which
point RevokeLineage's forward walk (presenting the pre-refresh spent
secret) had already cascade-revoked the live post-refresh access token
too -- correct, intentional T-08-REFRESH-REPLAY behavior, and the exact
same effect 08-09-PLAN.md's own mcp-lifecycle fixture ordering already
documented ('connected-apps would already be empty if list ran after
replay'). stage_refresh now captures the connected-app id while the
session is still live; stage_revoke DELETEs that id directly instead of
re-listing (ConnectedAppsDestroy has no revoked_at filter on its own
lookup, so this still exercises the real endpoint, idempotently, against
the id the real MCP-driven session actually owned).
Fills in every scripts/check-phase8.sh stage skeleton with real logic:
disposable Postgres (docker run + pg_isready), the assembled Go app built
and served against it with a throwaway onboarding-seeded gate account,
the real unchanged fonoteka-mcp process started with all three required
environment variables, and the full scripted SDK lifecycle -- discovery
(MCP's own RFC 9728 401 hint, verified separately from authorization
server metadata), DCR, PKCE authorize, JWT login/consent, token, an MCP
tool call, refresh, replay of the spent refresh token, revoke, and a
post-revoke refresh failure -- delegated to the new
scripts/check-phase8-mcp-client.mjs driver, which resolves the MCP SDK's
auth helpers from fonoteka-mcp's own node_modules (no new dependency,
same pattern as parity/capture_clients.mjs). Both repositories'
vet/test/race, the full parity/corpus/secret-scan gate, the existing
check-phase8-ui.mjs --final-gate UI harness, an unchanged-client git-diff
check for both MCP_ROOT and NUXT_ROOT, and a 08-SECURITY-REVIEW.md
status:verified gate close out the stage list.
--contract-self-test validates structure only (stage names/order,
cleanup trap, loopback-only binding, the three MCP env vars, the
redaction helper, no pre-final full-run flag, read-only unchanged-client
references) in well under 30 seconds -- it boots no services. The
--red-contract self-test from Task 1 is preserved unchanged. run_full_gate
(the no-flag invocation) is 08-10 Task 3's sole execution site; 08-09
never invokes it.