A ui-test entry had survived into the active trip's dailies. Three independent
failures had to line up for that, and all three were real:
1. cleanupEntry() used host-side fs.rmSync. Grav's Apache workers run as root,
so every entry the form creates is root-owned and recursive removal needs
write permission on that directory — which the host user lacks. Cleanup had
never worked for form-created entries; it just threw inside a path nothing
checked. It now falls back to `docker exec … rm -rf` in the container that
actually serves USER_DIR.
2. globalTeardown's dailies sweep keyed off a `parent:` in post-form.md — a key
deliberately removed (the write target comes from site.yaml active_trip, and
CLAUDE.md forbids re-adding a static parent). The regex could never match, so
dailiesDir was always null and the sweep silently did nothing. It now reuses
helpers' own resolution instead of keeping a divergent copy.
3. Nothing pinned the suite to this checkout's server. playwright.config.js
defaults to :8081, so a worktree run hit the MAIN checkout — entries created
in one content tree while the specs asserted and cleaned up in another.
test-ui now passes GRAV_BASE_URL from GRAV_PORT, and globalSetup hard-fails
when the server's bind mount disagrees with the tree the specs read.
Also fixed, found on the way to a green run:
- test-account interpolated the password into an `sh -c` string, so a password
containing a shell metacharacter was re-parsed by the container's shell
(`sh: 2: <fragment>: not found`, no account, every UI run dead). It now
travels via `docker exec -e`, making the recipe indifferent to its contents.
- `make start` in a worktree always failed: travel-memories declares
`env_file: .env` and worktree-new creates none. It degrades to start-grav
there — a worktree with no server is what sent runs to :8081 in the first
place.
- test-form-config asserted a hero_image field that 8cf1145 deliberately
removed; it had been failing ever since.
Verified: config 22/22, post 6/6, location-override 20/20, and a full UI run
now leaves zero ui-test entries behind. The remaining UI failures are
pre-existing on main — site.yaml pins owner_username to a real account while
the suite logs in as testrunner, so owner-only controls never render for it.
Only trip-publish.spec.js patches that; delete-flow, edit-mode and anon-view
do not. Left for a separate branch.
Add a dedicated, gitignored testrunner account auto-created by the suite
(make test-account, also invoked from global-setup) so `make test` no longer
needs the real account in .env. The target uses --admin-type both so the
account has admin.login for the gpx-manager specs. test-post.sh switches its
login POST to --data-urlencode to survive special chars in credentials.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Np4cMQLF77i664CAQXySzU
- dailies T2: switch ordering test to central-asia-2023 (pixelfed-1 oldest, pixelfed-22 newest)
- dailies T3-T6: update KNOWN_SLUG/TITLE/CITY/COUNTRY to the real japan entry (2026-06-17)
- stories S1-S7: update all italy-2025 URLs to italy-2026-demo
- stories S5/S6: fix URL regex and use val-dorcia-dawn for hero sanity check
- maps M5/M6: point Italy GPX map tests to italy-2026-demo (has markers + GPX)
- global-setup: run make demo-load before tests so italy-2026-demo always exists
- post P2: add retries:1 + test.setTimeout(60s) for intermittent FilePond upload
- user: story template hero fallback for media.types config override (see user commit)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vgmzx8VTTTmCskSpQtsLTr