Files
intotheeast-com/CLAUDE.md
T
m038andClaude Opus 5 143ec135c0 test(stories): cover scrolly step text and back-to-top; fix build-output note
S8 asserts scrolly-section splits its slot content into visible step panels
(the blank-text-column regression had no coverage: S3 only checked the image
column existed, and the silent early return threw nothing for S4 to catch).
S9 covers back-to-top now that main.js solely owns it, including the history
entry the removed inline copy lacked.

CLAUDE.md attributed css-compiled/ and fonts/ to css/style.css and
css/tokens.css. They are built from the js/src/ entrypoints' CSS and font
imports; css/ is hand-authored and served directly, needing no rebuild.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 23:16:22 +02:00

8.9 KiB

CLAUDE.md

Rules, gotchas, and entry points — the things that must change what you do before you open a file. Everything descriptive lives next to the code:

Need Read
How the site hangs together — stack, plugin roles, templates, partial contracts, data flows docs/reference/architecture.md
Domain vocabulary — Trip, Entry, Story, Active Trip CONCEPTS.md
Doing something operational — posting, writing stories, GPX, switching trips, local setup, deploying docs/guides/
Test suite layout and conventions docs/reference/testing.md
A bug or workflow trap already hit and written up docs/solutions/ — grep the module/tags/problem_type frontmatter; check when working in a documented area
Folder map, prerequisites, the full make command list README.md

The site is Grav (flat-file PHP CMS, no database) in Docker, with content and theme in the user/ submodule.

Hard rules

  • Only ever write inside travel-blog-intotheeast/ or its subfolders.
  • Never read .env, .env.prod, .env.test — they hold credentials. Pass them to commands (make, docker compose) but never read them; ask the user if you need a value.
  • Never SSH to a server directly — use the make remote-* targets, since credentials live in .env. If no target covers what you need, ask the user to run it or propose a new target.
  • Never hand-edit build output — sources and outputs share folders under user/themes/intotheeast/ (paths below are relative to it), so know which is which. Run make build-assets after editing any source.
    • Everything in js/ is generated except js/src/, js/maplibre-utils.js and js/nav.js.
    • css-compiled/ and fonts/ are generated by esbuild from the js/src/ entrypoints' CSS and font imports (fontsource, photoswipe, maplibre-gl) — not from css/. css/style.css and css/tokens.css are hand-authored and served directly (partials/base.html.twig), so editing them needs no rebuild.
    • templates/partials/weather-icons.html.twig is generated (source: scripts/gen-weather-icons.js).
  • Never toggle dev↔prod mode mid-session. If a caching or config issue appears, fix it at the application level (plugin, template logic) rather than flipping a mode flag — mode switches leave inconsistent state and make bugs harder to reproduce.

Dev environment

  • Dev server: http://localhost:8081 (make setup on a first run, make start / make stop after). A second service, travel-memories, runs on :8082. A worktree gets its own container and port 8090+ from its .worktree-env — pass GRAV_BASE_URL when pointing tests at one.
  • user/config/system.yaml is committed with dev values (twig.cache: false), so templates recompile per request and no cache flush is needed after editing a .html.twig. Prod values live in deploy/env/prod/system.yaml and never in user/config/.
  • ⚠️ Once user/env/<hostname>/ exists on a server, Grav's Admin saves ALL config there — system and plugin. So (a) config edited via Admin on the server is server-only and silently never reaches Gitea or local; (b) when reading or writing server config, check both user/config/… and user/env/<host>/config/…env wins, so look there first. Mechanics: docs/guides/deploy-cycle.md.
  • The Admin plugin slug is admin2, not admin.
  • plugins.txt is maintained by hand — installing a plugin via Admin does not update it. git-sync is remote-only and must never appear in it.
  • Everything under user/plugins/ is git-ignored and gets overwritten by make install-pluginsexcept the three site-owned plugins (cache-on-save, story-blocks, entry-actions). So a fix to a third-party plugin must be a tracked patch in deploy/patches/, never an in-place edit: deploy/patches/README.md.

