Files
intotheeast-com/CLAUDE.md
T
m038andClaude Opus 5 8202d2a257 docs: reconcile documentation against the code; add a supersession ledger
Five weeks of undocumented evolution left the docs describing a site that
partly no longer exists, with nothing marking which documents were historical.
The code is treated as the source of truth throughout; every claim below was
verified against code, config or the Makefile rather than inferred.

Two mechanisms, following patterns the repo already used:

- docs/reference/superseded-decisions.md (new) — one authoritative table of all
  14 reversals: what was planned, where, what is true now, when, and why. Plus
  a short list of decisions that were NOT reversed, since their planning docs
  are old enough to look suspect.
- Inline "> **Superseded ...**" notes at each stale claim, so a claim can never
  be read un-corrected. This mirrors the existing "> History:" notes in
  architecture.md and "> **Changed 2026-07:**" in trip-switching.md.

Scope split by tense: present-tense docs (CLAUDE.md, reference/, guides/,
README.md, CONCEPTS.md) are corrected; past-tense records (plans/, specs/,
milestones/, summary.md, pm-analysis.md) are annotated only, never rewritten —
their staleness is what makes them records.

Present-tense corrections:

- CLAUDE.md asserted css-compiled/ is generated from css/style.css and
  css/tokens.css. That source relationship does not exist: css/ is hand-authored
  and served directly via assets.addCss in partials/base.html.twig, while
  css-compiled/ is esbuild output from the CSS imports inside js/src/*.js.
  Highest-severity finding — an always-loaded file inviting a hand-edit of a
  generated bundle.
- README.md documented every remote-* target without the -test/-prod suffix
  guard-env requires, so its entire server runbook was unrunnable, and listed
  7 of ~20 targets while CLAUDE.md designates it authoritative for the full
  list. Rewritten with all targets, grouped, and the suffix rule stated.
- README.md told readers to "git clone" into user/, which is a submodule.
- architecture.md: nav is Home + Trips + (authenticated) New Post, not
  "Home + Past Trips only"; template tree omitted trips.html.twig,
  post-form.html.twig and forms/, and placed base.html.twig at templates/ root
  rather than in partials/; entry-actions has three API routes, not just delete;
  added the undocumented css-compiled/maplibre-gl.css output and a section on
  the /post pin editor as the one sanctioned non-entry-map map.
- design-system.md: documented 13 colour tokens against 19 in tokens.css
  (missing --color-error, --color-draft-accent and four glass overlays); claimed
  "all 3 map templates"; and described --color-canvas as "white".
- design-system-light.md documented a light palette in present tense. No light
  mode exists — tokens.css has a single :root block, no prefers-color-scheme or
  data-theme switch, and no light hex appears in css/. Banner added.
- posting.md said photos were optional; they are required, 1-6. It documented
  hero_image, which was removed for entries (stories keep it). It had no mention
  of the frontend edit flow or photo editor, both shipped 2026-07-08. Photo
  files are photo-01..NN, zero-padded.
- working/README.md advertised summary.md as the project's "current state" while
  summary.md describes Leaflet, /tracker, /map and /stats. Most misleading line
  in the tree.
- CLAUDE.md: recorded js/src/location-map.js as the one sanctioned exception to
  the single-map-path rule, and documented that make start/setup fail on a clean
  checkout because docker-compose.yml still builds a travel-memories service
  whose source moved out in a80b0a9.

Also: 2026-07-23-post-form-location-override.md read "Not started" while merged
in user/ as dd19995; status corrected.

Findings that are not documentation problems — the compose breakage, a
repeatable drift check, the unused shortcode-gallery-plusplus, and demo fixtures
for retired views — are collected in
docs/working/2026-07-25-doc-drift-recommendations.md and deliberately not acted
on. Design and verification method: docs/working/specs/2026-07-25-docs-reconciliation-design.md

The submodule gitlink is deliberately not bumped.

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

9.2 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 a third; an older three-variant setup was deliberately consolidated away.
    • One sanctioned exception: js/src/location-map.js is the /post pin editor — one draggable marker, no popups/GPX/bounds, maplibre-gl lazy-imported so a GPS-only submit never fetches it. It shares exactly one thing with the display path, the style URL in js/src/map-style.js. Do not fold them together.
  • 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.