本文へ移動
cccskills
無料GitHub で公開

new-route-scaffold

Scaffold recipe for a new authenticated route: SSR shell, DB-derived types, Redux hydration, view sub-pages. Use when creating a new route or page, adding a feature with a sidebar/main layout, adding view modes to a route, or feature types don't match database.types.ts.

インストール方法を見る

含まれるファイル(4)

  • SKILL.md15.3 KB
  • coming-soon-placeholder.md1.3 KB
  • file-templates.md13.7 KB
  • route-architecture.md8.4 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

New Route Scaffold — app/(core)/

This skill captures the exact workflow used to build the original /notes route (now app/(core)/notes, since rebuilt as a client workspace — see route-architecture.md). Follow it for every new authenticated route. Many decisions require information only Arman has — ask explicitly before assuming anything.

Companion files — read at the step that needs them

  • Writing any scaffold file in Phases 1–4 → copy its template from file-templates.md. Every code template (types, data.ts, route files, hydrators) lives there, keyed by phase.
  • Route UI not built yet (placeholder page.tsx) → read coming-soon-placeholder.md.
  • Full shell implementations, data flow, anti-patterns, mobile, more questions for Arman → route-architecture.md.
  • A route needs agent-aware context or bindings → invoke surface-authoring; this includes agent-native routes, where outside helpers may receive an approved task-appropriate snapshot while native and resident agents remain isolated from ambient inheritance.

STOP: Questions to Ask Before Writing a Single Line

Never assume the following — always ask Arman:

  1. Layout shape — How many panels? Sidebar width? Resizable? What goes in the header area?
  2. View modes — Does this route have sub-views (like edit/preview/split)? What are they named?
  3. Group-by / filter dimensions — How is the sidebar list organized? What FK relationships drive grouping?
  4. Data fetch scope — What fields does the list view need? What triggers a "full" fetch?
  5. Redux slice — Does one already exist? Should we create one or extend an existing slice?
  6. Existing types — Do the feature types already exist? Are they derived from the DB or hand-written (likely stale)?
  7. Navigation entry — Does features/shell/constants/nav-data.ts already have an entry? What favicon color/letter (constants/favicon-route-data.ts)?
  8. Mobile behavior — Any mobile-specific overrides beyond standard rules?

If anything is unclear, stop and ask. Do not invent layout details, field names, or group-by modes.


Phase 1: Audit & Fix Types

Before touching any route file, verify the feature type file matches types/database.types.ts.

Step 1 — Find the DB Row shape

# Search for the table definition in the generated types
grep -A 30 '"your_table": {' types/database.types.ts

Step 2 — Compare against the feature type file

Located at features/[feature]/types.ts. Hand-written types are almost always missing fields. The canonical pattern:

Template → copy the canonical features/[feature]/types.ts pattern (aliases, working interface, _CompatCheck guard, list projection, route enums) from file-templates.md § Phase 1.

Step 3 — Update Redux notes.types.ts factory functions

The createBlankNoteRecordFromPartial and createAutogeneratedNoteRecord functions hardcode default values for every field. After adding fields to the interface, update both factories to include the new fields — otherwise TypeScript will error. Key rule: new nullable fields default to null, not "" or [].


Phase 2: Server Data Layer

Create lib/[feature]/data.ts. This is the only place server-side DB queries live for this route.

Template → copy lib/[feature]/data.ts (list seed, single entity, preload helper) from file-templates.md § Phase 2.

Critical rules:

  • import "server-only" is mandatory — it prevents accidental client import
  • Every function must be wrapped in React cache() — layout, generateMetadata, and page all call the same function; cache() collapses them to one DB hit. 'use cache' is NOT available (cacheComponents off; build error)
  • 🚨 Never notFound() on an empty single-record read (authInterrupts is ON) — under RLS, empty means deleted, missing, denied, or signed out. The single-record read returns null; the route renders <AccessGate token id/> (live: app/(core)/pick-lists/[id]/page.tsx) or refuses with requireAccess(type, id, level, { forbid: true }). Read features/access-gate/FEATURE.md first
  • The preload pattern starts a fetch before an await chain, eliminating waterfalls

