# 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`](docs/reference/architecture.md) | | Domain vocabulary — Trip, Entry, Story, Active Trip | [`CONCEPTS.md`](CONCEPTS.md) | | Doing something operational — posting, GPX, switching trips, local setup, deploying | [`docs/guides/`](docs/guides/) | | Test suite layout and conventions | [`docs/reference/testing.md`](docs/reference/testing.md) | | A bug or workflow trap already hit and written up | [`docs/solutions/`](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`](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 (sources: `css/style.css`, `css/tokens.css`); so is `templates/partials/weather-icons.html.twig` (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//` 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//config/…` — **env wins**, so look there first. Mechanics: [`docs/guides/deploy-cycle.md`](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-plugins` — **except** 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`](deploy/patches/README.md). ## Content and trips - The active trip lives in **one** place: `user/config/site.yaml` → `active_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`](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`](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`](docs/reference/architecture.md) → "Shared partial contracts". What must not break: - **`partials/entry-map.html.twig` is the only map path** — the engine is `MapUtils.initEntryMap(opts)` in `js/maplibre-utils.js` (a hand-authored file, imported by `js/src/map.js`). Do not add a second map implementation; an older three-variant setup was deliberately consolidated away. - 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`](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=` / `make worktree-rm NAME=`** — 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-config` → `test-post` → `test-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`](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--design.md`, plans in `docs/working/plans/YYYY-MM-DD-.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 — ` · `⏸️ Deferred — ` · `✅ Complete (YYYY-MM-DD)` · `❌ Abandoned — `. - **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.