deckhearth/.cursor/skills/add-page/SKILL.md
Randall Stillwell bb05ca731b bootstrap: agent pipeline v0.5.0 + ship-readiness review
Installs the three-layer agent-pipeline scaffold (https://github.com/varutasu/agent-pipeline @ v0.5.0):

L1 — Context (curated brain)
- AGENTS.md: orientation, conventions, 8 explicit gotchas
- .cursor/rules/: no-go-zones, api-routes, auth-and-permissions,
  db-and-schema, ui-and-theming, schema-map
- .cursor/skills/: add-api-route, add-page recipes
- docs/agent-context/README.md: layer explainer
- docs/SCHEMA_MAP.md: hand-curated Neon Postgres reference
  (replaces Prisma schema map since stack is raw SQL)

L2 — Subagent roles (copied verbatim from upstream templates)
- 9 .cursor/agents/role-*.md files: Conductor, IA-Architect,
  UX-Reviewer, Architect, Implementer, Reviewer,
  Design-System-Auditor, A11y-Auditor, Doc-Writer

L3 — Pipeline scaffolding (Vercel variant)
- CI: lint + schema-map-drift only (no duplicate build —
  Vercel handles it). Test job commented out until vitest lands.
- preview-smoke + visual-diff via wait-for-vercel-preview
- pr-health-rollup sticky comment aggregator
- agent-context-drift weekly cron
- PULL_REQUEST_TEMPLATE, CODEOWNERS (auth/admin paths tagged)
- .convoys/ folder + seed ship-readiness.md review
- lib/flags/index.js (JS — converted from TS template)
- scripts/wt.sh (Cursor 3.2 deprecation stub),
  scripts/log-convoy-event.sh
- tests/smoke/app.smoke.spec.ts (Playwright skeleton)

Manifest
- .agent-context-manifest.yml: tracks 31 artifacts by sha256
  for future sync-agent-context drift detection

Review
- .convoys/ship-readiness.md: 16 findings (7 P0 ship-blockers,
  5 P1 quality-bar, 4 P2 refactor, P3 UX/IA/a11y/docs) with
  proposed 13-convoy launch sequence.

No production code changed in this commit. All findings in
the ship-readiness review will be addressed in follow-up convoys
starting with fix-auth-bypass.

Structural brain: user-code-review-graph MCP has indexed the
codebase (122 files, 628 nodes, 5602 edges, 11 communities,
84 flows). Per-developer; not committed.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-22 23:16:08 -05:00

3.7 KiB

name description
add-page Add a new Next.js page under pages/. Use when you need a new route, a new view for an existing resource, or an admin-only screen. Covers Layout wiring, auth, theme tokens, and the public/authenticated split pattern.

Add a page

pages/<file>.js becomes a route. Pages router conventions:

File Route
pages/about.js /about
pages/cards/[id].js /cards/:id (but use pages/card/[id].js per existing naming)
pages/admin/index.js /admin

Step 1: Decide auth shape

Mode Template
Public-only Render without ProtectedRoute; no auth check
Auth-required Wrap top-level export with <ProtectedRoute>
Admin-only Wrap with <AdminProtected> from components/AdminProtected.js
Public + auth-enhanced (e.g. /cards) Inline split: render <PublicView> if not logged in, <AuthedView> if logged in. Copy the pattern from pages/cards.js.

Step 2: Skeleton

import { useState, useEffect } from 'react';
import Layout from '../components/Layout';
import ProtectedRoute from '../components/ProtectedRoute';
import { useAuth } from '../lib/use-auth';

export default function MyPage() {
  return (
    <ProtectedRoute>
      <MyPageInner />
    </ProtectedRoute>
  );
}

function MyPageInner() {
  const { user, loading } = useAuth();
  const [items, setItems] = useState([]);
  const [fetching, setFetching] = useState(false);

  useEffect(() => {
    if (loading || !user) return;

    const token = localStorage.getItem('auth_token');
    setFetching(true);
    fetch('/api/my-resource', {
      headers: { Authorization: `Bearer ${token}` },
    })
      .then((r) => r.json())
      .then((data) => setItems(data.items || []))
      .catch((err) => console.error('fetch failed', err))
      .finally(() => setFetching(false));
  }, [user, loading]);

  return (
    <Layout user={user} showSearch={false}>
      <div className="p-6" style={{ backgroundColor: 'var(--bg-primary)' }}>
        <h1 className="text-2xl font-bold" style={{ color: 'var(--text-primary)' }}>
          My Page
        </h1>
        {fetching ? <p>Loading</p> : items.map((i) => <div key={i.id}>{i.name}</div>)}
      </div>
    </Layout>
  );
}

Step 3: Theme tokens (not hex)

  • Backgrounds → var(--bg-primary), var(--bg-secondary), var(--bg-tertiary)
  • Text → var(--text-primary), var(--text-secondary)
  • Accents → var(--accent-ember), var(--accent-flame)
  • Borders → var(--border)

Use Tailwind for layout, spacing, sizing, hover/focus states. Use CSS vars (inline style={{ ... }}) for colors that need to switch with theme.

Step 4: Always pass user to Layout

<Layout user={user}> — never let the default kick in (it's a hardcoded maintainer email; see AGENTS.md Gotcha #8).

Step 5: Mobile

components/Layout.js already handles the mobile drawer + bottom nav. To add the page to nav, edit NavigationContent in Layout.js. Use existing icon names from the getIcon registry; add new ones to that registry before referencing.

Step 6: Check

  • Auth wrapper chosen (ProtectedRoute / AdminProtected / public).
  • useAuth() from lib/use-auth.js (not the legacy lib/auth-context.js).
  • user passed to Layout explicitly.
  • Colors come from theme tokens, not hex.
  • All interactive elements have aria-label or visible text.
  • Mobile: confirm the page renders in the mobile drawer.

Anti-patterns

Don't Do
Hardcode hex colors Use CSS variables
Default user = { … } to a real email Default to null
Pull from lib/auth-context for new code Use lib/use-auth
Render Layout twice on the same page Single <Layout> at the top