- sessionKey, dateFormat, FileuploadField (protected thumbnails and
keyboard reorder backstops), RelationChildModal, RelationPivotModal,
RelationManager row actions and create-screen deferral, date and time
list cells; typed deferred relation-schema and file-list fixtures
- scripts/check-phase12.2.sh: go, security (named tests, refuses missing
or skipped), spa, openapi, dist, docs, hygiene and app stages, a
detector self-test, one PASS or FAIL line per stage under --all
scripts/check-phase12.sh, modelled on check-phase11.sh:
- --go: vet and test both repositories, golang.org/x/image pinned at v0.46.0
- --parity: 99 ported routes in the manifest, TestParityCorpus with its
coverage subtest, all four broadcast goldens, both Nuxt flows,
check_corpus --require-recorded --check-secrets and a secret scan of the
fuzz seed corpus
- --named: every test 12-VALIDATION.md names, by exact name, the fonoteka
plugin's under -race
- --removal: 25 anchor-exact mutations behind 12-SECURITY-REVIEW.md, each
required to fail its named test on an assertion; a dirty file is
refused and every file is restored and compared with cmp
- --coverage: an 80% floor per Phase 12 package in both repositories
- --evidence: one review row per T-12 threat, a removal row per high
mitigated threat, a green validation file naming only tests the gate runs
- --self-test: every detector, plant and harness branch fails closed
Under set -e a missing TAP summary aborted run_site without a refuse
message; the explicit refuse checks now report it. The site's test
script pins the TAP reporter so the summary lines exist on Node 23+.
- --built: build.sh (release when the framework checkout has v0.1.0, else
dev), then TestLandingLinks, TestTerminalCommandsInPage and
TestDocsHeaderSiteLink with SUMMERCMS_REQUIRE_BUILD=1, required to PASS
- --smoke and --deploy require smoke: ok and check-deploy: ok
- --terminal runs TestTerminalCommands with this checkout as the clone URL;
--verbatim leaves the page's URL for the cutover run
- --full runs the framework's whole suite; --all runs every stage in order
- TestCheckSiteURL (21 accepted and rejected values), TestSiteLabel and
TestSiteURLPrecedence (option over site.yaml, label-without-URL and
invalid option errors); new TestParseSite rows for blank and two-line labels
- TestSiteLink: escaped label, section page and 404 page, exact unset header
bytes
- TestDocsSiteFlagsInHelp and the --site-label-without-URL error
- check-phase11.2.sh --framework, --app and --site
- scripts/check-phase11.2.sh --plugin: go vet, the named plugin tests through
a go test -json detector (fail, skip, missing test, zero tests and no tests
to run all refuse) and a 90% statement coverage floor
- unknown modes print usage and exit 2
- lagoon.OrderBy takes variadic lagoon.OrderOption values; lagoon.Collate(name)
emits a validated, double-quoted COLLATE clause (e.g. "pl-x-icu")
- remove the exported CheckLocale and the ICU pl-PL check from Open and Use
- framework test containers and per-test databases are plain PostgreSQL
- lagoon README, root README and docs pages drop the locale requirement;
queries-and-pagination gains a "Sorting with a collation" section
backed by ExampleCollate
- a refusal exits from inside a stage, where the RETURN trap never ran,
leaving the self-test's 48 MB scratch copy in /tmp; an EXIT trap now
removes every stage's scratch paths
- the self-test copies examples/ with tar, excluding examples/*/bin,
instead of git ls-files, so a PHASE11_1_ROOT copy without .git works
- TestPhase11_1Acceptance asserts SC1 to SC5 on the real tree: sections
and module pages, theme markers and no Node exec, AI outputs in sync,
every go fence a src= copy run by go test, concept map and walkthrough
- check-phase11.1.sh --named runs every named test of the phase by exact
name (modules' TestDocs* and output Examples derived from source) and
refuses failures, skips, missing or renamed tests and no tests to run
- --all runs preconditions, deps, self-test, docs, forbidden, claude,
named and the full go vet and go test
- testdata/clean passes every check; 63 violation cases each overlay one
fault and a want.txt naming the single problem it must produce
- TestPlantedViolations also plants the forbidden name (split literals)
and symlink escapes at run time; TestBuildOutputGuard covers --out
- check-phase11.1.sh --self-test runs the corpus through a go test -json
detector that refuses FAIL, SKIP, zero tests and no tests to run
- the self-test scratch copy now carries examples/ and README.md, so its
baseline sees the example command literals the real tree sees
- WinterCMS-style shell: header with search and theme toggle, grouped
sidebar, on-page TOC, pager, page actions, callouts, heading
permalinks, footer and a 404 page
- fenced code highlighted at build time by chroma/v2 into tok-* classes,
with a copy button; no inline script, style or handler
- vendored DM Sans/DM Mono fonts and Lucide icons with their licences
- client-side search over search-index.json built with textContent only
- summer docs:serve builds into a temp dir, serves on loopback by default,
returns 404.html with status 404 and rebuilds on change
- relative links and anchors resolve against the renderer's heading IDs
- summer and ./bin/<app> command names come from the real command
constructors through docsite.Options.Commands; a nil set is a problem
- consuming-application names fail in page sources and built outputs
- go fences in docs/ pages need src=, callouts are NOTE, TIP or WARNING,
docs/ headings are plain ASCII
- gate gains --claude and self-test plants for each new rule
- go/parser index of every modules/ package and sub-package, with methods,
fields, interface methods and promoted members
- code spans in docs pages, module READMEs and the root README fail
Check and docs:build when the named identifier does not exist
- scripts/check-phase11.1.sh with preconditions, deps, docs, forbidden,
go and a self-test that plants one violation per rule
- 11-06 records TestBroadcastGoldens/created and /updated and reports them
as skipped until Phase 12; the Phase 10 detector refused any skip, so
check-phase10.sh --all failed on the fonoteka.go suite
- mirrors 73cfed7 (check-phase10.1.sh): the detector accepts exactly those
skips when their output carries 'pending: Phase 12'; the self-test proves
a pending skip passes and one without the text fails
- 11-06 records TestBroadcastGoldens/created and /updated and reports them
as skipped until Phase 12; the 10.1 detector refused any skip, so
check-phase10.1.sh --all failed on the fonoteka.go suite
- the detector now accepts exactly those skips when their output carries
'pending: Phase 12' (the same rule check-phase11.sh enforces); the
self-test proves a pending skip passes and one without the text fails
- scripts/check-phase11.sh: --self-test, --hygiene, --go, --postgres,
--named, --evidence, --all (prints 'phase11 all passed') and --removal
- the go test -json detector refuses failures, skips, zero tests and
'no tests to run'; only the two Phase 12 broadcast goldens may skip, and
only with their pending text
- hygiene refuses application names in the Phase 11 framework files, the
Centrifugo/Typesense/Web Push client libraries, a direct cron
requirement, River other than v0.47.0 and a module without README or
root row; each rule returns on its first violation and the self-test
proves each refuses its own plant and accepts look-alikes
- --removal: anchor-exact mutations for the high threats, each required to
fail its named test on an assertion and restored byte for byte (cmp)
scripts/check-phase10.1.sh: --self-test, --go, --security, --postgres,
--spa, --openapi, --dist, --hygiene, --evidence and --all.
- phase101_detect refuses failed, skipped, zero-test, non-JSON and
build-failed runs and required tests that did not pass
- hygiene_101 refuses HTML-string parsers in admin/src, network, cookie
or storage access in application plugin asset JS, and script, style or
inline handler markup in application partial templates; each rule is
proven by its own self-test plant
- --evidence requires a review row and, for every high threat, a named
test and a removal check row
TestMigrateSeedsCanonicalGenres and TestSchemaMatchesPHPSnapshot pass
since fonoteka.go 21c0f12, and the detector refuses an allow-listed
failure that passes, so check-phase10.sh --go failed. The gate now
allow-lists nothing.
- --hygiene refuses localStorage, sessionStorage, indexedDB or document.cookie
outside admin/src/state/useSidebar.ts (T-10-22)
- --self-test plants each violation with a scratch test import, so the
refusal must come from that rule and not from the untested-module check
- scripts/check-phase10.sh with --self-test, --go, --security, --postgres,
--spa, --openapi, --dist, --hygiene, --evidence and --all
- phase10_detect refuses failed, skipped, zero-test, non-JSON and build-failed
go test runs and named tests that did not pass
- the two known fonoteka parity failures are the only allow-listed ones and
refuse the gate once they pass again
- hygiene enforces the framework/app boundary, SC-4 alias-only API types,
typed-client-only HTTP, no raw HTML, same-origin dist, named lucide imports,
no retired admin prefix routes and a test import for every SPA module
- refresh and logout read the Bearer header first, then the summer_admin
cookie; a cookie refresh rotates the cookie without a token in the body and
logout always expires the cookie
- backend.cookie_secure (default true) may drop Secure outside production only
- activation rejects controller vendor segments api, assets, login, settings
- BuildRouter rejects non-cabana routes at or under the admin prefix
- SPA single-flights refresh on 401, replays once, and refreshes proactively
at 80 percent of expires_in; dist rebuilt
- scripts/check-admin-dist.sh rebuilds the SPA and fails on dist drift
- tests: TestPhase10CookieAuth, TestPhase10CSRF, TestPhase10Prefix,
TestPhase10AdminPrefixCollision, boardwalk serving and header tests
- backend.uri prefix (default /backend) mounts the admin API at {prefix}/api/v1
and the embedded SPA shell at {prefix} with an api/ JSON 404 fallback
- cookie transport: an X-Requested-With login sets the HttpOnly summer_admin
cookie and returns no token; the backend guard reads the cookie after Bearer
- CSRF wrapper refuses cookie-only POST/PUT/DELETE without X-Requested-With
- boardwalk package embeds boardwalk/dist, rewrites index.html once per prefix
and sets cache and security headers
- framework admin OpenAPI pipeline (swag, swagger2openapi, openapi-typescript)
with prefix-relative paths and typed envelopes for the tracer routes
- admin/ Vite SPA: login, plugin rail, section panel and read-only list
through the openapi-fetch client typed by the generated schema
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).
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>