diff --git a/docs/superpowers/plans/2026-06-21-documentation-restructure.md b/docs/superpowers/plans/2026-06-21-documentation-restructure.md new file mode 100644 index 0000000..09d7720 --- /dev/null +++ b/docs/superpowers/plans/2026-06-21-documentation-restructure.md @@ -0,0 +1,1097 @@ +# Documentation Restructure Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Restructure `docs/` from an organic flat layout into a type-first hierarchy (`guides/`, `reference/`, `working/`, `research/`) serving two personas: Mischa and Claude. + +**Architecture:** All 49 existing files move via `git mv` to preserve history. New content is written for four guides and one architecture reference. CLAUDE.md is trimmed and extended with superpowers skill path overrides. Four memory files are updated to reflect new paths. + +**Tech Stack:** git (file moves), bash (sed for cross-reference fixes), markdown. + +## Global Constraints + +- All file moves use `git mv` — never `mv` — so git history is preserved. +- The existing spec file (`docs/superpowers/specs/2026-06-21-documentation-restructure-design.md`) is itself one of the files being moved — move it in Task 1 with the rest. +- Do NOT modify any file inside `user/` — that is a separate git repo. +- Do NOT touch memory files outside of Task 9. +- CLAUDE.md lives at repo root and stays there. +- Commit after every task. Commit messages: `docs: `. + +--- + +### Task 1: Scaffold new folder structure and move all existing files + +**Files:** +- Create directories: `docs/guides/`, `docs/reference/`, `docs/working/specs/`, `docs/working/plans/`, `docs/working/qa/`, `docs/working/milestones/`, `docs/research/` +- Move: all 49 existing files under `docs/` to their new locations + +- [ ] **Step 1: Create the new directory tree** + +```bash +mkdir -p /home/mischa/Nextcloud/Projects/travel-blog-intotheeast/docs/guides +mkdir -p /home/mischa/Nextcloud/Projects/travel-blog-intotheeast/docs/reference +mkdir -p /home/mischa/Nextcloud/Projects/travel-blog-intotheeast/docs/working/specs +mkdir -p /home/mischa/Nextcloud/Projects/travel-blog-intotheeast/docs/working/plans +mkdir -p /home/mischa/Nextcloud/Projects/travel-blog-intotheeast/docs/working/qa +mkdir -p /home/mischa/Nextcloud/Projects/travel-blog-intotheeast/docs/working/milestones +mkdir -p /home/mischa/Nextcloud/Projects/travel-blog-intotheeast/docs/research +``` + +- [ ] **Step 2: Move specs/ and plans/ (bulk)** + +```bash +cd /home/mischa/Nextcloud/Projects/travel-blog-intotheeast + +# All specs +for f in docs/superpowers/specs/*.md; do + git mv "$f" "docs/working/specs/$(basename "$f")" +done + +# All plans +for f in docs/superpowers/plans/*.md; do + git mv "$f" "docs/working/plans/$(basename "$f")" +done +``` + +- [ ] **Step 3: Move milestone specs** + +```bash +cd /home/mischa/Nextcloud/Projects/travel-blog-intotheeast +git mv docs/milestone-1-spec.md docs/working/milestones/milestone-1.md +git mv docs/milestone-2-spec.md docs/working/milestones/milestone-2.md +git mv docs/milestone-3-spec.md docs/working/milestones/milestone-3.md +git mv docs/milestone-4-spec.md docs/working/milestones/milestone-4.md +``` + +- [ ] **Step 4: Move QA docs** + +```bash +cd /home/mischa/Nextcloud/Projects/travel-blog-intotheeast +git mv docs/qa-test-plan.md docs/working/qa/test-plan.md +git mv docs/qa-results.md docs/working/qa/results.md +``` + +- [ ] **Step 5: Move remaining working/ docs** + +```bash +cd /home/mischa/Nextcloud/Projects/travel-blog-intotheeast +git mv docs/backlog.md docs/working/backlog.md +git mv docs/bugs-and-fixes.md docs/working/bugs-and-fixes.md +git mv docs/pm-analysis.md docs/working/pm-analysis.md +git mv docs/production-todo.md docs/working/production-todo.md +git mv docs/summary.md docs/working/summary.md +``` + +- [ ] **Step 6: Move research docs** + +```bash +cd /home/mischa/Nextcloud/Projects/travel-blog-intotheeast +git mv docs/research-polarsteps.md docs/research/polarsteps.md +git mv docs/research-findpenguins.md docs/research/findpenguins.md +git mv docs/research-story-editing.md docs/research/story-editing.md +``` + +- [ ] **Step 7: Move design system to reference/** + +```bash +cd /home/mischa/Nextcloud/Projects/travel-blog-intotheeast +git mv docs/design/design-spec.md docs/reference/design-system.md +rmdir docs/design # now empty +``` + +- [ ] **Step 8: Move posting pipeline to guides/ (base for Task 4)** + +```bash +cd /home/mischa/Nextcloud/Projects/travel-blog-intotheeast +git mv docs/posting-pipeline.md docs/guides/posting.md +``` + +- [ ] **Step 9: Verify — no files remain at docs/ root, no docs/superpowers/ exists** + +```bash +find /home/mischa/Nextcloud/Projects/travel-blog-intotheeast/docs -maxdepth 1 -type f +# Expected: nothing (docs/README.md doesn't exist yet — will be created in Task 3) + +ls /home/mischa/Nextcloud/Projects/travel-blog-intotheeast/docs/ +# Expected: guides/ reference/ research/ working/ + +ls /home/mischa/Nextcloud/Projects/travel-blog-intotheeast/docs/working/specs/ | wc -l +# Expected: 15 (14 original + the restructure design spec) + +ls /home/mischa/Nextcloud/Projects/travel-blog-intotheeast/docs/working/plans/ | wc -l +# Expected: 19 (18 original + this plan) +``` + +- [ ] **Step 10: Commit** + +```bash +cd /home/mischa/Nextcloud/Projects/travel-blog-intotheeast +git add -A +git commit -m "docs: restructure docs/ into guides/ reference/ working/ research/" +``` + +--- + +### Task 2: Fix all internal cross-references + +**Files:** +- Modify: all files under `docs/working/` and `docs/research/` that reference old paths + +After the moves, links inside plan and spec files still point to old paths like `docs/superpowers/plans/...` and `docs/milestone-1-spec.md`. This task fixes them all. + +- [ ] **Step 1: Replace docs/superpowers/specs/ references** + +```bash +cd /home/mischa/Nextcloud/Projects/travel-blog-intotheeast +grep -rl "docs/superpowers/specs/" docs/ | xargs sed -i 's|docs/superpowers/specs/|docs/working/specs/|g' +``` + +- [ ] **Step 2: Replace docs/superpowers/plans/ references** + +```bash +cd /home/mischa/Nextcloud/Projects/travel-blog-intotheeast +grep -rl "docs/superpowers/plans/" docs/ | xargs sed -i 's|docs/superpowers/plans/|docs/working/plans/|g' +``` + +- [ ] **Step 3: Replace docs/superpowers/ catch-all (any remaining bare references)** + +```bash +cd /home/mischa/Nextcloud/Projects/travel-blog-intotheeast +grep -rl "docs/superpowers/" docs/ | xargs sed -i 's|docs/superpowers/|docs/working/|g' +``` + +- [ ] **Step 4: Replace milestone spec references** + +```bash +cd /home/mischa/Nextcloud/Projects/travel-blog-intotheeast +grep -rl "docs/milestone-1-spec" docs/ | xargs sed -i 's|docs/milestone-1-spec\.md|docs/working/milestones/milestone-1.md|g' +grep -rl "docs/milestone-2-spec" docs/ | xargs sed -i 's|docs/milestone-2-spec\.md|docs/working/milestones/milestone-2.md|g' +grep -rl "docs/milestone-3-spec" docs/ | xargs sed -i 's|docs/milestone-3-spec\.md|docs/working/milestones/milestone-3.md|g' +grep -rl "docs/milestone-4-spec" docs/ | xargs sed -i 's|docs/milestone-4-spec\.md|docs/working/milestones/milestone-4.md|g' +``` + +- [ ] **Step 5: Replace posting-pipeline reference** + +```bash +cd /home/mischa/Nextcloud/Projects/travel-blog-intotheeast +grep -rl "docs/posting-pipeline" docs/ | xargs sed -i 's|docs/posting-pipeline\.md|docs/guides/posting.md|g' +``` + +- [ ] **Step 6: Fix references inside CLAUDE.md** + +```bash +cd /home/mischa/Nextcloud/Projects/travel-blog-intotheeast +sed -i 's|docs/superpowers/specs/|docs/working/specs/|g' CLAUDE.md +sed -i 's|docs/superpowers/plans/|docs/working/plans/|g' CLAUDE.md +sed -i 's|docs/superpowers/|docs/working/|g' CLAUDE.md +``` + +- [ ] **Step 7: Verify no old paths remain** + +```bash +cd /home/mischa/Nextcloud/Projects/travel-blog-intotheeast +grep -r "docs/superpowers/" docs/ CLAUDE.md +# Expected: no output +grep -r "docs/milestone-[0-9]-spec" docs/ CLAUDE.md +# Expected: no output +grep -r "docs/posting-pipeline" docs/ CLAUDE.md +# Expected: no output +``` + +- [ ] **Step 8: Commit** + +```bash +cd /home/mischa/Nextcloud/Projects/travel-blog-intotheeast +git add -A +git commit -m "docs: fix all internal cross-references after restructure" +``` + +--- + +### Task 3: Create docs/README.md (navigation index) + +**Files:** +- Create: `docs/README.md` + +- [ ] **Step 1: Write docs/README.md** + +Create `/home/mischa/Nextcloud/Projects/travel-blog-intotheeast/docs/README.md` with this content: + +```markdown +# docs/ + +## If you're Mischa + +**Doing something operational?** → [`guides/`](guides/) +- [Posting a journal entry](guides/posting.md) +- [Managing GPX files](guides/gpx-manager.md) +- [Switching to a new trip](guides/trip-switching.md) +- [Rebuilding local dev from scratch](guides/local-setup.md) + +**Checking project status?** → [`working/`](working/) +- [Backlog](working/backlog.md) +- [Production todo](working/production-todo.md) +- [QA results](working/qa/results.md) + +**Design or architecture decisions?** → [`reference/`](reference/) +- [Design system](reference/design-system.md) +- [Architecture overview](reference/architecture.md) + +--- + +## If you're Claude + +**Always-loaded project rules** → [`CLAUDE.md`](../CLAUDE.md) (repo root) + +**Active specs and plans** → [`working/specs/`](working/specs/) and [`working/plans/`](working/plans/) + +**Stable facts** → [`reference/`](reference/) + +**Raw research input** → [`research/`](research/) +``` + +- [ ] **Step 2: Verify** + +```bash +cat /home/mischa/Nextcloud/Projects/travel-blog-intotheeast/docs/README.md +# Expected: the full content above, no truncation +``` + +- [ ] **Step 3: Commit** + +```bash +cd /home/mischa/Nextcloud/Projects/travel-blog-intotheeast +git add docs/README.md +git commit -m "docs: add README.md navigation index" +``` + +--- + +### Task 4: Write docs/guides/posting.md + +**Files:** +- Modify: `docs/guides/posting.md` (was `posting-pipeline.md`, moved in Task 1 — reformat into a user-facing guide) + +The moved `posting-pipeline.md` is a technical reference. Reformat it as a step-by-step guide for Mischa, preserving all factual content, adding a quick-start at the top and a troubleshooting section at the bottom. + +- [ ] **Step 1: Rewrite docs/guides/posting.md** + +Replace the full content of `docs/guides/posting.md` with: + +```markdown +# Posting a Journal Entry + +Two ways to post: the **mobile form** at `/post` (quick, phone-friendly) or the **Admin panel** at `/admin` (drafts, scheduling, editing). + +--- + +## Quick start — mobile form + +1. Open `/post` on your phone (login required) +2. Fill in **Title** and **Content** (required) +3. Tap **Get Location** → fills Lat/Lng automatically +4. Tap **Get Weather** → fills weather fields using your coordinates +5. Type **City** and **Country** (optional but nice) +6. Attach photos (optional) — first photo becomes the hero image +7. Tap **Submit** → entry appears in the feed immediately + +--- + +## Form fields reference + +| Field | Required | Notes | +|---|---|---| +| Title | ✅ | Entry headline | +| Content | ✅ | Markdown body | +| Date | ✅ | Defaults to now — adjust if posting later | +| Lat / Lng | — | Filled by Get Location; used for map marker | +| City | — | Shown as `📍 Kyoto, Japan` on feed cards | +| Country | — | Combined with City in location badge | +| Weather | — | Filled by Get Weather (Open-Meteo, free, no key) | +| Photos | — | All uploaded files appear in the gallery; first = hero | + +**Weather descriptions** (must be one of these if entered manually): +`Sunny` · `Partly cloudy` · `Cloudy` · `Foggy` · `Drizzle` · `Rain` · `Snow` · `Thunderstorm` + +--- + +## How it works (for debugging) + +``` +Browser → /post (post-form.md) + └─ Grav Form plugin validates fields + └─ add-page-by-form plugin + ├─ reads pageconfig.parent (/trips//dailies) + ├─ writes user/pages/01.trips//01.dailies//entry.md + └─ moves uploaded photos into the page folder + └─ cache-on-save plugin + └─ calls $grav['cache']->deleteAll() → entry visible immediately + └─ form shows success message +``` + +**Slug format:** `-.entry` +Example: `2026-07-20-0930-first-day-in-kyoto.entry` + +**Entry folder structure:** +``` +user/pages/01.trips/japan-korea-2026/01.dailies/ +└─ 2026-07-20-0930-first-day-in-kyoto.entry/ + ├─ entry.md ← frontmatter + markdown body + ├─ temple.jpg ← hero image (or set hero_image in frontmatter) + └─ market.jpg ← additional gallery image +``` + +--- + +## Admin panel — drafts and scheduling + +Use the Admin panel at `/admin` for drafts, scheduled posts, or editing existing entries. + +1. Log in at `/admin` +2. **Pages → Add Page** +3. Set **Parent Page** to `/trips//dailies` and **Template** to `entry` +4. Fill in the **Entry** tab (city, country, lat/lng, weather) +5. Write content in the **Content** tab +6. Upload photos in the **Media** tab +7. **Drafts:** set `published: false` — won't appear until you flip it to `true` +8. **Scheduling:** set `publish_date` in **Options → Scheduling** +9. Save + +The Admin form fields are defined by `user/themes/intotheeast/blueprints/entry.yaml`. + +--- + +## Frontmatter reference + +Every entry supports these frontmatter fields: + +| Field | Type | Notes | +|---|---|---| +| `title` | string | Required | +| `date` | datetime | Format: `Y-m-d H:i` (e.g. `2026-06-17 10:00`) | +| `template` | string | Always `entry` | +| `published` | bool | `true` to show in feed | +| `lat` | string | Decimal degrees (e.g. `52.3676`) | +| `lng` | string | Decimal degrees (e.g. `4.9041`) | +| `location_city` | string | e.g. `Kyoto` | +| `location_country` | string | e.g. `Japan` | +| `weather_desc` | string | One of the allowed values above | +| `weather_temp_c` | number | Celsius, displayed rounded | +| `hero_image` | string | Filename to pin as hero (e.g. `temple.jpg`); auto-selects first image if blank | + +--- + +## Troubleshooting + +**Entry doesn't appear in feed after submit** +→ Check that `active_trip` in `user/config/site.yaml` matches the parent in `user/pages/02.post/post-form.md` (`pageconfig.parent`). If they're out of sync, entries go to the wrong folder. See [trip switching guide](trip-switching.md). + +**Get Weather button shows an error** +→ Fill in Lat/Lng first (tap Get Location or enter manually). Open-Meteo requires coordinates. + +**Photos not showing in gallery** +→ Verify files were uploaded (check the entry folder in Admin → Media). Only jpg, jpeg, png, webp, gif are rendered. + +**500 error after posting** +→ Run `make fix-perms` to restore container file ownership. +``` + +- [ ] **Step 2: Verify** + +```bash +grep -c "Quick start" /home/mischa/Nextcloud/Projects/travel-blog-intotheeast/docs/guides/posting.md +# Expected: 1 + +grep -c "Troubleshooting" /home/mischa/Nextcloud/Projects/travel-blog-intotheeast/docs/guides/posting.md +# Expected: 1 +``` + +- [ ] **Step 3: Commit** + +```bash +cd /home/mischa/Nextcloud/Projects/travel-blog-intotheeast +git add docs/guides/posting.md +git commit -m "docs: rewrite posting guide as user-facing step-by-step" +``` + +--- + +### Task 5: Write docs/guides/gpx-manager.md + +**Files:** +- Create: `docs/guides/gpx-manager.md` + +- [ ] **Step 1: Write docs/guides/gpx-manager.md** + +Create `/home/mischa/Nextcloud/Projects/travel-blog-intotheeast/docs/guides/gpx-manager.md` with this content: + +```markdown +# Managing GPX Files + +GPX route files live as media on the active trip page. The map picks them up automatically — any `.gpx` file in `user/pages/01.trips//` appears on the trip map. + +--- + +## Browser UI — /gpx-manager + +The GPX manager at `/gpx-manager` requires admin login (redirects to login form if not authenticated). + +### Upload a file + +1. Open `/gpx-manager` (login required) +2. Click **Choose file** → select your `.gpx` file +3. Click **Upload** +4. The filename is auto-slugified before upload: spaces and special characters become hyphens, everything becomes lowercase. + - Example: `Day 1 — Arrival (Kyoto).gpx` → `day-1-arrival-kyoto.gpx` +5. The file appears in the list immediately + +### Delete a file + +1. Find the file in the list at `/gpx-manager` +2. Click **Delete** next to it +3. Confirm — the file is removed from the trip media and will no longer appear on the map + +--- + +## Without the browser UI + +Drop the file directly into the trip folder and push: + +```bash +cp your-route.gpx /path/to/user/pages/01.trips/japan-korea-2026/ +make content-push +``` + +`make content-push` commits and pushes the `user/` repo to Gitea, which triggers a production pull via webhook. + +**Filename tip:** slug your filename before dropping it — lowercase, hyphens only: +``` +day-1-kyoto.gpx ✅ +Day 1 Kyoto.gpx ⚠️ works but slugified on upload; skip this if dropping manually +``` + +--- + +## Filename slugification rules + +The browser UI slugifies client-side before upload. Manually placed files are used as-is, so name them cleanly. + +Rules applied by the UI: +- Lowercase everything +- Replace spaces with hyphens +- Replace non-alphanumeric characters (except `.`) with hyphens +- Collapse multiple consecutive hyphens to one +- Strip leading/trailing hyphens + +--- + +## Komoot workflow (no API integration yet) + +Komoot doesn't offer GPX export via API without authentication. Current workaround: + +1. Open your tour in the Komoot app or website +2. **More → Export → GPX track** (available on Komoot Premium; free users get a limited version) +3. Save the `.gpx` file to your phone or laptop +4. Upload via `/gpx-manager` or drop into the trip folder + +Future: a Komoot integration field in the GPX manager (paste tour URL → server fetches GPX) is in the backlog at [`working/backlog.md`](../working/backlog.md). + +--- + +## How files are served + +GPX files are registered as a valid media type in `user/config/media.yaml`, so Grav stores and serves them alongside images. The map template picks them up via: + +```twig +{% for file in trip_page.media.all %} + {% if file.filename ends with '.gpx' %} + {# add to map source list #} + {% endif %} +{% endfor %} +``` + +No manual linking is needed — upload and it appears. +``` + +- [ ] **Step 2: Verify** + +```bash +grep -c "slugif" /home/mischa/Nextcloud/Projects/travel-blog-intotheeast/docs/guides/gpx-manager.md +# Expected: >= 3 + +grep -c "Komoot" /home/mischa/Nextcloud/Projects/travel-blog-intotheeast/docs/guides/gpx-manager.md +# Expected: >= 3 +``` + +- [ ] **Step 3: Commit** + +```bash +cd /home/mischa/Nextcloud/Projects/travel-blog-intotheeast +git add docs/guides/gpx-manager.md +git commit -m "docs: add GPX manager guide" +``` + +--- + +### Task 6: Write docs/guides/trip-switching.md + +**Files:** +- Create: `docs/guides/trip-switching.md` + +- [ ] **Step 1: Write docs/guides/trip-switching.md** + +Create `/home/mischa/Nextcloud/Projects/travel-blog-intotheeast/docs/guides/trip-switching.md` with this content: + +```markdown +# Switching to a New Trip + +When you start a new trip, **two files must be updated together** — if only one is changed, new entries will be posted to the wrong folder silently (no error, wrong trip). + +--- + +## Checklist + +- [ ] Update `user/config/site.yaml` → `active_trip` +- [ ] Update `user/pages/02.post/post-form.md` → `pageconfig.parent` +- [ ] Create the new trip page tree (see below) +- [ ] Run `make content-push` to push the changes to production + +--- + +## Step 1 — Update site.yaml + +In `user/config/site.yaml`, set `active_trip` to the new trip slug: + +```yaml +active_trip: japan-korea-2026 # ← change this +``` + +The slug must exactly match the folder name under `user/pages/01.trips/`. + +--- + +## Step 2 — Update post-form.md + +In `user/pages/02.post/post-form.md`, set `pageconfig.parent` to the new dailies path: + +```yaml +pageconfig: + parent: /trips/japan-korea-2026/dailies # ← change this +``` + +**Why both?** Grav's config and page frontmatter are static YAML — no variable substitution is possible, so `post-form.md` can't read from `site.yaml` automatically. They must match manually. + +**What breaks if they're out of sync:** `active_trip` controls which trip page is featured on the home page and trip page. `pageconfig.parent` controls where new entries land. If they differ, new posts go to the old trip's dailies folder while the home page shows the new trip — entries appear to vanish. + +--- + +## Step 3 — Create the new trip page tree + +Create the standard four subfolders under `user/pages/01.trips//`: + +``` +user/pages/01.trips/japan-korea-2026/ +├─ trip.md ← title, date_start, date_end, cover_image, album_url +├─ 01.dailies/ +│ └─ dailies.md ← template: dailies (list page) +├─ 02.map/ +│ └─ map.md ← template: map +├─ 03.stats/ +│ └─ stats.md ← template: stats +└─ 04.stories/ + └─ stories.md ← template: stories +``` + +Copy these files from an existing trip and update the frontmatter (especially `title` and `date_start` in `trip.md`). + +Fields in `trip.md` to update: + +| Field | Example | Notes | +|---|---|---| +| `title` | `Japan & Korea 2026` | Displayed in nav and trip header | +| `date_start` | `2026-07-15` | Used for "X days on the road" stat | +| `date_end` | *(leave blank while travelling)* | Set when you return | +| `cover_image` | `cover.jpg` | Shown on the trips listing page | +| `album_url` | `https://...` | Optional external album link | + +--- + +## Step 4 — Push + +```bash +make content-push +``` + +This commits and pushes the `user/` repo to Gitea. The webhook triggers a production pull automatically. + +--- + +## Verify + +After pushing, check: +1. Home page shows the new trip (title and date) +2. Submit a test entry via `/post` — verify it lands under `user/pages/01.trips//01.dailies/` +3. Map at `/trips//map` shows the correct (empty or GPX-only) state +``` + +- [ ] **Step 2: Verify** + +```bash +grep -c "checklist\|Checklist" /home/mischa/Nextcloud/Projects/travel-blog-intotheeast/docs/guides/trip-switching.md +# Expected: >= 1 + +grep -c "pageconfig.parent" /home/mischa/Nextcloud/Projects/travel-blog-intotheeast/docs/guides/trip-switching.md +# Expected: >= 2 +``` + +- [ ] **Step 3: Commit** + +```bash +cd /home/mischa/Nextcloud/Projects/travel-blog-intotheeast +git add docs/guides/trip-switching.md +git commit -m "docs: add trip switching guide" +``` + +--- + +### Task 7: Write docs/guides/local-setup.md + update CLAUDE.md + +**Files:** +- Create: `docs/guides/local-setup.md` +- Modify: `CLAUDE.md` — replace §2 with pointer; add superpowers skill path overrides + +- [ ] **Step 1: Write docs/guides/local-setup.md** + +Create `/home/mischa/Nextcloud/Projects/travel-blog-intotheeast/docs/guides/local-setup.md` with this content: + +```markdown +# Local Development Setup + +This guide covers setting up the dev environment from scratch after cloning the repo. + +--- + +## First-time setup + +`user/plugins/` and `user/data/` are excluded from git but Grav requires them to exist. Create them once: + +```bash +mkdir -p user/plugins user/data +``` + +Then run: + +```bash +make setup +``` + +`make setup` = `build → start → install-plugins → fix-perms`. This builds the Docker image (Grav 2.0 baked in), starts the container, installs all plugins from `plugins.txt`, and fixes file ownership. + +The dev server runs at **http://localhost:8081**. + +--- + +## After any docker compose down + +Always run `make setup` — not just `make start` — to ensure permissions are correct. + +`docker compose restart` (soft restart) preserves the image and is fine for quick restarts. Only `make setup` is needed after `docker compose down`. + +--- + +## Fix 500 errors after plugin install + +If the site returns a 500 error after plugin installation or after recreating the container: + +```bash +make fix-perms +``` + +This creates uid 1000 in the container, chowns `/var/www/html` to 1000:1000, and reloads Apache. + +--- + +## Upgrading to a newer Grav RC + +Grav 2.0 is baked into the custom Docker image via `Dockerfile`. The base `getgrav/grav` image ships 1.7 — the `Dockerfile` downloads the 2.0 RC bundle from GitHub and overwrites the core files at build time. + +To upgrade: +1. Update the bundle URL in `Dockerfile` +2. Run `make setup` — Docker rebuilds the image layer automatically + +--- + +## Required system.yaml settings (Grav 2.0) + +After upgrading, verify these are set in `user/config/system.yaml`: + +```yaml +accounts: + type: flex # required for Admin2 API +pages: + type: flex # required for Admin2 pages API +``` + +--- + +## Admin user API permissions + +The admin user account needs `api.*` permissions for Admin2. In `user/accounts/.yaml`: + +```yaml +access: + admin: + login: true + super: true + api: + super: true + access: true +``` + +--- + +## Disable the old admin plugin + +Both `admin` and `admin2` route to `/admin` and conflict. After installing `admin2`, disable the old one: + +In `user/plugins/admin/admin.yaml`: +```yaml +enabled: false +``` + +--- + +## JWT secret + +Leave `jwt_secret: ''` in `user/plugins/api/api.yaml`. It works for local dev; production installs generate a secure secret automatically during `make remote-install`. + +--- + +## Language URL prefix + +If Grav redirects to `/en/...` URLs, ensure `user/config/system.yaml` contains: + +```yaml +languages: + supported: [en] + include_default_lang: false +``` + +Without `include_default_lang: false`, Grav adds a language prefix to all URLs even for single-language sites. +``` + +- [ ] **Step 2: Replace CLAUDE.md §2 with pointer and add skill path overrides** + +In `CLAUDE.md`, replace the entire `## 2. Local development setup` section (from `## 2.` through the end of the Language URL prefix block) with: + +```markdown +## 2. Local development setup + +Full setup guide: [`docs/guides/local-setup.md`](docs/guides/local-setup.md) + +### Superpowers skill paths + +Specs: `docs/working/specs/YYYY-MM-DD--design.md` +Plans: `docs/working/plans/YYYY-MM-DD-.md` + +The brainstorming and writing-plans skills default to `docs/superpowers/`; these lines override that default. +``` + +- [ ] **Step 3: Verify CLAUDE.md** + +```bash +grep -c "Local development setup" /home/mischa/Nextcloud/Projects/travel-blog-intotheeast/CLAUDE.md +# Expected: 1 + +grep -c "Superpowers skill paths" /home/mischa/Nextcloud/Projects/travel-blog-intotheeast/CLAUDE.md +# Expected: 1 + +grep -c "docs/working/specs" /home/mischa/Nextcloud/Projects/travel-blog-intotheeast/CLAUDE.md +# Expected: 1 + +# Confirm the long §2 content is gone +grep -c "First-time setup after cloning" /home/mischa/Nextcloud/Projects/travel-blog-intotheeast/CLAUDE.md +# Expected: 0 +``` + +- [ ] **Step 4: Verify local-setup.md** + +```bash +grep -c "make setup" /home/mischa/Nextcloud/Projects/travel-blog-intotheeast/docs/guides/local-setup.md +# Expected: >= 3 + +grep -c "include_default_lang" /home/mischa/Nextcloud/Projects/travel-blog-intotheeast/docs/guides/local-setup.md +# Expected: 1 +``` + +- [ ] **Step 5: Commit** + +```bash +cd /home/mischa/Nextcloud/Projects/travel-blog-intotheeast +git add docs/guides/local-setup.md CLAUDE.md +git commit -m "docs: extract local setup guide from CLAUDE.md; add skill path overrides" +``` + +--- + +### Task 8: Write docs/reference/architecture.md + +**Files:** +- Create: `docs/reference/architecture.md` + +- [ ] **Step 1: Write docs/reference/architecture.md** + +Create `/home/mischa/Nextcloud/Projects/travel-blog-intotheeast/docs/reference/architecture.md` with this content: + +```markdown +# Architecture Overview + +How the intotheeast site hangs together. + +--- + +## Stack + +| Layer | Technology | Notes | +|---|---|---| +| CMS | Grav 2.0.0-rc.10 | Flat-file PHP CMS; no database | +| Admin | Admin2 v2.0.0-rc.15 | Plugin slug: `admin2` (not `admin`) | +| Container | Docker (`getgrav/grav` base + custom `Dockerfile`) | Grav 2.0 baked in at build time | +| PHP session | `session.save_path = /tmp` | Set in `php/php-local.ini` | +| Dev URL | http://localhost:8081 | Mapped from container port 80 | +| Maps | MapLibre GL JS | Replaced Leaflet; all 3 map templates use it | +| GPX rendering | maplibre-gl-leaflet-gpx (CDN) | Renders GPX files as route layers | + +--- + +## Plugin roles + +The posting pipeline is a chain of three plugins: + +``` +Browser POST /post +│ +├─ Grav Form plugin (built-in) +│ └─ validates required fields; handles file uploads +│ +├─ add-page-by-form (third-party, patched) +│ └─ reads post-form.md config: +│ ├─ pageconfig.parent → target folder (e.g. /trips/japan-korea-2026/dailies) +│ ├─ pageconfig.slug_field → slug from date + title +│ └─ pagefrontmatter → template: entry, published: true +│ └─ writes entry.md to user/pages/01.trips//01.dailies/.entry/ +│ └─ moves uploaded photos into the page folder +│ +└─ cache-on-save (custom, user/plugins/cache-on-save/) + └─ calls $grav['cache']->deleteAll() on every new-entry form submission + └─ ensures entries appear in feed immediately in both dev and prod mode +``` + +Other notable plugins: + +| Plugin | Role | +|---|---| +| `login` | Auth for /post and /gpx-manager | +| `api` (Grav API v1) | Used by /gpx-manager to list/upload/delete GPX files | +| `admin2` | Admin panel at /admin | + +--- + +## Template hierarchy + +All page templates extend `base.html.twig`: + +``` +templates/ +├─ base.html.twig ← site shell: nav, fonts, CSS tokens +├─ default.html.twig ← extends base; generic page +├─ home.html.twig ← extends base; context-aware two-column layout +├─ trip.html.twig ← extends base; trip page with filter bar (All/Journal/Stories) +├─ entry.html.twig ← extends base; single journal entry (gallery, badges, map) +├─ dailies.html.twig ← extends base; journal feed list +├─ map.html.twig ← extends base; full-height MapLibre trip map +├─ stats.html.twig ← extends base; trip stats (days, distance, elevation) +├─ stories.html.twig ← extends base; stories grid +├─ story.html.twig ← extends base; single story (Ken Burns hero, shortcodes) +└─ gpx-manager.html.twig ← extends base; admin UI for GPX file management +``` + +Partials live in `templates/partials/`. Key partials: `entry-card.html.twig` (feed card), `map-init.html.twig` (shared MapLibre bootstrap). + +--- + +## Trip entity structure + +The site is organized around Trip entities. The active trip is set in `user/config/site.yaml` → `active_trip`. + +``` +user/pages/01.trips/ +└─ japan-korea-2026/ + ├─ trip.md ← template: trip; title, date_start, cover_image, album_url + ├─ *.gpx ← GPX route files (served as page media; auto-detected by map.html.twig) + ├─ 01.dailies/ ← journal entries (template: dailies list + entry children) + ├─ 02.map/map.md ← template: map + ├─ 03.stats/stats.md ← template: stats + └─ 04.stories/ ← story pages (template: stories list + story children) +``` + +--- + +## GPX data flow + +``` +GPX file uploaded to trip page media + │ + ▼ +user/pages/01.trips//*.gpx + │ + ▼ +map.html.twig: trip_page.media.all → filter .gpx files → pass as JS array + │ + ▼ +MapLibre source: each GPX file added as a GeoJSON source via maplibre-gl-leaflet-gpx + │ + ▼ +Connector suppression: same-file 10km proximity check prevents spurious inter-track segments + │ (override with force_connect: true in trip frontmatter) + ▼ +Rendered as route polyline on map +``` + +--- + +## Data flow for a post submission + +``` +1. User fills /post form and taps Submit +2. Grav Form plugin validates: title and content required +3. add-page-by-form reads post-form.md: + pageconfig.parent: /trips/japan-korea-2026/dailies + pageconfig.slug: {date}-{title|slugify} + pagefrontmatter: template: entry, published: true +4. New page written to: + user/pages/01.trips/japan-korea-2026/01.dailies/ + └─ 2026-07-20-0930-first-day-in-kyoto.entry/ + └─ entry.md +5. Photos moved into the same folder +6. cache-on-save calls $grav['cache']->deleteAll() +7. Browser: form shows success message +8. Feed at /trips/japan-korea-2026 immediately shows new entry +``` + +--- + +## Key config files + +| File | Purpose | +|---|---| +| `user/config/site.yaml` | `active_trip` slug; site title/description | +| `user/config/system.yaml` | Twig cache, flex accounts/pages, language prefix | +| `user/config/media.yaml` | Registers `.gpx` as a valid media type | +| `user/plugins/api/api.yaml` | `session_enabled: true` for GPX manager auth | +| `user/themes/intotheeast/css/tokens.css` | Design tokens (colors, fonts, spacing) | +| `CLAUDE.md` | Project rules and always-loaded context for Claude | +``` + +- [ ] **Step 2: Verify** + +```bash +grep -c "Plugin roles\|Template hierarchy\|GPX data flow\|Data flow for a post" \ + /home/mischa/Nextcloud/Projects/travel-blog-intotheeast/docs/reference/architecture.md +# Expected: 4 +``` + +- [ ] **Step 3: Commit** + +```bash +cd /home/mischa/Nextcloud/Projects/travel-blog-intotheeast +git add docs/reference/architecture.md +git commit -m "docs: add architecture overview reference" +``` + +--- + +### Task 9: Update memory files + +**Files:** +- Modify: 4 memory files in `~/.claude/projects/-home-mischa-Nextcloud-Projects-travel-blog-intotheeast/memory/` + +Four memory files still reference `docs/superpowers/` paths. Update them to `docs/working/`. + +- [ ] **Step 1: Identify affected lines** + +```bash +MEMORY_DIR="/home/mischa/.claude/projects/-home-mischa-Nextcloud-Projects-travel-blog-intotheeast/memory" +grep -n "docs/superpowers" "$MEMORY_DIR/MEMORY.md" "$MEMORY_DIR/feedback-plan-execution-gap.md" \ + "$MEMORY_DIR/project-story-mode-and-maplibre.md" "$MEMORY_DIR/project-homepage-redesign.md" +``` + +- [ ] **Step 2: Apply sed replacements** + +```bash +MEMORY_DIR="/home/mischa/.claude/projects/-home-mischa-Nextcloud-Projects-travel-blog-intotheeast/memory" +for f in "$MEMORY_DIR/MEMORY.md" "$MEMORY_DIR/feedback-plan-execution-gap.md" \ + "$MEMORY_DIR/project-story-mode-and-maplibre.md" "$MEMORY_DIR/project-homepage-redesign.md"; do + sed -i 's|docs/superpowers/specs/|docs/working/specs/|g' "$f" + sed -i 's|docs/superpowers/plans/|docs/working/plans/|g' "$f" + sed -i 's|docs/superpowers/|docs/working/|g' "$f" +done +``` + +- [ ] **Step 3: Verify no old paths remain in memory** + +```bash +MEMORY_DIR="/home/mischa/.claude/projects/-home-mischa-Nextcloud-Projects-travel-blog-intotheeast/memory" +grep -r "docs/superpowers" "$MEMORY_DIR/" +# Expected: no output +``` + +- [ ] **Step 4: Commit memory project memory update to docs repo** + +Memory files are not in the git repo, so no commit needed. But update the memory pointer in MEMORY.md to reference the plans stored at their new location. + +Verify MEMORY.md now reads correctly: + +```bash +grep "plans\|specs\|superpowers\|working" \ + "/home/mischa/.claude/projects/-home-mischa-Nextcloud-Projects-travel-blog-intotheeast/memory/MEMORY.md" +# Expected: all plan/spec references point to docs/working/, none to docs/superpowers/ +``` + +- [ ] **Step 5: Final verification — complete restructure** + +```bash +cd /home/mischa/Nextcloud/Projects/travel-blog-intotheeast + +# No files at docs/ root except README.md +find docs -maxdepth 1 -type f +# Expected: docs/README.md only + +# Four subdirectories only +ls docs/ +# Expected: guides/ reference/ research/ working/ + +# All guides present +ls docs/guides/ +# Expected: gpx-manager.md local-setup.md posting.md trip-switching.md + +# Reference docs present +ls docs/reference/ +# Expected: architecture.md design-system.md + +# No old paths anywhere +grep -r "docs/superpowers" docs/ CLAUDE.md +# Expected: no output + +# Skill path overrides in CLAUDE.md +grep "docs/working/specs\|docs/working/plans" CLAUDE.md +# Expected: 2 lines +``` + +- [ ] **Step 6: Commit final state** + +```bash +cd /home/mischa/Nextcloud/Projects/travel-blog-intotheeast +git add -A +git commit -m "docs: complete documentation restructure — all tasks done" +```