From 1837a5f53614460cb66370bac0861741792d5b3e Mon Sep 17 00:00:00 2001 From: RyuseiTaniguchi Date: Mon, 7 Sep 2026 09:31:07 +0900 Subject: [PATCH] docs: make AGENTS.md canonical and correct its drifted claims MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The repo only had CLAUDE.md, so Codex, Cursor and anything else reading AGENTS.md got nothing. Next.js' own generator treats AGENTS.md as the real file and CLAUDE.md as a one-line `@AGENTS.md` import, and its managed block prefers whichever file already hosts it — so hosting the block in AGENTS.md also stops `next dev` from writing to CLAUDE.md. Block verified byte-identical via `hasCurrentAgentRules()`. Corrections, each checked against the code rather than carried over: - Both design docs it called "the authoritative reference" (2026-05-09-ledgr-design.md, -testing-architecture-design.md) do not exist. Replaced with an honest pointer to docs/superpowers/ and a note that the code wins on conflict. - SimpleFIN is a full sync path (src/lib/simplefin/: client, schemas, sync, queries, recurring), not the Plaid-only story the stack table told. - The MCP server (src/lib/mcp/ with tools, OAuth, widget apps), the cron scheduler (src/lib/scheduler/) and the .well-known OAuth routes were absent entirely. - Encryption keys are versioned (ENCRYPTION_KEY_V), not a single key. - 30 tables, not 29. categorization/ has no orchestrator.ts. - Commands were missing test:changed, test:mutate:diff, build:mcp-widgets, reset-password and two backfills. Adds what this session cost us to learn: the UI primitives are Base UI and not Radix; `shadcn add` emits `import { cn } from "cn"` and downgrades recharts in package.json; ui/chart.tsx carries two deliberate local edits a regenerate would clobber; vitest is node-only so component tests need a config change; and CI reds on the self-hosted runners are often contention rather than the diff. --- AGENTS.md | 235 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ CLAUDE.md | 203 +--------------------------------------------- 2 files changed, 236 insertions(+), 202 deletions(-) create mode 100644 AGENTS.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..e8ddf49 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,235 @@ +# AGENTS.md + +Guidance for AI coding agents working in this repository. This is the canonical +instructions file; `CLAUDE.md` imports it. + +## Project Overview + +**Ledgr** — a self-hostable, open-source personal finance app (AGPLv3). + +Self-hosting is the only deployment model. There is no hosted product, so there +is one audience and one setup path; `docs/superpowers/specs/2026-07-07-ledgr-hosted-beta-design.md` +describes a direction that was abandoned. + +Design docs live in `docs/superpowers/specs/` (design) and +`docs/superpowers/plans/` (execution). They are point-in-time records, not a +maintained spec — when a doc and the code disagree, the code wins. + +## Stack + +| Layer | Choice | +|-------|--------| +| Framework | Next.js 16 (App Router) | +| Language | TypeScript | +| UI | shadcn/ui v4 (`base-nova` style, Base UI primitives) + Tailwind v4 | +| Charts | Recharts v3 via shadcn Chart (`components/ui/chart.tsx`) | +| ORM | Drizzle ORM 0.45 | +| Database | PostgreSQL 18 (via node-postgres Pool) | +| Auth | Better Auth (+ passkeys) | +| Bank Sync | Plaid Node SDK and SimpleFIN — both first-class; CSV/OFX import for the rest | +| AI | Vercel AI SDK (BYOK — user brings own API key) | +| MCP | Ledgr exposes itself as an MCP server (`src/lib/mcp/`) with OAuth | +| Scheduling | `node-cron` scheduler (`src/lib/scheduler/`) driving job functions | +| Testing | Vitest + fast-check + Playwright + Stryker + MSW | + +Note the UI primitives are **Base UI**, not Radix. APIs differ — `ToggleGroup` +takes `value: string[]` and hands back an empty array when the active item is +clicked again, `PopoverTrigger` takes a `render` prop, and so on. Read the +component in `src/components/ui/` before assuming a Radix signature. + +## Key Conventions + +- **All monetary amounts are INTEGER (cents).** $12.50 → 1250. Never use floats + for money. Convert to display format at the UI layer via `lib/money.ts`. +- **Plaid amount convention:** Positive = debit/expense, negative = credit/income. + `normalized_amount` flips sign for human display. +- **Ownership enforcement:** Use `scopedQuery(householdId)` to auto-inject + `household_id` filtering. Never write manual WHERE clauses for tenant + isolation. It takes an optional second `db` argument for tests. +- **Encryption:** Plaid/SimpleFIN tokens and AI API keys are encrypted at the app + layer (aes-256-gcm). Keys are **versioned** — `ENCRYPTION_KEY` is v1, + `ENCRYPTION_KEY_V2` and up are later versions, so rotation can re-wrap + ciphertext without downtime (`pnpm rotate-keys`). +- **Timestamps:** Use `new Date()` for Postgres `timestamp` columns. Use + `nowISO()` from `@/lib/date-utils` only for text date columns. Never + `new Date().toISOString()` for timestamp columns — Drizzle handles the + Date→Postgres conversion. +- **Transfers are excluded from spend.** Rows with `isTransfer` are left out of + reports, budgets and spending totals. Investment-account activity is tagged + `isTransfer: true` with `transferSource: "investment_account"` at sync time, + which is what keeps brokerage fills out of the Transactions tab. +- **Deployment target:** Docker, self-hosted. `docker compose up` starts Postgres + and the app; migrations run on container startup via + `scripts/docker-entrypoint.sh`. + +## Commands + +```bash +# Development +pnpm install # Install dependencies +pnpm dev:db # Start Postgres (Docker) +pnpm dev:setup # Start Postgres + migrate + dev server +pnpm dev # Next.js dev server (requires running Postgres) +pnpm db:generate # Generate Drizzle migrations +pnpm db:migrate # Run migrations +pnpm db:studio # Open Drizzle Studio + +# Testing +pnpm test # Vitest unit + integration +pnpm test:changed # Only tests related to changed files (fast loop) +pnpm test:watch # Watch mode +pnpm test:coverage # v8 coverage report +pnpm test:e2e # Playwright +pnpm test:mutate # Stryker (full) +pnpm test:mutate:incremental # Stryker (changed files) +pnpm test:mutate:diff # Stryker (diff vs main) — what CI runs on PRs +pnpm lint # ESLint +pnpm typecheck # tsc --noEmit + +# Operations +pnpm reset-password --check|--set # Operator password check/reset +pnpm rotate-keys # Re-wrap encrypted columns to a new key version +pnpm backfill-clean-names # Backfill merchant-cleaned names +pnpm backfill-transfers # Backfill transfer pairing +pnpm backfill-investment-activity # Tag existing investment rows as transfers +pnpm backfill-balances # Backfill balance history +pnpm build:mcp-widgets # Build the MCP app widgets +``` + +## Project Structure + +``` +src/ +├── app/ +│ ├── (auth)/ # Login, signup +│ ├── (dashboard)/ # accounts, transactions, budgets, bills, +│ │ # investments, reports, rules, import, settings +│ ├── api/ # ai/chat, auth, dashboard, export, health, +│ │ # import, mcp/oauth, plaid/{webhook,oauth-return}, search +│ ├── mcp/authorize/ # OAuth consent screen for MCP clients +│ └── .well-known/ # OAuth authorization-server + protected-resource metadata +├── components/ +│ ├── atoms/ molecules/ organisms/ # Atomic-design layering +│ └── ui/ # shadcn components — see "UI conventions" +├── db/schema/ # Drizzle schema, one file per domain (30 tables) +├── lib/ +│ ├── plaid/ # Plaid client + sync +│ ├── simplefin/ # SimpleFIN client, schemas, sync, queries, recurring +│ ├── categorization/ # engine.ts, pfc-map.ts, rule-pattern.ts +│ ├── ai/ # categorize, resolve-merchants, provider, chat +│ ├── mcp/ # MCP server: tools/, auth/, apps/ (widgets) +│ ├── scheduler/ # cron config + runner + tasks +│ ├── jobs/ # snapshot-balances, backfill-*, rotate-encryption-keys +│ ├── auth/ import/ # Better Auth config; CSV/OFX parsers +│ ├── scoped-query.ts encryption.ts money.ts date-utils.ts +├── actions/ # Server Actions (mutations) +└── queries/ # Server-side data fetching +tests/integration/ # DB-backed tests + testcontainers setup +e2e/ # Playwright +``` + +## Auto-Categorization Pipeline + +Tiers, in order. Each sets `categorySource` on the transaction to record +provenance: + +1. **`rule`** — user pattern rules on transaction name or merchant, by priority +2. **`merchant_default`** — `merchant.categoryId`, when the user has set one +3. **`pfc`** — Plaid `personal_finance_category.detailed`, mapped in `pfc-map.ts` +4. **`ai`** — batch the remainder to the user's AI provider, confidence-gated +5. Uncategorized — flagged for manual review + +`manual` is set by user edits and is never overwritten by a lower tier. + +## UI conventions + +- **Reach for `src/components/ui/` before hand-rolling.** Most of the library is + already installed and wired. Install what is missing rather than rebuilding it. +- **`shadcn add` has two known hazards in this repo.** It emits + `import { cn } from "cn"` and tries to install an npm package by that name, and + it rewrites `package.json` dependency versions it should leave alone (it has + downgraded `recharts` on every run). Check `git diff package.json` after any + `add`, and answer **no** to overwrite prompts — `card.tsx` is customized. +- **`components/ui/chart.tsx` carries two deliberate local edits**, both commented + in the file: `cn` is imported from `@/lib/utils`, and `ChartTooltipContent` + takes a `valueFormatter` prop because every value this app charts is an integer + cent count and upstream renders values with `toLocaleString()`. Do not let a + regenerate clobber them. +- **Charts must go through `ChartContainer`.** Recharts' bare `` paints a + hardcoded white box that is unreadable in dark mode. Series labels come from + `ChartConfig`, not per-series `name` props. Callers size charts with an + explicit-height parent, so pass `className="aspect-auto h-full w-full"`. +- **Category names are user data** (`Groceries & Dining`) and cannot be emitted as + `--color-` custom properties. Charts keyed by category keep inline colors. +- **Not every raw element is a bug.** Clickable table rows, category pills and + editable text are legitimately custom; `Button` is the wrong base for a ``. + The hand-rolled `rounded-lg border` shells are also *visually distinct* from + this repo's `Card` (`rounded-xl bg-card ring-1 ring-foreground/10`, no border) — + converting them is a redesign, not a refactor. + +## Testing Architecture + +| Layer | Tool | What it tests | +|-------|------|--------------| +| Unit + Property | Vitest + fast-check | Pure logic (money, encryption, categorization) | +| Integration | Vitest + Postgres (testcontainers) | Drizzle queries, scoped-query isolation, actions | +| Mutation | Stryker (diff on PRs) | Whether tests actually catch bugs | +| E2E | Playwright | Critical user journeys | +| Contract | MSW + Zod | Plaid/SimpleFIN response shapes | +| Static | TypeScript strict + ESLint | Type safety | + +- **Colocate unit tests** with source (`money.test.ts` next to `money.ts`). + DB-backed tests go in `tests/integration/`, Playwright in `e2e/`. +- **Vitest is `environment: "node"` and matches `*.test.ts` only.** There is no + jsdom project, so React component tests are not currently possible without a + config change. Verify component work by running the app. +- **Test DB factory:** `createTestDb()` from `tests/integration/setup.ts` — async, + one Postgres schema per test file. Use + `beforeAll(async () => { ({ db, close } = await createTestDb()); })`. +- **Property tests** use `@fast-check/vitest`: `test.prop([arb])("name", fn)`. +- **No tests for declarative code** (schemas, configs, type definitions). +- **Time in tests:** never hardcode absolute dates that must land in a "recent" + window — queries compute windows from `new Date()`, so fixed dates rot as the + calendar moves. Derive fixture dates relative to now. +- **JavaScript `-0` gotcha:** `normalizeAmount(0)` returns `-0`. Use `Math.abs()` + when comparing to zero. + +**Budget per work type:** feature → 3-5 behavioral tests, plus property tests if +it touches financial math. Bug fix → 2-3 regression tests proving the fix. +Refactor → 0 new tests; the existing ones must pass. + +### TDD workflow + +Red (`pnpm test:changed` or `test:watch`) → green → refactor → commit. The +pre-commit hook runs `eslint --fix` only; it does not run tests, because +integration tests need Docker. The real gate is CI. + +**CI:** typecheck → lint → vitest → Stryker (`mutation (diff)`, PR-only and +non-blocking). Runs on `.github/workflows/ci.yml`, on self-hosted runners. +Playwright is not yet in the blocking job. + +CI reds on these runners are not always your code — contention between +container-heavy jobs, a shared pnpm store racing itself, and legacy mutation debt +on DB files all produce reds that look like breakage. Check which step failed and +re-run before assuming the diff is at fault. + +## Database + +Migrations are generated with `pnpm db:generate` and must be reviewed before +committing: + +- **Generated `NOT NULL` column adds have no backfill** and will break a populated + database. Rewrite the migration to add the column nullable, backfill, then set + the constraint. +- **Never hand-edit a migration's `when` timestamp in the journal.** Older Docker + images replay it and crash-loop. + + + +# This is NOT the Next.js you know + +This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` (resolved from this file's directory; in monorepos the `next` package may not be visible from the repo root) before writing any code. Heed deprecation notices. + +This block is written and re-added by `next dev` — verify at `node_modules/next/dist/server/lib/generate-agent-files.js`. Removing it from a diff only re-creates the uncommitted change; committing it with your work keeps the tree clean. + + diff --git a/CLAUDE.md b/CLAUDE.md index 689c60e..43c994c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,202 +1 @@ -# CLAUDE.md - -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. - -## Project Overview - -**Ledgr** — a self-hostable, open-source personal finance app (AGPLv3). - -**Design spec:** `docs/superpowers/specs/2026-05-09-ledgr-design.md` — the authoritative reference for architecture, data model, and feature design. Read this before making architectural decisions. - -## Stack - -| Layer | Choice | -|-------|--------| -| Framework | Next.js 16 (App Router) | -| Language | TypeScript | -| UI | shadcn/ui v4 + Tailwind v4 | -| Charts | Recharts v3 (via shadcn Chart) | -| ORM | Drizzle ORM 0.45 | -| Database | PostgreSQL 18 (via node-postgres Pool) | -| Auth | Better Auth | -| Bank Sync | Plaid Node SDK (optional — CSV import is first-class) | -| AI | Vercel AI SDK (BYOK — user brings own API key) | -| Background Jobs | Standalone job functions (snapshot-balances, backfill-balances) | -| Testing | Vitest + fast-check + Playwright + Stryker + MSW | - -## Key Conventions - -- **All monetary amounts are INTEGER (cents).** $12.50 → 1250. Never use floats for money. Convert to display format at the UI layer via `lib/money.ts`. -- **Plaid amount convention:** Positive = debit/expense, negative = credit/income. `normalized_amount` column flips sign for human display. -- **Ownership enforcement:** Use `scopedQuery(householdId)` wrapper to auto-inject `household_id` filtering on all queries. Never write manual WHERE clauses for tenant isolation. -- **Encryption:** Plaid access tokens and AI API keys encrypted at app layer (aes-256-gcm, key from `ENCRYPTION_KEY` env var). -- **Plaid is the primary feature.** Bank sync via Plaid is the core experience. CSV/OFX import is available as a supplementary option for accounts not supported by Plaid. -- **Timestamps:** Use `new Date()` for all Postgres `timestamp` columns. Use `nowISO()` from `@/lib/date-utils` only for text date columns. Never use `new Date().toISOString()` for timestamp columns — Drizzle handles Date→Postgres conversion. -- **Deployment target:** Docker, self-hosted. `docker compose up` starts both Postgres and the app. Migrations run automatically on container startup via `scripts/docker-entrypoint.sh`. - -## Commands - -```bash -# Development -pnpm install # Install dependencies -pnpm dev:db # Start Postgres (Docker) -pnpm dev:setup # Start Postgres + migrate + dev server -pnpm dev # Next.js dev server (requires running Postgres) -pnpm db:generate # Generate Drizzle migrations -pnpm db:migrate # Run migrations -pnpm db:seed # Seed default categories + demo data -pnpm db:studio # Open Drizzle Studio - -# Testing -pnpm test # Vitest unit + integration tests -pnpm test:watch # Vitest in watch mode -pnpm test:coverage # Vitest with v8 coverage report -pnpm test:e2e # Playwright e2e tests -pnpm test:e2e:ui # Playwright with interactive UI -pnpm test:mutate # Stryker mutation testing (full) -pnpm test:mutate:incremental # Stryker mutation testing (changed files only) -pnpm lint # ESLint -pnpm typecheck # TypeScript type checking - -# Docker -docker compose up # Run the full app -docker compose up --build # Rebuild and run -``` - -## Architecture - -``` -Browser ──▶ Next.js App Router - ├── Server Components ── read-only data (transactions, reports) - ├── Server Actions ──── mutations (sync, categorize, budget CRUD) - ├── API Routes ──────── Plaid webhooks, AI streaming, CSV import - └── Client Components ── interactive UI (charts, forms) - │ - Drizzle ORM ──▶ PostgreSQL (via node-postgres Pool) - Plaid Node SDK ──▶ Plaid API (sandbox/production via PLAID_ENV) - Vercel AI SDK ──▶ User's LLM provider (Claude/OpenAI/Gemini) - Jobs ──▶ Background tasks (sync, snapshots, categorization) -``` - -## Project Structure - -``` -ledgr/ -├── src/ -│ ├── app/ # Next.js App Router pages -│ │ ├── (auth)/ # Login, signup, onboarding -│ │ ├── (dashboard)/ # Main app (accounts, transactions, budgets, etc.) -│ │ └── api/ # Plaid webhooks, AI chat, CSV import, health -│ ├── components/ # UI components (shadcn/ui, charts, dashboard widgets) -│ ├── db/ -│ │ ├── schema/ # Drizzle schema files (one per domain) -│ │ ├── seed/ # Default categories + demo data -│ │ └── index.ts # Drizzle client + node-postgres Pool -│ ├── lib/ -│ │ ├── plaid/ # Plaid client, sync logic -│ │ ├── categorization/ # Rule engine, PFC mapping, orchestrator -│ │ ├── ai/ # AI categorization, chat -│ │ ├── auth/ # Better Auth config + adapter -│ │ ├── import/ # CSV/OFX parsers -│ │ ├── jobs/ # Background job functions (snapshots, backfill) -│ │ ├── scoped-query.ts # Household-scoped query wrapper -│ │ ├── encryption.ts # AES encrypt/decrypt -│ │ ├── date-utils.ts # Timestamp and date helpers (nowISO, todayDateString) -│ │ └── money.ts # Cents ↔ display helpers -│ ├── actions/ # Server Actions -│ └── queries/ # Server-side data fetching -├── tests/ -│ ├── integration/ -│ │ ├── setup.ts # Postgres test DB factory (per-file schema isolation) -│ │ ├── db-factory.test.ts # DB factory smoke tests -│ │ └── scoped-query.test.ts # Household isolation integration tests -│ ├── global-setup.ts # Testcontainers Postgres lifecycle -│ └── mocks/ -│ ├── handlers.ts # MSW handlers (Plaid API) -│ └── server.ts # MSW server setup for Vitest -├── e2e/ -│ └── health.spec.ts # Playwright health check E2E -├── scripts/ -│ ├── docker-entrypoint.sh # Container startup (migrate + serve) -│ ├── migrate.mjs # Standalone Drizzle migration runner -│ └── install-migrate-deps.mjs # Installs migration deps from package.json versions -├── docker-compose.yml # Postgres 18 + app services -├── Dockerfile # Multi-stage production build (Node 24 LTS) -├── vitest.config.ts -├── playwright.config.ts -├── stryker.config.json -└── .env.example -``` - -## Data Model Highlights - -29 tables. Key entities: `households`, `accounts`, `transactions` (with `transaction_splits`, `transfer_pair_id`), `merchants`, `category_groups`/`categories`/`category_rules`, `budgets`/`budget_categories`, `recurring_transactions`, `investment_holdings`/`holdings_history`/`investment_transactions`, `plaid_items`/`sync_log`, `saved_reports`, `oauth_clients`/`oauth_codes`/`oauth_consents`/`oauth_refresh_tokens`. - -See the design spec for full schema with indexes and constraints. - -## Testing Architecture - -**Design spec:** `docs/superpowers/specs/2026-05-09-testing-architecture-design.md` - -| Layer | Tool | What It Tests | -|-------|------|--------------| -| Unit + Property | Vitest + fast-check | Pure logic (money, encryption, categorization rules) | -| Integration | Vitest + Postgres (testcontainers) | Drizzle queries, scoped-query isolation, server actions | -| Mutation | Stryker (incremental) | Whether tests actually catch bugs (not just coverage) | -| E2E | Playwright | Critical user journeys end-to-end | -| Contract | MSW + Zod | Plaid API response shapes | -| Static | TypeScript strict + ESLint | Type safety | - -**Key conventions:** -- **Colocate unit tests** with source files (`money.test.ts` next to `money.ts`). -- **Integration tests** (need DB) go in `tests/integration/`. -- **E2E tests** go in `e2e/`. -- **No tests for declarative code** (schemas, configs, type definitions). -- **Test DB factory:** `createTestDb()` from `tests/integration/setup.ts` — async, creates a unique Postgres schema per test file for isolation. Shared testcontainer started via `tests/global-setup.ts`. Use `beforeAll(async () => { ({ db, close } = await createTestDb()); })` pattern. -- **Property-based tests** use `@fast-check/vitest`. API: `test.prop([arb])("name", fn)` — not `fc.test()`. -- **Scoped-query** accepts optional `db` parameter for testability: `scopedQuery(householdId, testDb)`. -- **MSW mocks** for Plaid API in `tests/mocks/`. Use `server` from `tests/mocks/server.ts` in Vitest. -- **Mutation testing gate:** Stryker breaks build below 60% mutation score, warns below 80%. Run incremental on PRs. -- **JavaScript -0 gotcha:** `normalizeAmount(0)` returns `-0`. Use `Math.abs()` when comparing zero. - -**Test budget per work type:** -- Feature: 3-5 behavioral tests + property tests if financial math -- Bug fix: 2-3 regression tests proving the fix -- Refactor: 0 new tests (existing tests must pass) - -### TDD Workflow (new work) - -New features and bugfixes start **test-first** (superpowers `test-driven-development` skill). The red-green-refactor loop, mapped to this repo: - -1. **Red** — write the smallest failing test next to the code (`*.test.ts` colocated, or `tests/integration/` if it needs the DB). Run it and watch it fail: - - `pnpm test:changed` — runs only tests related to your changed files (fast loop) - - or `pnpm test:watch` for continuous feedback -2. **Green** — write the minimal code to pass. Re-run until green. -3. **Refactor** — clean up with tests staying green. -4. **Commit** — the `pre-commit` hook (`simple-git-hooks` + `lint-staged`) runs `eslint --fix` on changed files (fast, no Docker). Run tests yourself before committing via `test:changed`/`test:watch`. - -Enforcement layers, fast → slow: `test:changed`/watch (you, locally) → pre-commit hook (lint) → CI (full suite). The test gate lives in CI — integration tests need Docker and are too heavy for a pre-commit hook — so red blocks the merge once branch protection requires the `test` check. - -**Time in tests:** never hardcode absolute dates that must fall in a "recent" window — queries compute windows from `new Date()`, so hardcoded dates silently rot as the calendar moves. Derive fixture dates relative to now (see `dashboard-queries.test.ts`). - -**CI pipeline order:** typecheck → lint → vitest → stryker (incremental). Wired in `.github/workflows/ci.yml` (runs on push to `main` + all PRs; mutation is PR-only). Playwright is not yet in the blocking job. - -## Auto-Categorization Pipeline - -1. **User rules** — pattern matching on transaction name or merchant (ordered by priority) -2. **Merchant default** — if `merchant.categoryId` is set by user -3. **PFC mapping** — Plaid's `personal_finance_category.detailed` code mapped to seed categories via static map in `lib/categorization/pfc-map.ts` -4. **AI fallback** — batch uncategorized transactions → user's AI provider (confidence-gated) -5. Uncategorized — flagged for manual review - -Each tier sets `categorySource` on the transaction (`"rule"` | `"merchant_default"` | `"pfc"` | `"ai"` | `"manual"`) to track provenance. Manual user edits always set `"manual"` and are never overwritten by lower tiers. - - - -# This is NOT the Next.js you know - -This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` (resolved from this file's directory; in monorepos the `next` package may not be visible from the repo root) before writing any code. Heed deprecation notices. - -This block is written and re-added by `next dev` — verify at `node_modules/next/dist/server/lib/generate-agent-files.js`. Removing it from a diff only re-creates the uncommitted change; committing it with your work keeps the tree clean. - - +@AGENTS.md