Phase 3: Route Files

File map

app/(core)/[feature]/
├── layout.tsx          ← static metadata only
├── loading.tsx         ← dimension-exact skeleton of the full shell
├── error.tsx           ← <ErrorBoundaryView context="[Feature]" /> one-liner
├── page.tsx            ← fetches list seed, hydrates Redux, renders shell
└── [id]/
    ├── layout.tsx      ← parallel fetch, generateMetadata, BOTH hydrators, shell
    ├── loading.tsx     ← skeleton of the content area only (not the full shell)
    ├── error.tsx       ← <ErrorBoundaryView context="[Feature] Detail" /> one-liner
    ├── not-found.tsx   ← malformed-URL 404 only; an empty record read renders <AccessGate>
    ├── page.tsx        ← redirect to default view
    ├── edit/layout.tsx ← titlePrefix sub-layout (Phase 6), one per view that needs its own tab title
    ├── edit/page.tsx
    ├── split/page.tsx
    ├── rich/page.tsx
    ├── md/page.tsx
    ├── preview/page.tsx
    └── diff/page.tsx

error.tsx (route root and [id]/)

Every error boundary is a one-liner — never build error UI inline.

"use client";
import { ErrorBoundaryView } from "@/components/errors/ErrorBoundaryView";

export default function MyFeatureError({ error, reset }: { error: Error & { digest?: string }; reset: () => void }) {
    return <ErrorBoundaryView error={error} reset={reset} context="My Feature" />;
}

Optional props: context (string label for console logs), homePath (override home button path, default "/").

ErrorBoundaryView shows all users a polished error UI with retry/back/home actions. Admins get a collapsible debug panel with full error details, request context, user context, stack trace, raw JSON dump, and a "Copy for AI" button that strips minified chunk URLs and formats a clean Markdown summary for pasting into any AI chat.


page.tsx — placeholder for routes under development

Only when the route's real UI isn't built yet → read coming-soon-placeholder.md (ComingSoonPage usage and the four props you must always set).


layout.tsx (route root)

Static metadata only. No data fetching, no children manipulation.

Template → file-templates.md § Phase 3.

loading.tsx (route root)

Must mirror the exact dimensions of the shell. Every element has explicit h- and w-. No content-derived sizing. See route-architecture.md for the full skeleton example from the notes route.

Key rules:

  • Outer container: h-full flex overflow-hidden (AppShell .shell-main is already full viewport — do not subtract header height)
  • Route chrome: <PageHeader> — never an in-body faux header bar
  • Sidebar: w-[280px] shrink-0 — must match the real aside exactly
  • No spinners — <Skeleton> components only
  • [id]/loading.tsx mirrors the content area only (layout persists across navigation)

page.tsx (route root — index, no item selected)

Template → file-templates.md § Phase 3 (list seed + hydrator + Suspense + empty state).

[id]/layout.tsx — the most important file

This is where both hydrators live and parallel fetching happens.

Template → file-templates.md § Phase 3 (generateMetadata, preloadNote + Promise.all, <AccessGate> on an empty read, both hydrators, shell).

[id]/page.tsx — redirect to default view

Template → file-templates.md § Phase 3.

View sub-pages ([id]/edit/page.tsx, etc.)

Sub-pages export no metadata and fetch no data. A view that needs its own tab title gets a sibling layout.tsx with titlePrefix (Phase 6) — never a page-level { title }.

Templates → file-templates.md § Phase 3 (the titlePrefix sub-layout, the single-panel view, and the split view that passes both panels).


Phase 4: Redux Hydrators

Both live in features/[feature]/route/. They render null and dispatch synchronously during the first render pass — not in useEffect.

Templates → copy ListHydrator + EntityHydrator from file-templates.md § Phase 4.

Why useRef and NOT useEffect: useEffect fires after paint — children reading from the store see empty state for one frame and flash. The useRef guard dispatches during the render pass, before any child reads.


Phase 5: Shell Components

All shell components are Server Components. Client Component islands are pushed as deep as possible — only the interactive parts get "use client".

Component tree:

