Imported from JoelGrayson/Cognito (
project-context/AGENTS.md). Install upstream withnpx skills add JoelGrayson/Cognito --skill project-context. Copyright stays with the author.
AGENTS.md
Project context for AI coding agents and teammates. Keep this file short; details live in docs/.
Project
(Project name TBD) is an AI-powered self-learning platform for the HackMIT education track. Self-learners fail because they don't know what to learn, in what order, or how long it takes, and they can't tell whether they actually understood it. We generate a personalized, editable roadmap (a node/edge graph in the style of roadmap.sh), guide the learner through it, and later add spaced repetition and an avatar "teach-back" to verify understanding.
Current phase: Phase 1 transition. REPO_IMPL=memory|drizzle selects storage; drizzle uses DATABASE_URL (Docker Postgres via docker compose up -d --wait, then pnpm db:migrate). lib/session.ts now uses Better Auth anonymous sessions. See docs/kickoff-plan.md.
Stack
Next.js (App Router) + TypeScript (strict) + Tailwind. zod for all schemas. React Flow (@xyflow/react) + dagre (@dagrejs/dagre) for the graph. Claude API via @anthropic-ai/sdk. Zustand for client state. Vitest for tests.
Phase 1 adds: Supabase Postgres (as plain Postgres), Drizzle ORM, Better Auth (anonymous plugin).
Commands
Use pnpm for dependency management and scripts. Do not use npm, npx, or yarn.
pnpm dev
pnpm build
pnpm lint
pnpm test # vitest
pnpm exec tsx --env-file=.env.local scripts/eval-graph.ts # prompt eval: validation pass rate, latency
pnpm exec drizzle-kit push # Phase 1, schema owner only, read the diff first
Environment
ANTHROPIC_API_KEY, MOCK_AI=true (no API calls, deterministic mocks), REPO_IMPL=memory|drizzle (default memory).
Phase 1: DATABASE_URL, BETTER_AUTH_SECRET, BETTER_AUTH_URL. Never commit .env*.local.
Layout
app/ routes: landing, onboarding, plan/[planId], api/*
components/TopicGraph/ shared graph component (workshop, plan view, learning experience)
components/onboarding/ questionnaire, chat panel, generating screen
lib/graph/ validate, applyOps, topoSort, layout, diff, fallback (pure, tested)
lib/schedule/ order and weekly schedule computation (pure, tested)
lib/ai/ Anthropic client, models.ts, tools, prompts, mocks, retry
lib/repo/ persistence interfaces + memory impl (drizzle impl in Phase 1)
lib/plans/ shared read API: getPlan, getActivePlan, getNextNode, applyPlanOps
lib/session.ts requireUserId() (stub in Phase 0, Better Auth in Phase 1)
lib/user-data.ts migrateUserData(fromId, toId) for account linking
types/learning.ts zod schemas + inferred types (single source of truth)
fixtures/ sample profile and graphs
scripts/ eval-graph.ts
docs/ project docs (see below)
db/ Phase 1 only: schema.ts, auth-schema.ts, index.ts
Rules (read before writing code)
- Types:
types/learning.tsis the single source of truth (zod schemas, inferred TS types). Never redefine or hand-copy a type. - Phase 0 boundary: do not import from
db/,drizzle-orm,better-auth, or@supabase/*. Persistence goes only throughlib/repo/*. Identity goes only throughlib/session.ts. - Graph changes go only through
applyOps(drafts) orapplyPlanOps(saved plans), followed byvalidateGraph. Never edit graph JSON by hand. - After a plan is saved, never hard-delete a node. Set
scope: "excluded". Progress and cards reference(planId, nodeId)with no foreign key. scopevsprogress:scope(included|known|excluded) lives on the node.progress(todo|in_progress|done) lives in its own table. Never mix them.- Lessons attach to leaf nodes. Containers (nodes with children) have
estMinutes: 0. Only leaves appear inorder. - LLM output comes only from forced tool calls (
tool_choice) validated with zod. Never parse free-text JSON. Every AI function has a mock and a fallback. - AI calls live only in
lib/ai/. Model names live only inlib/ai/models.ts. - API routes derive
userIdfromlib/session.ts, never from the request body. - Units: minutes for durations, ISO 8601 for dates in JSON,
timestamptzfor DB timestamps. - Node ids:
/^[a-z0-9_]{1,60}$/, unique per plan, immutable. - Pure logic (
lib/graph,lib/schedule) has no React and no I/O, and has tests. - Don't write to tables you don't own (see Ownership). Ask the owner for a function.
- Changing the data contract: edit
docs/data-contract.mdandtypes/learning.tsin the same PR and tell the team. Don't silently drift.
Gotchas
- React Flow: import
@xyflow/react/dist/style.css, give the container an explicit height, and definenodeTypesoutside the component (or memoize it). - The dev in-memory store must live on
globalThis, or it resets on every hot reload. - Long routes (
/api/plan/generate):export const maxDuration = 60. - Tool
input_schemafrom zod:z.toJSONSchema(zod 4), orzod-to-json-schemaon zod 3. - Phase 1, Better Auth: call
getSessionbeforesignIn.anonymous()(a second anonymous sign-in errors). The anonymous user is deleted after linking, so move rows inonLinkAccountfirst. - Phase 1, Supabase: use the transaction pooler with
postgres(url, { prepare: false }).drizzle-kit pushcan try to drop teammates' tables, so read the diff.
Ownership
| Area | Owner | Notes |
|---|---|---|
Onboarding: app/onboarding, app/plan/[planId] (overview), lib/graph, lib/schedule, lib/ai, components/TopicGraph, study_plans, onboarding_sessions |
(name TBD) | Writes plans |
Learning experience: app/plan/[planId]/learn, topic_progress, node_content |
(name TBD) | Reads plans via lib/plans |
| Future: spaced repetition, avatar teach-back | (name TBD) | review_cards, assessments |
Docs (read on demand)
docs/project-overview.md: vision, pillars, scope now vs later, demo story.docs/architecture.md: layers, AI integration, environments, phases, testing.docs/data-contract.md: authoritative decisions D1 to D12, zod contract, tables, 13 mermaid flow diagrams.docs/onboarding-spec.md: screens, API routes, AI functions and prompt rules, fallbacks, acceptance criteria.docs/kickoff-plan.md: Phase 0 milestones M0 to M7 with tasks and done-when criteria.
Open decisions (proposals until ratified by the team)
D1 to D12 in docs/data-contract.md are proposals with defaults. Also open: project name, UI kit (shadcn/ui proposed), hosting (Vercel proposed), whether account upgrade is in the demo (proposed: out of scope).
If code and docs disagree, stop and ask rather than picking one.
Working style
- Small PRs, merge often. Schema changes get their own PR.
- Before finishing a task:
pnpm lint && pnpm test && pnpm build. - Prefer editing existing modules over adding parallel ones. Reuse
lib/graphandlib/aihelpers. - Use
MOCK_AI=truefor UI work; verify prompt changes withscripts/eval-graph.tsagainst the real API.