跳到正文
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.4 KB
  • coming-soon-placeholder.md1.3 KB
  • file-templates.md13.7 KB
  • route-architecture.md8.4 KB

SKILL.md(原文)

安装前可以查看交给 AI 代理的指令内容。

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

评价

还没有评价。欢迎分享你的使用感受。

评论

无需登录即可发表。欢迎分享使用感受、提问,或说明在哪些环境下无法运行。

还没有评论。来写第一条吧。

提交后会立即公开。请勿填写邮箱、电话等个人信息。URL 最多 2 个。

0 / 1000 字

此表单受 reCAPTCHA 保护,适用 Google 的隐私权政策和服务条款。

同一仓库的技能

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-matrx42026年10月23日 更新

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-matrx42026年10月23日 更新

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-matrx42026年10月23日 更新

The method for getting a cheaper model to match an agent's big-model answers by fixing instructions, inputs and setup. Use when asked to move an agent to a smaller or cheaper model, prove one model beats another, or compare models on an agent's test cases.

中文简介正在准备中。当前显示原文说明。

armanisadeghi/ai-matrx42026年10月23日 更新

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-matrx42026年10月23日 更新

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-matrx42026年10月23日 更新

查看 armanisadeghi 的全部技能

报告此技能的问题