[Feature]Shell (Server) — h-full flex overflow-hidden (+ PageHeader for route chrome)
├── [Feature]Sidebar (Server — w-[NNpx] shrink-0 frame)         ← ask Arman for width
│   └── [Feature]SidebarClient (Client — Redux reads, interactions)
└── [Feature]MainArea (Server — flex-1 min-w-0 frame)
    ├── [Feature]TabBar (Client — open tabs from Redux)
    └── {children} per page → [Feature]ViewShell (Server)
        ├── leftPanel  (Client editor island)
        └── rightPanel (Client preview island — split mode only)

Key layout rules:

  • Outermost: h-full overflow-hidden — never h-screen, h-page, or calc(100dvh - header) on (core) routes
  • Route chrome: <PageHeader> — see features/shell/components/header/variants/USAGE.md
  • Sidebar: w-[280px] shrink-0 — ask Arman for the exact width
  • Main area: flex-1 min-w-0 overflow-hidden
  • Scroll regions: overflow-y-auto only on the inner list container
  • No inline styles unless Arman has explicitly approved (e.g., glass mode testing)

See route-architecture.md for the NotesShell / NoteViewShell composition — the pattern to build, not importable code (notes deleted its shell/ components 2026-06-24, features/notes/FEATURE.md).


Phase 6: Metadata Rules

From app/(core)/_read_first_route_rules/metadata-and-seo.md (full recipe: the route-metadata-favicons skill):

LocationWhat to useNotes
layout.tsx (root)createRouteMetadata("/path", { title, description })Provides favicon + OG for entire route
[id]/layout.tsxcreateDynamicRouteMetadata("/path", { title, description }) inside generateMetadataProvides favicon + OG for item detail
[id]/<view>/layout.tsxcreateDynamicRouteMetadata("/path", { titlePrefix: "View Name", title: entity.name, letter }) (static route: createRouteMetadata + titlePrefix)Tab reads View Name | Entity — AI Matrx. A page-level { title } renders View Name — AI Matrx and loses the name. No distinct title → no metadata; the parent layout covers favicon/OG. Live: agents/[id]/run/layout.tsx
New routeAdd favicon entry in constants/favicon-route-data.tsAsk Arman for color and letter

Phase 7: Navigation Entry

Add to features/shell/constants/nav-data.ts (label, href, iconName, section, color) if not already present; the favicon hex + letter go in constants/favicon-route-data.ts. Ask Arman for all values — icon, href, section, color (Tailwind name + hex), and favicon letter. Never guess these.


Skeleton Rules (Non-Negotiable)

Every loading.tsx must satisfy:

  1. Identical outer container — same h-, w-, flex classes as the real shell
  2. Exact fixed dimensions — every skeleton block has explicit h- and w- (no content-derived sizing)
  3. Mirror structure — sidebar skeleton has toolbar row + list items; editor skeleton has title + body lines
  4. No spinners for page content — <Skeleton> only (from @ai-matrx/design-system)
  5. [id]/loading.tsx covers only the content area, not the full shell (the layout persists)

Checklist Before Asking Arman to Review

  • DB type audit done — _CompatCheck added to types file
  • All factory functions updated with new fields
  • lib/[feature]/data.ts created with server-only, cache(), and preloadNote
  • app/(core)/[feature]/layout.tsx — static metadata only
  • app/(core)/[feature]/loading.tsx — exact dimension match to shell
  • app/(core)/[feature]/error.tsx — one-liner wrapping <ErrorBoundaryView context="[Feature]" />
  • app/(core)/[feature]/page.tsx — list seed + both hydrators + Suspense
  • app/(core)/[feature]/[id]/layout.tsx — parallel fetch + both hydrators + generateMetadata
  • app/(core)/[feature]/[id]/loading.tsx — content-area skeleton only
  • app/(core)/[feature]/[id]/error.tsx — one-liner wrapping <ErrorBoundaryView context="[Feature] Detail" />, plus not-found.tsx (malformed URLs only)
  • Empty single-record read renders <AccessGate token id/> — no notFound() anywhere in lib/[feature]/data.ts
  • app/(core)/[feature]/[id]/page.tsx — redirect to default view
  • All view sub-pages created (ask Arman for the list)
  • features/[feature]/route/ — ListHydrator + EntityHydrator
  • Shell components: Shell, Sidebar, SidebarClient, MainArea, TabBar, ViewShell, EditorPlaceholder
  • NoteEditorPlaceholder (or equivalent) clearly marked as stub for later replacement
  • Navigation entry added (or confirmed already present)
  • No useEffect in hydrators
  • No h-screen or min-h-screen anywhere — use h-dvh for marketing pages; h-full for (core) AppShell bodies
  • No inline styles (unless Arman explicitly approves for testing)
  • Lints pass on all new files

