08-10 Task 3 is the first time this gate has actually been executed
against real Docker/Postgres/the real fonoteka CLI/the real fonoteka-mcp
process. Four independent, previously-undetected defects surfaced:
- stage_postgres never set POSTGRES_INITDB_ARGS for the ICU pl-PL locale
lagoon.Use requires (every other Postgres testcontainer in this project
already does); the app failed to boot at all.
- stage_app_boot's seed step POSTed to
/_fonoteka/api/v1/onboarding/bootstrap, a route routes.go never mounts
(its own comment marks that group deliberately empty, pending a later
phase). The gate's test user/collection are now seeded directly with
SQL, matching every app-level OAuth test's own real-Postgres seeding.
- phase8_workdir() assigned PHASE8_WORKDIR from inside a function body
that is always invoked via command substitution (a subshell): the
assignment never escaped back to the calling shell, so every separate
caller (stage_postgres, stage_app_boot, each phase8_mcp_stage call, ...)
minted its own fresh mktemp directory. This silently fragmented one
run's state (app.log, the MCP client's gate-state.json) across dozens
of directories that never saw each other's writes -- the MCP client's
dcr stage could never see discovery's saved metadata. PHASE8_WORKDIR is
now set once, directly, in run_full_gate before any stage runs.
- gate-state.json (the MCP client's shared cross-invocation state) holds
raw live secrets by design and is never redacted; stage_secret_scan
correctly flagged it. It is now deleted once the MCP lifecycle stages
are done with it, before the scan runs -- the scan itself stays exactly
as strict as it already was.
stage_security_review also now refuses a nonzero threats_open count or a
missing required T-08-* row, not just a missing/unverified file, and gains
--security-review-only, a focused mode for Task 2's own verify command.
stage_security_review now also refuses a nonzero threats_open count and
any missing required T-08-* threat row, not just a missing/unverified
file. Adds --security-review-only, a focused mode running just this
stage (Task 2's own verify command) with no services booted.
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.
- Declares the ordered Phase 8 stage list and stage function skeletons
- --red-contract <stage> is a permanent RED-harness self-test hook
(exit 86, PHASE8_STAGE:<stage>:FAIL:PHASE8_RED:real-mcp-stage)
- --contract-self-test and the full gate are completed in Task 3/08-10
check-phase8-ui.mjs encodes 08-UI-SPEC.md's full consent/connected-app
state matrix, accessibility, responsive, and i18n contract as a versioned
32-scenario catalog across 7 categories. --contract-self-test validates
catalog completeness, guarded Nuxt source-file hashes (proving the
harness itself never writes inside vue-fonoteka-app), and that
@playwright/test resolves from the already-installed dependency, all
without booting a browser or service (runs in ~50ms).
--final-gate (running verify:oauth-return-path, verify:oauth-i18n, and
the real Playwright matrix) is scaffolded but refuses to run without
PHASE8_UI_ALLOW_FINAL_GATE=1 and is explicitly 08-10's closing-checkpoint
responsibility, not executed by this plan.
- wristband.Server.Metadata is a compiling 501 stub; TestPhase8RedMetadata
asserts the exact unwrapped PHP metadata document, headers and status and
fails with the PHASE8_RED:metadata sentinel (D-06)
- scripts/check-phase8-red.sh implements the shared go/shell RED contract
for the rest of Phase 8: exact selected test/package failure plus sentinel,
rejecting unrelated fail actions, compile/setup failures, panics,
malformed JSON, missing/duplicate sentinels and zero selection (D-04/D-18)
- Assert memory recipients, HTML safety, and SMTP TLS without credential leaks
- Prove real SMTP delivery through Mailpit HTTP API when Docker is available
- Add scripts/check-phase4.sh as the phase vet, test, race, and SMTP gate
Co-authored-by: Cursor <cursoragent@cursor.com>
The Phase 2 PHP self-replay currently fails four wishlist album_count
routes that this slice did not change. The Phase 3 script now re-runs
TestParitySynthetic and the CLI record/replay smoke instead of
check-phase2.sh --fresh-php.
Co-authored-by: Cursor <cursoragent@cursor.com>
- Root and app vet/test/race plus focused genres parity and corpus audit
- Refuses missing Docker and runs the Phase 2 --fresh-php regression
Co-authored-by: Cursor <cursoragent@cursor.com>
- Avoid urlsafe passwords that start with a dash and break mariadbadmin -p
- Probe readiness with a quoted SQL SELECT against the disposable container
Co-authored-by: Cursor <cursoragent@cursor.com>
- Check root and fonoteka.go with vet, test and race plus TestParitySynthetic
- Audit the 154-route corpus and smoke parity:record/replay against loopback
- Provision a disposable MariaDB, pin PHP to 127.0.0.1:8423, and self-replay seed, routes and clients
Co-authored-by: Cursor <cursoragent@cursor.com>