Files
intotheeast-com/docs/working/plans/2026-06-21-documentation-restructure.md
T

1098 lines
35 KiB
Markdown

# 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: <what changed>`.
---
### 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/<active_trip>/dailies)
├─ writes user/pages/01.trips/<active_trip>/01.dailies/<slug>/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:** `<YYYY-MM-DD-HHmm>-<slugified-title>.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/<active_trip>/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/<active_trip>/` 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/<new-slug>/`:
```
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/<new-slug>/01.dailies/`
3. Map at `/trips/<new-slug>/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/<username>.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-<topic>-design.md`
Plans: `docs/working/plans/YYYY-MM-DD-<topic>.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/<trip>/01.dailies/<slug>.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/<slug>/*.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"
```