Content and trips

  • The active trip lives in one place: user/config/site.yamlactive_trip, and its value is a route (/trips/denmark-2026), not a bare slug.
  • cache-on-save derives the post write target from active_trip at submit time. Never re-add a pageconfig.parent to post-form.md — a static parent would override it and reintroduce the old silent-desync bug. Switching trips: docs/guides/trip-switching.md.
  • The standalone /dailies, /map, /stats and /stories trip views were deleted (2026-07-04) — map, stats, and filtering all render inline on the trip page. Do not re-create them or link to them. 01.dailies/ and 04.stories/ are routable:false data containers whose children are aggregated by the trip page.
  • GPX routes are page media on the trip page, auto-detected — no manual linking. Manage them at /gpx-manager (admin login): docs/guides/gpx-manager.md.
  • make content-push commits and pushes user/ to Gitea, which triggers the production pull; make content-pull is the reverse.

Two shared partials — the rules

Trip and home render the same map and feed chrome through two shared partials, both included with {…} only. Parameter contracts: docs/reference/architecture.md → "Shared partial contracts". What must not break:

  • partials/entry-map.html.twig is the only path for a display map — the engine is MapUtils.initEntryMap(opts) in js/maplibre-utils.js (a hand-authored file, imported by js/src/map.js). Do not add another display-map implementation; an older three-variant setup was deliberately consolidated away.
  • One sanctioned exception: js/src/location-map.js, the /post form's pin editor (one draggable marker, no popups/GPX/bounds-fitting, maplibre-gl lazy-imported so a GPS-only submit never fetches it). It shares exactly one thing with the display path — MAP_STYLE from js/src/map-style.js, imported by both so the basemap cannot drift. Do not fold it into initEntryMap, and do not add a third path.
  • It must keep assigning window.tripMap / window.homeMap — the Playwright map specs assert those globals.
  • Keep trip-feed-col.html.twig single-purpose. Its sibling partials/home-predeparture.html.twig is the home-only "Coming soon" state — do not fold the pre-departure branch back into it.

Dual-repo submodule structure

user/ is a git submodule with its own Gitea remote and its own cadence; the outer repo pins an exact commit. Full workflow, worktree mechanics, teardown: docs/solutions/architecture-patterns/dual-repo-submodule-workflow.md.

  • M user / m user is normal, not an error. M = the pin differs from user/ HEAD; m = the submodule working tree is dirty (e.g. a local-testing site.yaml). Do not "fix" either by committing the gitlink or that site.yaml.
  • Don't bump the pin for routine content changes. Bump it once at the end of a cross-repo feature, to a commit reachable from user/'s published main, and push user/ before the outer repo.
  • Use make worktree-new NAME=<x> / make worktree-rm NAME=<x> — never a hand-rolled git worktree add. The targets initialise the submodule and an isolated dev server; skipping the deinit on teardown is what leaves orphaned .worktrees/ dirs.

Testing

make test runs everything (test-configtest-posttest-ui). The dev server must be running — every suite drives the live site over HTTP. Layout, helpers, and per-suite commands: docs/reference/testing.md.

  • Auth is a dependency project. auth.setup.js writes tests/.auth/user.json, which the chromium project reuses as storageState. Never add per-test logins.
  • The testrunner admin account is created automatically and is git-ignored — never commit it, and keep its password free of shell/Make/URL-special characters, since several consumers interpolate it.
  • retries: 0, so a failing test is a real failure, not flake.

Working docs

Specs go in docs/working/specs/YYYY-MM-DD-<topic>-design.md, plans in docs/working/plans/YYYY-MM-DD-<topic>.md. These paths override the docs/superpowers/ default used by the brainstorming and writing-plans skills.

Every plan needs a **Status:** line immediately after its title heading: 📋 Not started · 🔄 In progress — <note> · ⏸️ Deferred — <reason> · ✅ Complete (YYYY-MM-DD) · ❌ Abandoned — <reason>.

  • When asked what's open: surface Not started and In progress; show Deferred but label it clearly; omit Complete and Abandoned unless explicitly asked.
  • When finishing a plan: set its status to ✅ Complete (YYYY-MM-DD) before closing the session — whether you executed it directly or via the executing-plans / subagent-driven-development skills.