| name | route-makeover |
| description | Scan an entire SvelteKit project's routes and generate a full design-makeover epic — a dependency-wired task tree that runs the impeccable treatment (critique → audit → findings-driven fixes → polish → re-critique) against every high-value route, gated behind a shared design-system foundation phase. Also supports append mode: when a feature batch adds new routes after the epic is already running, use the Append mode section to add cluster tasks to the existing epic (foundation already done — no F1/F2/F3 needed). Use when the user wants to redesign/overhaul the whole app's UI, give all routes the impeccable treatment, plan a design firm's work across the app, or add new-route tasks to an existing makeover epic. Pairs with the impeccable skill and jat:tasktree. |
Turn a whole SvelteKit app into a structured design makeover: scan every route → cluster + tier them → write a PRD → materialize an impeccable-treatment epic (epic + foundation tasks + one task per route cluster, fully dependency-wired).
This skill orchestrates two others: the impeccable design skill (the per-route treatment) and jat:tasktree (the epic/task materialization). It does not redesign routes itself — it produces the plan and the tasks that do.
When to use
The user wants the whole app (or a tier of it) redesigned, not a single screen. Signals: "give all our routes the design treatment", "full design makeover", "plan the redesign across the app", "turn our routes into a design epic", "hand the design firm a task tree".
For a single page/feature, use $impeccable craft/shape/critique directly instead.
Preflight (gates — pass before generating anything)
| Gate | Check | If fail |
|---|
| SvelteKit | src/routes/ (or --root) exists | Ask for the routes root; abort if not a SvelteKit app. |
| impeccable | The impeccable skill is reachable (vendored at .agents/skills/impeccable/ or installed) | Tell the user the makeover depends on it; stop. |
| Context | A real design-authority exists: PRODUCT.md/DESIGN.md, or the project's own equivalent (a design-system doc + its token/state-color/component sources). | If none, run $impeccable teach/document (or hand-author from the existing token/component files), then resume. Becomes F1 regardless — but you need enough now to set register + archetype. |
| Tasks | jt CLI available (run bare jt for usage — note jt create has no --help, it would make a junk task). | Tell the user tasks can't be created; offer to emit the PRD only. |
Procedure
1. Scan
Run the bundled scanner from the project root:
node .agents/skills/route-makeover/scripts/scan-routes.mjs --plan docs/route-plan.json
It writes docs/ROUTES.md (the inventory) and docs/route-plan.json (structured clusters with a proposed tier ∈ 1–4 and register ∈ brand|product per cluster). Read the plan JSON in full. Counts and tier distribution are in .counts.
Flags: --root <dir> (non-default routes path), --out <md> (inventory path), --no-md (skip the inventory), --plan <path> (JSON output; omit → stdout).
2. Plan with judgment
First name the project archetype — it decides register, tier meaning, and guardrails, and the scanner cannot infer it:
- Brand / marketing SaaS (landing + app; a JST-template project): the scanner's
brand first-impression + money-path tiers apply; register is a brand/product mix; use the JST-style guardrails (white-label token-only, multi-theme proof, test on the real deployment).
- Internal tool / dashboard / admin / dev-tooling (e.g. the JAT IDE itself): there are no marketing/brand/checkout surfaces — register is essentially all
product, and the scanner's brand/money tiers are meaningless. It will dump almost everything into one tier-4 "Other" bucket and mislabel entry/search pages as fake tier-1 "brand". Throw out its tiers and re-tier by operator value: T1 daily-drivers → T2 frequent tools → T3 regular tools → T4 secondary. Guardrails come from the project's own design system, not a white-label token policy (see reference/treatment.md → "Adapting guardrails per archetype").
Then apply judgment to the clusters themselves — the scanner's clustering is a proposal, not the answer:
- Re-cluster; don't trust the buckets. For dashboard/internal apps the scanner emits one giant "Other" cluster — decompose it by functional domain (the screens an operator thinks of together) and by shared-component leverage (routes rendering the same core components redesign together). Ground this in what each route actually is — a quick read of each
+page.svelte (purpose, LOC, primary components) beats guessing; an Explore/general-purpose agent can characterize all routes in one fan-out.
- Merge clusters that share a flow; split any cluster too big for one 2–8h session. Size each so one agent can hold its diagnose→fix loop in context.
- Confirm register per cluster against the archetype + any
PRODUCT.md register field; override per surface (a marketing hero inside a product app is still brand).
- Drop redirect-only / stub / deprecated / already-perfect routes — they have no design surface (the scanner may still list them; e.g.
/-redirect, legacy /old → /new, catch-all 404).
Then confirm scope with the user via AskUserQuestion (skip only if they already specified): which tiers to materialize now (1–4 full, 1–2 first, or 1 pilot), and confirm the one-task-per-cluster granularity (the right default — keeps the diagnose→fix loop in one agent's context; the per-command alternative fragments it).
3. Write the PRD
Using reference/treatment.md (the treatment sequence, register policy, guardrails, DoD, foundation phase, and PRD template), write the PRD to docs/prds/<slug>-design-treatment.md — or match the project's existing PRD location/naming convention if it has one (e.g. the JAT IDE keeps PRDs at ide/docs/prd-*.md). Fill the template from the plan + scope answers. The per-cluster task list is the (judgment-adjusted) clusters, ordered by tier. Always include the 3 foundation tasks (F1/F2/F3).
Adapt the guardrails, DoD, and foundation sections to the archetype — see reference/treatment.md → "Adapting guardrails per archetype". JST defaults (token-only/no-oklch-literals, multi-theme white-label proof, jatui reuse, test on the real deployment) are for brand SaaS; an internal tool swaps in its own design system as the authority and tests on its own dev server. State the swap explicitly in the PRD so cluster agents don't apply the wrong policy.
4. Materialize the epic (tasktree)
Follow jat:tasktree. The mechanics below are battle-tested — jt's task-tree behavior has sharp edges that silently corrupt the tree:
⚠️ jt gotchas:
jt create has no --help — jt create --help makes a junk task titled "--help". Run bare jt for usage.
- Epic children get DOTTED ids
<epic>.N (e.g. jat-65q2g.4). jt create --parent E … prints Created jat-65q2g.4: <title>. Capture the id by parsing that line — not by grepping jat-[a-z0-9]+, which stops at the dot and hands you the parent every time. jt create --json dumps the whole object with no clean top-level id line, so the plain-output parse is easier.
jt delete <id> with the un-dotted epic id deletes the epic (orphaning its .N children under a dead prefix). Always delete the full dotted id, and re-jt show the epic after any delete.
- On postgres-backed projects,
--parent sets the parent pointer but does not wire epic←child dependency edges — wire every edge manually.
Recipe (capture ids to a temp file — shell state does not persist across separate tool calls; sleep between creates to dodge create-flake):
idof(){ sed -n 's/^Created \([^:]*\):.*/\1/p' | head -1; }
E=$(jt create "Epic: Design makeover — <project> routes" --type epic --priority 1 \
--labels "design,impeccable,frontend" --description "…PRD path + 'stays open, blocked by all children'…" 2>&1 | idof)
echo "E=$E" > /tmp/makeover-ids.env
out=$(jt create "F1 — …" --type task --priority 0 --parent "$E" --description "…" 2>&1)
echo "F1=$(printf '%s' "$out" | idof)" >> /tmp/makeover-ids.env
source /tmp/makeover-ids.env
for c in $F1 $F2 $F3 $C1 … $Cn; do jt dep add "$E" "$c"; done
jt dep add "$F2" "$F1"; jt dep add "$F3" "$F1"
for c in $C1 … $Cn; do jt dep add "$c" "$F2"; jt dep add "$c" "$F3"; done
Do not close the epic. It stays open + blocked by all children; it becomes the cross-cluster consistency/UAT verification task when they complete.
5. Verify + report
source /tmp/makeover-ids.env
jt show "$E" --json
jt show "$F1" --json
jt show "$C1" --json
Verify gating from each task's depends_on (entries look like {id,type:"blocks",…}), not from jt ready:
⚠️ On postgres, jt ready returns all open tasks (dependency filtering is SQLite-only), so it cannot confirm "only F1 is ready". Trust jt show --json → depends_on instead. (Reads can lag the cache briefly; re-check if a freshly-wired edge is missing.)
Print the tasktree banner: total tasks, P0/P1/P2 counts, the F1 → {F2,F3} → clusters dependency graph, and the execution strategy (F1 solo → F2+F3 parallel → fan out by tier). Tell the user to start at E.1 (/jat:start E.1).
Append mode (new routes after the epic is running)
Use when a feature batch lands (harvest epic, new vertical module, etc.) and adds routes that weren't covered by the original epic. The foundation is already done — skip F1/F2/F3 and add children directly to the existing epic.
A1. Find the existing epic and baseline
jt search "impeccable design treatment" --limit 5 --json | jq -r '.[] | "\(.id) [\(.status)] \(.title)"'
Note the epic id (E). Check which cluster tasks are already closed — those routes are covered. Open children are in-flight.
A2. Surface new routes via git
The scanner can't know what's "new" — use git to find page routes added since the last run:
git log --since="<date>" --diff-filter=A --name-only --pretty=format:"--- %h %as %s" \
-- "src/routes/**" | grep -v "^---" | grep -v "^$" | grep -E "\+page\.(svelte|server\.ts|ts)$" | sort
Cross-reference against the existing cluster task descriptions: a route already named in a closed cluster is covered. A route not mentioned anywhere is new. Routes that fall in the same functional area as a closed cluster but weren't listed (e.g. new sub-pages under an already-treated module) count as new unless the cluster description was broad enough to subsume them — read it and judge.
The scanner excludes API endpoints, cron, webhooks, feeds, and health routes — apply the same exclusion here. Drop redirect-only +page.svelte stubs too.
A3. Cluster the new routes
Group by functional domain using the same sizing rule: one agent should be able to hold the diagnose→fix loop for all routes in a cluster in one session. Typical clusters: 3–8 page routes. Split if larger; merge if a domain has only 1–2 tiny pages.
Confirm register per cluster (brand vs product — see reference/treatment.md).
A4. Create tasks as children of the existing epic
No new epic needed. No F1/F2/F3 — foundation is done.
idof(){ sed -n 's/^Created \([^:]*\):.*/\1/p' | head -1; }
E="<existing-epic-id>"
sleep 0.4
N1=$(jt create "Impeccable N1 — <area>" \
--type task --priority 1 --parent "$E" \
--description "Routes (register: <brand|product>):
- /path/to/route — description
- /path/to/other — description
Treatment (one session):
1. \$impeccable critique <route> → Nielsen score + P0-P3 issues
2. \$impeccable audit <route> → /20
3. Findings-driven fixes (<product: layout clarify harden distill> | <brand: typeset colorize layout bolder>)
4. \$impeccable polish
5. Re-critique (target ≥34/40, audit ≥18/20)
Guardrails: <paste project guardrails from the existing PRD>.
DoD: critique ≥34/40, audit ≥18/20, zero AI-slop, all states, build clean.
PRD: <path to existing PRD>" 2>&1 | idof)
echo "N1=$N1"
A5. Wire epic → new children
for c in $N1 $N2 … $Nn; do
jt dep add "$E" "$c"
sleep 0.3
done
No dep on F1/F2/F3 — they're closed. New tasks are immediately ready.
A6. Verify
jt show "$N1" --json | jq '{id:.id, status:.status, blocked_by:.blocked_by, depends_on:.depends_on}'
blocked_by: [epic] with an empty depends_on is correct — it means the epic depends on this task (not that the epic blocks the task from starting). This is the same pattern the original T1–Tn tasks had.
Print a short banner: N new tasks added, clusters listed, tell the user to start any with /jat:start <id> (all parallelize).
Notes
- One task per cluster, full sequence embedded is the default and the best-product choice. Resist one-task-per-command: critique emits the fix commands the same session runs, and splitting loses that context across sessions.
- Foundation gates routes because shared-system work — tokens, the component primitives, the app shells — propagates to every route. Never let per-route polish run before F2/F3.
- Shared primitives carry the foundation. On component-heavy apps a handful of primitives (cards, tables, badges, the skeleton/loading family) render across many routes, so F2 fixing them improves a dozen routes at once — per-cluster tasks then become mostly layout/IA/state work, not rebuilds. Lean on this when sizing clusters, and enumerate those primitives explicitly in F2.
$impeccable may be vendored as individual skills. Some projects (e.g. the JAT IDE) install the family as standalone /critique, /audit, /polish, /typeset, … with no $impeccable umbrella — invoke each directly. Check what's actually installed before writing commands into task descriptions.
- The scanner excludes API endpoints, cron, webhooks, feeds/manifests/health, and component demo galleries — those have no design surface. It does not detect redirect/stub
+page.sveltes (e.g. a goto() redirect) — you must drop those in step 2.
- Re-running the scanner is safe and idempotent; regenerate
docs/ROUTES.md after routes change.