Related Skills — Read These First

These skills contain the rules this workflow is built on. When in doubt, they win:

  • ssr-zero-layout-shift — .claude/skills/ssr-zero-layout-shift/SKILL.md — server/client component boundaries, Suspense rules, hydration pattern, skeleton design, fixed-dimension containers, CLS prevention (absorbed nextjs-ssr-architecture + nextjs-app-router-expert)
  • Shell header + page height — features/shell/components/header/variants/USAGE.md — <PageHeader>, h-full, when .h-page applies
  • redux-selector-rules — .claude/skills/redux-selector-rules/SKILL.md — selector patterns, curried selector caching, avoiding re-render loops
  • core-route-headers — .claude/skills/core-route-headers/SKILL.md — invoke before writing any (core) shell or loading.tsx: <PageHeader> chrome, h-full overflow-hidden body, mobile header
  • Route rules — app/(core)/_read_first_route_rules/RULES.md — mandatory, read before every session on this route group

Additional Resources

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

Admin debug system wiring for the floating AdminIndicator's live debug data and Copy Full Context. Use when adding debug visibility to a route or feature, wiring useDebugContext or the debug panel, capturing console errors, or enabling the copy-context workflow for a page.

日本語の概要は準備中です。原文の説明を表示しています。

armanisadeghi/ai-matrx32026年10月13日 更新

Compact two-icon Copy / Copy-for-AI controls (components/agent-copy). Use when adding copy buttons to a row, card, list, or record; merging duplicate Copy/JSON/AI controls; continuing the copy rollout; or writing a Copy-for-AI payload. NOT for markdown content actions (use rich-document-actions).

日本語の概要は準備中です。原文の説明を表示しています。

armanisadeghi/ai-matrx32026年10月13日 更新

Disclosing a surface's existing fixed AI jobs in the shell's top Agents menu. Use when a page, panel, overlay, or window already runs a mandate behind a button, assist, automatic action, or mode; when the agent-disclosure guard names a file; or during a surface check.

日本語の概要は準備中です。原文の説明を表示しています。

armanisadeghi/ai-matrx32026年10月13日 更新

The watch-fix-rerun method for making an existing platform agent or automated process efficient and correct: baseline its ledger, run one unit yourself, fix the class behind every wasted call, rerun, record. Use when asked to improve, optimize, watch, or 'make efficient' an agent, a sandbox session, a sync job, or any recurring automated process.

日本語の概要は準備中です。原文の説明を表示しています。

armanisadeghi/ai-matrx32026年10月13日 更新

Redux state for agent execution and firing agent shortcuts. Use when editing features/agents/redux/, building agent UI, creating a conversation, touching assembleRequest or NDJSON stream state, wiring agent-state selectors, adding a per-conversation capability, or triggering a shortcut from a button, menu, or code (useShortcutTrigger, launchAgentExecution).

日本語の概要は準備中です。原文の説明を表示しています。

armanisadeghi/ai-matrx32026年10月13日 更新

The provision for a call site: the exhaustive menu of values that place in the code can realistically produce. Use before creating or fixing a mandate or agent that reasons about more than its own input, when deciding whether an agent can answer at all with what it is sent, or when its output 'looks right' but nobody checked it could be. NOT for building the agent (use create-agent).

日本語の概要は準備中です。原文の説明を表示しています。

armanisadeghi/ai-matrx32026年10月13日 更新

armanisadeghi のスキルをすべて見る

このスキルの問題を報告する