Files
intotheeast-com/CLAUDE.md
m038andClaude Opus 5 7c9c140a1b Merge main into feat/docs-reconcile; defer to main on two overlapping fixes
main advanced 13 commits while this documentation audit ran — the
location-override work was merged into the outer repo — and it independently
fixed two of the audit's findings. Without this merge the branch would have
REVERTED them, which is the worst outcome for a cleanup pass because it arrives
disguised as an improvement.

Both conflicts resolved in main's favour; main's wording was better informed:

- CLAUDE.md, the single-map-path rule: main's carve-out (829325c) states the
  exception as its own top-level bullet, names MAP_STYLE as the one shared
  thing, and spells out both prohibitions ("do not fold it into initEntryMap",
  "do not add a third path"). Taken verbatim over the version drafted here.
- 2026-07-23-post-form-location-override.md: main (a517331) had already set the
  status to Complete, with far richer detail — the multi-agent review findings,
  the green-run numbers, the DEL4 regression still open, and the merge SHAs.
  Taken in full; the audit's claim that the status "lagged" was dropped, since
  it was true only of this branch's older branch point.

Submodule pin: main bumped user/ to dd19995 and this merge preserves that. The
audit's own no-gitlink-commit discipline applies to bumping the pin as a side
effect of routine work, not to discarding a bump main already made.

main touched none of the other nine corrected documents, so the remaining 18
findings stand unchanged.

Audit notes corrected to match reality rather than left overstated:

- superseded-decisions.md R13 now dates the carve-out to 2026-07-24 (829325c)
  rather than implying this pass introduced it.
- The reconciliation spec gains an "audit baseline moved twice" section: the
  submodule pin lagged real HEAD, and then the base branch advanced mid-audit.
- The compounded learning's section 6 is rewritten from "audit the current
  state" to "re-check the baseline before publishing, not only before starting",
  with the two habits that actually follow: merge the base branch in before
  opening the PR and read conflicts as findings, and when the incoming version
  is better, take it wholesale. An audit has no special authority over the work
  it audits.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 00:21:47 +02:00

9.3 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, 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
Why an old plan describes something that no longer exists docs/reference/superseded-decisions.md — check before re-creating anything found in docs/working/
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 esbuild output from the imports inside js/src/not from css/. Everything in css/ is hand-authored and served directly (assets.addCss in partials/base.html.twig), never compiled. So templates/partials/weather-icons.html.twig is also 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 worktree gets its own container and port 8090+ from its .worktree-env — pass GRAV_BASE_URL when pointing tests at one.
  • ⚠️ make start / make setup fail on a clean checkoutdocker compose up -d still tries to build a travel-memories service whose source was moved out of this repo, so the build context is missing. Use make start-grav (Grav only). Existing containers keep working from a cached image, which is why this hides until a rebuild.
  • 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.