From 2fbfc884b99c8e460b22f14e989af6b19271ec14 Mon Sep 17 00:00:00 2001 From: Mischa Date: Fri, 24 Jul 2026 20:26:10 +0200 Subject: [PATCH] docs(claude-md): fix four stale facts; document testing, dev commands, patches Audit of the root CLAUDE.md (scored 76/100) found the architecture and remote-ops coverage strong but the test workflow entirely undocumented and several facts drifted from the tree. Corrections (verified against the checkout): - active_trip was japan-korea-2026; committed value is /trips/denmark-2026 and no japan-korea trip folder exists. Also note the value is a route. - Admin2 2.0.10 -> 2.0.12 (installed version). - demo-load/demo-reset described as italy-only; the Makefile loops over every fixture trip under user/docs/demo/trips/. - user/ gitignore claim omitted the three un-ignored site-owned plugins and the secret/env exclusions. Additions: - Section 3 "Testing": make test/test-config/test-post/test-ui, the auto-created testrunner account, Playwright layout, the auth.setup.js storageState dependency, and GRAV_BASE_URL for worktree servers. - Local dev command table, plus which theme assets are build outputs (js/src -> bundles) versus hand-authored (css/style.css, css/tokens.css). - Custom plugins: story-blocks and entry-actions alongside cache-on-save. - Local plugin patches: install-plugins overwrites git-ignored third-party plugins; deploy/patches/ + apply-plugin-patches is the tracked fix path. - travel-memories service on 8082; make pixelfed-import. Every make target and file path referenced was verified to resolve. Co-Authored-By: Claude Opus 5 (1M context) --- CLAUDE.md | 62 +++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 56 insertions(+), 6 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index ac2389b..6f1f7d8 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -16,7 +16,7 @@ ### Current stack - **Grav:** 2.0.7 stable (baked into the custom Docker image via `Dockerfile`; server upgrades in place via `bin/gpm self-upgrade`) -- **Admin:** Admin2 v2.0.10 (plugin slug: `admin2`, NOT `admin`) +- **Admin:** Admin2 v2.0.12 (plugin slug: `admin2`, NOT `admin`) - **GPM channel:** `stable` — set in `user/config/system.yaml` → `gpm.releases` (authoritative). `GRAV_CHANNEL=production` in `docker-compose.yml` is cosmetic/consistency only - **Plugin management:** `admin2`, `api`, and `flex-objects` are now **GPM-managed via `plugins.txt`** (installed by `make install-plugins`), no longer hand-extracted from the core bundle. `git-sync` stays **remote-only** — never in `plugins.txt` - **Docker image:** `getgrav/grav` with `GRAV_CHANNEL=production` @@ -24,12 +24,43 @@ ### Dev server -The Docker dev server runs at **http://localhost:8081** (mapped from container port 80 in `docker-compose.yml`). +The Docker dev server runs at **http://localhost:8081** (mapped from container port 80 in `docker-compose.yml`). A second service, `travel-memories`, runs at **http://localhost:8082**. Both ports and the container name are overridable via `GRAV_PORT` / `TM_PORT` / `GRAV_CONTAINER` — a worktree's `.worktree-env` sets these so isolated servers never collide (see "Dual-repo submodule structure"). + +### Local dev commands + +| Command | What it does | +|---|---| +| `make setup` | One-shot first run: `build` → `start` → `install-plugins` → `fix-perms` | +| `make start` / `make stop` | Bring the compose stack up / down (`start-grav` = Grav service only) | +| `make build` | Rebuild the custom Docker image (after `Dockerfile` changes) | +| `make build-assets` | Rebuild theme JS/CSS bundles in a `node:20-alpine` container | +| `make install-plugins` | GPM-install everything in `plugins.txt`, then re-apply local patches | +| `make fix-perms` | Fix ownership in the container after root-owned writes | + +**`make build-assets` is mandatory after editing anything in `user/themes/intotheeast/js/src/`.** Sources live in `js/src/`; esbuild writes the committed bundles — `js/main.js`, `js/map.js`, `js/feed-actions.js`, `js/trip-publish.js`, `js/post/`, and the CSS extracted into `css-compiled/`. **Never hand-edit those.** By contrast `css/style.css` and `css/tokens.css` are hand-authored sources, not build outputs. `build-assets` runs as your host UID (`--user`) so the outputs in the bind-mounted `user/` tree are not root-owned. + +### Custom plugins + +Three plugins are site-owned and tracked in the `user/` repo (everything else under `user/plugins/` is GPM-managed and git-ignored): + +| Plugin | Role | +|---|---| +| `cache-on-save` | Clears the page-tree cache on `new-entry` submits, and derives the write target from `site.active_trip` (`onFormValidationProcessed` → `setData('parent', …)`) | +| `story-blocks` | Storytelling shortcode blocks for long-form stories (depends on `shortcode-core`) | +| `entry-actions` | Owner-only, active-trip-scoped journal entry actions (delete) via the Grav API | + +### Local plugin patches + +Third-party plugins live in the **git-ignored** `user/plugins/`, so local fixes to them do not travel with the content repo and are **overwritten by `make install-plugins`** or a fresh image build. Keep the fix as a tracked patch in `deploy/patches/` instead: + +- `make apply-plugin-patches` — idempotent `git apply` (skips already-applied patches). `make install-plugins` runs it automatically as its last step +- `make remote-apply-plugin-patches-test` / `-prod` — piped over SSH into `patch -p1 --forward`; also runs automatically after a remote plugin install +- Details and the current patch list: `deploy/patches/README.md` ### Trip entity architecture The site is structured around Trip entities. Key facts: -- Active trip is set in `user/config/site.yaml` → `active_trip: japan-korea-2026` +- Active trip is set in `user/config/site.yaml` → `active_trip` (currently `/trips/denmark-2026`). The value is a **route**, not a bare slug - Trip pages live at `user/pages/01.trips//` - Each trip has two content subfolders: `01.dailies/` (journal entries) and `04.stories/` (stories). The former `02.map/` and `03.stats/` standalone views were **removed** (2026-07-04, see `docs/working/plans/2026-07-04-standalone-page-cleanup.md`) — map and stats now render inline on the trip page - `01.dailies/` and `04.stories/` are `routable:false` **data containers** — visiting `/trips//dailies` or `/stories` directly 404s/redirects; their children (entries/stories) render at their own detail URLs and are aggregated by the trip page @@ -141,12 +172,13 @@ For a full upgrade/deploy through local → test → prod (ordered steps, smoke - `make content-push` — commit and push `user/` to Gitea (triggers production pull via webhook) - `make content-pull` — pull latest from Gitea to local - `plugins.txt` is manually maintained — installing a plugin via Admin does NOT update it -- `make demo-load` — load demo content into `italy-2026-demo` trip (12 journal entries + 4 stories + 7 GPX files); source in `user/docs/demo/trips/italy-2026-demo/` -- `make demo-reset` — remove the entire `italy-2026-demo` pages folder and clear cache (full reset; re-run demo-load to restore) +- `make demo-load` — load **every** fixture trip under `user/docs/demo/trips/` into the pages tree (currently `italy-2026-demo` and `no-photos-demo`). Add a new fixture by dropping a trip folder there; no Makefile edit needed +- `make demo-reset` — remove the demo trips' pages folders and clear cache (full reset; re-run `demo-load` to restore) +- `make pixelfed-import` — import posts from Pixelfed via `scripts/pixelfed-import.py` ### User repo gitignore -Only these folders are tracked in the `user/` Git repo: `pages/`, `config/`, `accounts/`, `themes/`. The `plugins/` and `data/` folders are excluded. +Only these folders are tracked in the `user/` Git repo: `pages/`, `config/`, `accounts/`, `themes/`. The `plugins/` and `data/` folders are excluded — **except** the three site-owned plugins, which are un-ignored explicitly (see "Custom plugins" below). Also ignored: the test accounts, `italy-2026-demo` pages, secrets (`config/plugins/git-sync.yaml`, `config/security.yaml`, `api-private.php`), and the whole `env/` override tree. ### Dual-repo submodule structure @@ -253,3 +285,21 @@ Every plan in `docs/working/plans/` must have a `**Status:**` line immediately a **When asked what's open:** surface `Not started` and `In progress` plans. Show `Deferred` plans but label them clearly. Omit `Complete` and `Abandoned` unless explicitly asked. **When finishing a plan:** update the `**Status:**` field in the plan file to `✅ Complete (YYYY-MM-DD)` before closing the session. This applies whether execution was done by Claude directly, via the superpowers:executing-plans skill, or via superpowers:subagent-driven-development. + +## 3. Testing + +**The dev server must be running** (`make start`) — every suite drives the live site over HTTP. + +| Command | Scope | +|---|---| +| `make test` | Everything: `test-config` → `test-post` → `test-ui` | +| `make test-config` | Form/config sanity via `scripts/test-form-config.sh` | +| `make test-post` | End-to-end post submission via `scripts/test-post.sh` | +| `make test-ui` | Playwright suite (`npx playwright test`) | + +- **Test account is automatic.** `test-post` and `test-ui` depend on `test-account`, which creates a `testrunner` admin (password `Testpass1234`) inside the container if absent. It is git-ignored — never commit it, and keep the password free of shell/Make/URL-special characters since several consumers interpolate it. +- **Playwright layout:** config at `playwright.config.js`, specs under `tests/ui/` (`a11y`, `auth`, `dailies`, `gpx`, `home`, `maps`, `nav`, `post`, `stories`, `trip`), shared helpers in `tests/ui/helpers.js`, global setup/teardown in `tests/`. +- **Auth is a dependency project.** `auth.setup.js` runs first and writes `tests/.auth/user.json`; the `chromium` project reuses it as `storageState`. Don't add per-test logins. +- **Base URL:** defaults to `http://localhost:8081`; override with `GRAV_BASE_URL` (required when testing a worktree's isolated server on `8090+`). +- Single spec / focused run: `npx playwright test tests/ui/maps` (add `--headed` to watch). `retries: 0` and screenshots-on-failure only, so a failure is a real failure. +- **`window.tripMap` / `window.homeMap` are asserted by the map specs** — any surface using the `entry-map` partial must keep assigning them (see "One map path" above).