Add /ia-review and /spec-drift-review process skills

Captures two recurring review processes as repeatable Claude skills so
they don't have to be reinvented from scratch each time:

- /ia-review: walks the route tables + nav surfaces, builds the
  sitemap, flags drift, and writes/refreshes
  docs/information-architecture.md. Refresh mode preserves the user's
  accumulated decisions and implementation backlog while only
  rewriting the snapshot sections.

- /spec-drift-review: walks every spec in openspec/specs/, compares
  it to shipped code, and produces a categorized drift report
  (high/medium/low severity) plus code-without-spec findings and
  structural suggestions (split/merge/new specs, CAPABILITIES.md
  catch-up). Excludes specs touched by active openspec changes since
  that mismatch is intended in-flight work, not drift.

Also extends the IA doc with the items resolved/deferred during the
review (observations 1-10), three new streams (D: drop redundant Feed
button on logged-in /, E: break Settings apart, F: propose /explore
spec), and an "Open exploration" section for the routes-vs-activities
terminology question.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ullrich Schäfer 2026-04-26 08:41:07 +02:00
parent ad11624c83
commit a847ff2e52
3 changed files with 648 additions and 33 deletions

View file

@ -0,0 +1,275 @@
---
name: ia-review
description: Take a snapshot of the apps' information architecture (sitemap, navigation, audience-gating per route) and write or refresh `docs/information-architecture.md`. Use when the user wants to review the IA, plan a navigation/surface redesign, or check for drift since the last review.
license: MIT
metadata:
author: trails.cool
version: "1.0"
---
Walk the apps' route table + navigation surfaces, build a sitemap, surface
tensions and open questions, and capture the result in
`docs/information-architecture.md` (fresh write or refresh against the
prior snapshot).
The output is a *review document for the user* — not a unilateral plan.
Decisions are made by the user during the conversation that follows; the
doc captures the snapshot and the open questions that need answering.
---
## When to use
- The user explicitly asks for an IA review ("review the IA", "what's
the information architecture look like").
- Before a navigation/surface redesign, so the redesign is informed by
a current snapshot rather than a vibe.
- After a chunk of new features — pages, modals, settings sections —
to check whether the IA is drifting (busy navbar, duplicated surfaces,
orphaned routes).
- When the user says "we should do another IA review" after the prior
doc has aged.
---
## Steps
1. **Detect mode (fresh vs refresh)**
Check whether `docs/information-architecture.md` already exists.
- **No file:** fresh review. Build the snapshot from scratch.
- **File exists:** refresh review. Read it first; preserve the
decisions/backlog the user has accumulated and only update the
snapshot sections (sitemap, navigation, observations, open
questions). Decisions previously crossed out stay crossed out.
In refresh mode, also read the snapshot date at the top — anything
shipped *since* that date is what your refresh should focus on.
2. **Identify the apps in scope**
trails.cool ships two front-ends; the IA question lives mostly in
the Journal:
- `apps/journal/` — user accounts, social, content. Main IA
surface.
- `apps/planner/` — anonymous, ephemeral. ~5 routes; include for
completeness but don't dwell.
If the project structure has changed and there's a new app, include
it.
3. **Read the route tables**
For each app:
```
apps/<app>/app/routes.ts
```
This is the authoritative URL → route-file mapping. Both apps use
explicit registration (per CLAUDE.md), so `routes.ts` is complete.
4. **Read the navigation surfaces**
- `apps/journal/app/root.tsx` (and `apps/planner/app/root.tsx` if
it has navigation) — the top navbar lives here. Read both the
loader (to see what data the navbar consumes — counts, badges,
user fields) and the `NavBar` component (to see what entries
render).
- `apps/journal/app/components/Footer.tsx` — the footer.
- Any auth-gate / Terms-gate logic in the root loader.
5. **Sample key route loaders to understand audience**
For each top-level route, scan its loader to determine:
- Does it require a session? (loaders typically `redirect("/auth/login")`
for anonymous visitors when so.)
- Does it serve different content per session? (e.g., `home.tsx`
branches on `user`.)
- Does it have an access rule beyond auth? (locked-account 404s,
visibility checks, etc.)
You don't need to read every route — pick the top-level ones and
any that look like they might gate differently than the URL hints.
6. **Build the sitemap**
Group routes by audience: **Public surface** (anonymous-reachable)
and **Authenticated surface** (signed-in only). Within each group,
order by topic (auth, profile, content, settings, legal, etc.).
Use plain code blocks with one URL per line and a one-line gloss
per entry. Keep the format scannable; don't repeat what the URL
already says.
7. **Map navigation surfaces**
Two short tables/snippets:
- **Navbar (signed-in)** — entries left-to-right.
- **Navbar (signed-out)** — entries left-to-right.
- **Footer** — links + any meta text.
Note any entry whose visibility is conditional (badge counts, etc.).
8. **Identify "feed concepts" and other duplications**
trails.cool has historically had multiple feed-like surfaces. Any
IA review should ask: how many lists of activities are there? Is
the same data reachable from multiple URLs? Is there a URL that
shows different products to different audiences?
Capture these in a small table or section if they exist.
9. **Map cross-app linking**
Journal ↔ Planner cross-links (JWT callback URLs, "Try the
Planner" buttons, etc.). One short list.
10. **List observations**
Walk the snapshot and call out tensions worth discussing. Useful
prompts:
- **Busy clusters** — three or more controls for the same concept
side-by-side in the navbar.
- **Redundant paths** — same destination reachable from multiple
surfaces with no clear reason.
- **Dead-end routes** — pages reachable only by typing the URL,
no in-app link.
- **Missing surfaces** — common user need with no in-app path
(e.g., "find people to follow" with no `/explore`).
- **Audience mismatch** — same URL serving meaningfully different
products to anon vs auth.
- **Visual inconsistency** — adjacent navbar entries with
different treatment (icon vs text, different baselines).
- **Mobile hazards** — clusters that will wrap badly under
small viewport widths.
Each observation should be one short paragraph. Each is a
question for the user to answer, not a decision you've made.
11. **List open IA questions**
Distinct from observations: these are larger directional choices
where the answer determines what other observations even matter.
Examples: "Should `/` and `/feed` merge for signed-in users?",
"Where does an `/explore` page live, if at all?", "Mobile
pattern — hamburger? Bottom tab bar?"
Keep these as bullets the user can answer in one line each.
12. **Write the doc**
Output to `docs/information-architecture.md`. Use this top
structure:
```markdown
# Information Architecture Review
*Snapshot date: YYYY-MM-DD.* If the navbar, route table, or feed
model has shifted since then, treat this doc as stale and refresh
against `apps/journal/app/routes.ts` + `apps/journal/app/root.tsx`.
A snapshot of where every page lives, who sees it, and how visitors
navigate between them. Intended for review — flag anything that
doesn't make sense or should change.
## Apps
[...]
## Journal sitemap
### Public surface (logged-out)
[...]
### Authenticated surface (logged-in)
[...]
### Navigation surfaces
[...]
## Logged-in vs logged-out home
[...if `/` does double duty...]
## [Any "N feed concepts" / duplication sections]
[...]
## Cross-app linking
[...]
## Planner sitemap
[...short...]
## Observations worth discussing
[...]
## Open IA questions
[...]
```
Use today's date for the snapshot. Reference the source-of-truth
files at the top so the next review knows what to compare against.
13. **In refresh mode, preserve the user's accumulated decisions**
The prior doc may already contain:
- Resolved observations (struck through with a *Resolved: ...*
note).
- An "Implementation backlog" section with streams.
- An "Open exploration" section.
These are the *user's work*, not the snapshot. Carry them forward
untouched unless one is plainly obsolete (e.g., the feature it
references no longer exists). When in doubt, leave it and let the
user prune.
If a previously-flagged observation is no longer present in the
current code (e.g., it was implemented), update its status note
rather than removing it — preserves history.
14. **Surface a ranked next-action list**
After writing the doc, summarize in 46 lines what changed since
the prior review (or what the most actionable observations are if
fresh). End with a question: which open IA question does the user
want to tackle first?
Don't start implementation work — this skill is for the
*snapshot*. The decisions and the implementation backlog grow
through the conversation that follows.
---
## What this skill is NOT
- **Not an implementation skill.** Don't write code, don't open PRs.
The doc is the deliverable; decisions and code follow in normal
conversation.
- **Not a unilateral redesign.** Observations are questions for the
user. Don't bake "decisions" into the snapshot — those go in the
backlog only when the user has actually answered the question.
- **Not a spec change.** OpenSpec specs describe what's shipped; this
doc describes the IA *as it stands* with tensions flagged. Any
resulting spec updates happen during implementation, not during the
review.
---
## Guardrails
- Always include the snapshot date at the top of the output doc — IA
drifts; future-you needs to know whether to trust the snapshot or
refresh it.
- Reference the source-of-truth files (`routes.ts`, `root.tsx`) so the
next review's diff is mechanical.
- Keep observations as questions, not decrees. The user makes the
call.
- In refresh mode, preserve the user's accumulated decisions verbatim.
Only the snapshot sections are yours to rewrite.
- Don't invent routes or features. If you can't find evidence for it
in the code, don't put it in the snapshot.
- Keep it scannable. The doc is for review; verbose explanations bury
the signal.

View file

@ -0,0 +1,216 @@
---
name: spec-drift-review
description: Walk every spec in `openspec/specs/`, compare it to the shipped code, and produce a categorized drift report (high/medium/low severity per spec, plus code-without-spec findings and structural suggestions). Use when the user wants to check spec drift, after a chunk of features has shipped, or when planning a spec catch-up PR.
license: MIT
metadata:
author: trails.cool
version: "1.0"
---
Walk the specs directory + the shipped code, compare them claim by
claim, and produce a structured drift report. The report is the
deliverable; fixes happen in a follow-up PR after the user has reviewed
the findings.
The goal is to keep `openspec/specs/` honest — specs are useless if they
don't describe the actual product, and worse than useless if they
contradict it.
---
## When to use
- The user explicitly asks to check spec drift ("are the specs in sync",
"review specs against code").
- After a multi-feature chunk has shipped without per-feature spec
promotion — drift accumulates fastest in catch-up phases.
- Before reorganizing the specs directory (split / merge / rename).
- When a spec contradicts the codebase and you're not sure which is
right.
---
## Steps
1. **Get the lay of the land**
Run these in parallel:
```bash
ls openspec/specs/
ls openspec/changes/ # in-flight work — NOT drift
cat openspec/CAPABILITIES.md # if it exists, it's the index
openspec list --json # any active changes that explain drift
```
**Important:** any spec referenced in an active openspec change is
*expected* to drift from current code — that drift is the work in
progress. Note these and exclude them from the report.
2. **Identify the code-side anchors per spec**
For each spec at `openspec/specs/<capability>/spec.md`, find the
primary code locations that implement it. Most capability specs map
to one or more of:
- **Routes:** `apps/journal/app/routes/*.tsx` / `*.ts`
authoritative for URL behavior, redirects, access gating.
- **Server lib:** `apps/journal/app/lib/<topic>.server.ts` — most
business logic.
- **Schema:** `packages/db/src/schema/journal.ts` — table shapes,
visibility values, defaults, indexes.
- **i18n:** `packages/i18n/src/locales/{en,de}.ts` — user-facing
strings, often telling.
- **Tests:** integration tests are a great oracle for the *intended*
behavior; mismatch with the spec usually means the spec is stale.
Don't read every file. Pick the 13 anchors per spec that the
requirements most plausibly map to.
3. **Compare claim by claim**
For each requirement in a spec, ask:
- **Does the code do this?** If not, is it because the requirement
was retired or because it was never shipped?
- **Does the code do *more* than this?** New scenarios shipped
without a spec update.
- **Does the code do this *differently*?** Different URL,
different default, different status code, different rule.
- **Does this requirement reference a name that no longer exists?**
Renamed routes, deleted helpers, removed tables.
Track findings with severity:
| Severity | What it means |
|----------|---------------|
| **High** | Spec actively misleads — claims a behavior the code does not exhibit. A reader implementing against the spec would write the wrong code. |
| **Medium** | Spec is incomplete — code has scenarios the spec doesn't describe. Reader gets less information than they should but isn't actively misled. |
| **Low** | Wording drift — comments/cross-refs mention a renamed thing, but the requirement statements are still accurate. |
4. **Find code-without-spec**
Walk the route tree in `apps/journal/app/routes.ts` and the topic
files in `apps/journal/app/lib/`. For each top-level concept ask:
- Is this concept covered by a spec?
- If yes, does the spec mention this surface?
- If no, should it be?
Genuine "no spec" cases are usually: new feature shipped without
spec promotion, or piece of infrastructure deemed too internal for a
spec. Both are valid; flag the former, leave the latter alone.
5. **Structural review**
Spec organization itself can drift:
- **Specs that grew too big** — multiple unrelated requirements
under one spec. Candidates for a split (we previously split
`account-settings` into `profile-settings`, `account-management`,
`connected-services`).
- **Specs that overlap** — the same requirement appears in two
specs, or one spec keeps cross-referencing another. Candidate for
a merge or a clearer ownership boundary.
- **Specs that should exist but don't** — a capability is shipped
and substantial enough to merit its own spec but is currently
squeezed into another. (We added `sse-broker` and `notifications`
this way.)
- **`CAPABILITIES.md` drift** — if the index exists, check that
every spec dir has an entry and every entry points at a real
spec. The index is the easy thing to forget when adding a spec.
6. **Compose the report**
Output to the conversation as a markdown structure. Don't write a
doc unless the report is unusually large and the user asks for one —
most drift reports get acted on inside a single PR and don't need a
long-lived artifact.
Suggested structure:
```markdown
# Spec drift review — YYYY-MM-DD
**Active changes excluded from this review:**
- <name> (touches: <specs>)
## High-severity drift
### `<spec-name>`
- **Requirement: <name>** says <X>; code at `<file:line>` does <Y>.
[link / one-line action]
- …
## Medium-severity drift
### `<spec-name>`
- <description + code anchor>
- …
## Low-severity drift (wording / cross-refs)
- `<spec-name>`: <description>
- …
## Code-without-spec
- <feature> at `<file>` — should this be in <spec-name>, or a new
spec? Recommendation: <…>
## Structural suggestions
- Split: <spec-name><new-1>, <new-2>
- Merge: <spec-a> + <spec-b><new>
- New spec: <name> covering <area>
- `CAPABILITIES.md` updates: <list>
```
7. **Propose next actions, then stop**
End the review with three concrete options the user can pick from:
- **Ship a catch-up PR for everything** — works when drift is
mostly low/medium and the fix is mechanical.
- **Fix high-severity first, defer the rest** — works when high
items are urgent and the rest can wait for natural per-feature
spec updates.
- **Restructure first, then catch up** — works when structural
suggestions (split / merge / new spec) are large enough that
fixing claims inside the wrong spec shape would just have to be
redone.
Don't pick for them. Don't start fixing yet.
---
## What this skill is NOT
- **Not a fixer.** This skill produces a report. Apply happens after
the user has decided which findings to act on.
- **Not a CI check.** It's interactive — judgment calls (severity,
splits, merges) are part of the value, not an automation target.
- **Not for in-flight work.** Active openspec changes legitimately
cause spec/code mismatch; flag them in the "excluded" section and
move on.
- **Not a code review.** The question is "does the spec match the
code", not "is the code good." Code-quality observations belong in
PR review, not here.
---
## Guardrails
- Always start by reading active openspec changes — drift caused by
in-flight work is not drift.
- Don't write spec edits during the review. The user picks which
findings to act on; edits happen after.
- When code and spec disagree, the prevailing rule on this project is
**code is source of truth** unless the user says otherwise. Surface
the conflict; don't preemptively decide.
- Skip generated/scaffold files (e.g. `.react-router/types/**`) — they
are derived, not product.
- Don't pad the report with low-severity wording drift unless the user
asks for it. Concentrate signal.
- Keep observations specific (file + line + claim), not abstract. A
finding without an anchor isn't actionable.