# Overview (/docs/overview) # New project docs [#new-project-docs] These are the current Howells project defaults: * the bundled UI baseline for shared UI, tokens, motion, and Storybook * native coding-assistant workflows and Matt Pocock's skills for general software development * the independent skills collection for specialist work that should not live inside one product repo * `@howells/lint` for pinned Oxlint/Oxfmt linting and formatting * `@howells/typescript-config` for thin, explicit tsconfig presets * `@howells/envy` for typed env parsing and deployment env checks * `@howells/ai`, Mastra, and deliberately scoped MCP packages for AI-capable product and agent work * Motif's SDK and agent-readable CLI for image generation, image editing, utility media tools, and creative automation * conservative Turborepo defaults through the root `turbo.json` snippet ## Defaults [#defaults] * Default repo shape: `pnpm` monorepo, usually with `apps/*` and `packages/*` * Default UI stack: Next.js App Router, React, Tailwind CSS v4, Base UI, and Storybook when the repo exports reusable UI; unified Radix is the deliberate opt-out * Default linting and formatting: prefer the `@howells/lint` Oxlint/Oxfmt lane * Default task runner: Turborepo with cache disabled until a repo proves it is deterministic * Default data stack for product apps: Drizzle and Neon, with the narrowest typed API boundary that fits and React Query when client server-state exists * Default AI-capable shape: shared provider baseline through `@howells/ai`, image generation through Motif, product orchestration through repo-local `ai` or `agents`, and MCP in its own package only when the product needs that protocol surface ## Sections [#sections] * [Principles](/docs/principles) * [Getting Started](/docs/getting-started) * [Stack Decisions](/docs/reference/stack-decisions) * [Architecture Defaults](/docs/reference/architecture-defaults) * [Repo Archetypes](/docs/reference/repo-archetypes) * [Package Boundaries](/docs/reference/package-boundaries) * [Deployment Defaults](/docs/reference/deployment-defaults) * [Default Dependencies](/docs/reference/default-dependencies) * [Config Snippets](/docs/reference/config-snippets) * [UI Projects](/docs/reference/ui-projects) * [Shared Package Candidates](/docs/reference/shared-package-candidates) * [Agent Workflow](/docs/reference/agent-workflow) * [Development Skills](/docs/reference/development-skills) * [Worktree Coordination](/docs/reference/worktree-coordination) * [Agentic Development](/docs/reference/agentic-development) * [Launch Checklist](/docs/reference/launch-checklist) ## Agent skill [#agent-skill] The Project Docs are the source of truth. The installable skill at `skills/scaffold` is a distribution surface that routes supported coding assistants through these docs without loading every reference at once. ## What this standard optimises for [#what-this-standard-optimises-for] * Low config drift across active repos * Fast project setup without re-deciding toolchain basics * Correctness over cleverness * Shared UI primitives without flattening project identity * Short agent instructions that prevent ambiguity and drift * Reusable skills that stay independent when they solve cross-repo agent problems # Getting started (/docs/getting-started) # Getting started [#getting-started] Use this sequence when creating a new repo. These defaults are tuned for new TypeScript, UI, data, agent, and package work. Do not let legacy PHP/Craft dependencies influence the default shape of a new repo. ## 1. Choose the repo shape [#1-choose-the-repo-shape] Default to a `pnpm` monorepo: ```text apps/ packages/ docs/ ``` Add another top-level glob only when it has a clear owner and purpose. Before creating files, choose the repo archetype from [Repo Archetypes](/docs/reference/repo-archetypes). That decision should drive the package graph, deployment target, and dependency baseline. ## 2. Decide whether this is a UI project [#2-decide-whether-this-is-a-ui-project] If the project ships a UI: * start from the bundled UI baseline in [UI Projects](/docs/reference/ui-projects) * default to Next.js App Router * keep shared primitives in a package, not in the app * include Storybook when the repo exports reusable UI If the project is not UI-first: * still use the same `pnpm`, TypeScript, Oxlint/Oxfmt, `@howells/husky`, and Turbo baseline * prefer thinner workspace structure and fewer packages If this is a full-stack product app rather than a simple UI shell: * treat Drizzle and Neon as the default persistence architecture, and React Query as the default when the client owns server-state * choose the narrowest typed API boundary: server composition for app-internal work, `tRPC` for same-workspace consumers, and a versioned OpenAPI/oRPC contract for separately deployed or non-TypeScript consumers * split shared infra into packages instead of burying it in one app * use `@howells/envy` for typed env parsing and deployment env checks when runtime env exists * default package boundaries to `db`, `ui`, `typescript-config`, `tailwind-config`, `env`, and `motion`; add `trpc`, `auth`, repo-local `ai`, `agents`, `mcp`, `assets`, or `upload` only where the repo actually needs them If the repo is AI-capable, agent-heavy, or ingestion-heavy: * use `@howells/ai` as the provider baseline before adding raw provider SDKs * use `howells/motif` packages for fal.ai image generation, editing, utility media tools, and agent-facing creative automation * add repo-local `ai`, `mastra`, `agents`, `mcp`, `cli`, `ingestion`, or `enrichment` packages based on real reuse boundaries * use Mastra when the work is agent orchestration, memory, observability, or MCP-adjacent workflow, not for one-off model calls * use `zod` for tool, model IO, and transport contracts * use [Agentic Development](/docs/reference/agentic-development) before scaffolding agent-facing surfaces ## 3. Create the baseline files first [#3-create-the-baseline-files-first] Start with these files before writing app code: * `package.json` * `.node-version` * `pnpm-workspace.yaml` * `turbo.json` * `oxlint.config.ts` * `oxfmt.config.ts` * `tsconfig.json` * `components.json` for UI repos * `AGENTS.md` Use the snippets in [Config Snippets](/docs/reference/config-snippets). Choose the first package boundaries with [Package Boundaries](/docs/reference/package-boundaries). Keep shared infrastructure out of app code. ## 4. Install the shared config packages [#4-install-the-shared-config-packages] For the current house baseline: * `@howells/lint` * `@howells/typescript-config` * `turbo` * `typescript` * `@howells/husky` * `lint-staged` * `tsx` * `vitest` * `@howells/envy` when the repo has runtime env Do not install direct `oxlint` or `oxfmt` dependencies. Use the `@howells/lint` Oxlint/Oxfmt lane. ## 5. Keep the scripts standard [#5-keep-the-scripts-standard] Keep these root script names unless the repo records a reason to differ: * `dev` * `dev:all` * `build` * `lint` * `format` * `typecheck` * `test` * `check` * `check:affected` * `clean` * `prepare` The exact commands can vary by repo, but the script contract should stay stable. ## 6. Add the agent and rules layer deliberately [#6-add-the-agent-and-rules-layer-deliberately] Every repo should have a concise `AGENTS.md`. Add platform-specific configuration only for tools the repo uses. * keep `AGENTS.md` short and focused on repo-specific constraints * add assistant-specific MCP config only when the repo benefits from project-specific servers * rely on the coding assistant's native capabilities and the [development skill map](/docs/reference/development-skills) for explicit planning, implementation, review, testing, and architecture methods * keep repo-local rules small; use project-specific instructions only when the repo has conventions the model and installed skills cannot infer * use independent skills from `~/Sites/skills` for specialist work such as UI polish, browser field testing, package extraction, boundary checks, naming, prose cleanup, and plugin packaging Start with `AGENTS.md`. Add Codex, Claude Code, Cursor, MCP, or workflow support only when used. ## 7. Verify the baseline before feature work [#7-verify-the-baseline-before-feature-work] Run these before feature work: ```bash pnpm install pnpm lint pnpm typecheck pnpm build pnpm test ``` Fix this gate before feature work. ## 8. Record intentional deviations [#8-record-intentional-deviations] If you do not use the default stack, write down the reason early: * why not Next.js for a UI app * why not Drizzle and Neon for persistence * why the chosen API boundary fits its consumers and deployment shape * why not `@howells/ai` for AI provider plumbing * why not `@howells/envy` for runtime env * why not the bundled UI baseline for shared UI primitives Recorded deviations prevent the same decision from being reopened in every repo. ## 9. Pick the deployment shape early [#9-pick-the-deployment-shape-early] Do not leave hosting and runtime shape implicit. Use [Deployment Defaults](/docs/reference/deployment-defaults) to choose between: * Vercel for Next.js apps, docs, and Storybook-like web surfaces * Railway for worker-heavy or service-heavy systems * the house media storage packages when the project has real media storage and delivery needs # Principles (/docs/principles) # Principles [#principles] These rules come from active projects. They describe current practice, including the rules I still break. Constraints help people and agents make consistent choices. Prefer types, closed options, and boundaries that tools can enforce. ## Correctness & failure [#correctness--failure] ### Make the wrong thing impossible to compile [#make-the-wrong-thing-impossible-to-compile] Use discriminated unions for exclusive options, group fields that must occur together, and narrow `unknown` at system boundaries. Parse request bodies, environment variables, and third-party responses before use. Strict types also constrain coding agents. If only the correct call compiles, the compiler catches plausible but invalid code before review. Type-aware linting extends that protection beyond the type system. ### Fail loud, never fall back silently [#fail-loud-never-fall-back-silently] Throw with the cause. Do not turn billing, authentication, configuration, or data failures into plausible output. Empty catches and silent defaults hide the trace needed to fix the problem. Fail where the error occurs. Use [`/fail-fast`](https://github.com/howells/skills/tree/main/fail-fast) to find masked failures. ## Shape & structure [#shape--structure] ### Design from the data model up [#design-from-the-data-model-up] Start with domain entities, relationships, and constraints. Screens, states, and queries should follow the model. For Drizzle projects, treat the schema as the blueprint and design it before the UI hardens around accidental data shapes. ### The lightest shape that fits [#the-lightest-shape-that-fits] Start with the smallest structure that fits. A single-purpose tool may not need a monorepo, task runner, workspace boundaries, or a test harness. Add each when the work requires it, and remove machinery that no longer pays for itself. I tend to scaffold the full system too early. This rule exists to check that instinct. ### Don't over-optimise [#dont-over-optimise] Try the simplest tool already present. Measure a query before adding a cache; test Postgres before adding a vector database; feel repeated pain before extracting an abstraction. Optimising early spends complexity before the requirement is known. ### Boundaries are mechanical, not conventional [#boundaries-are-mechanical-not-conventional] Enforce dependency direction with lint or build rules. Apps do not import apps; packages do not import apps; shared infrastructure is reached through a workspace package or versioned API. Keep domain logic in the domain layer, app clients thin, and orchestration focused on coordination. ### One source of truth; derive the rest [#one-source-of-truth-derive-the-rest] Give each enum, schema, config value, capability list, and version one canonical home. Generate copies with a script or build step. Shared data belongs in a versioned service for separate deployables or an owned package within one workspace. Consumers cross that boundary instead of copying the data or reaching into another component's internals. ### Many small files, budgeted [#many-small-files-budgeted] Keep files focused, usually with one main export. Separate meaningful subcomponents when doing so improves independent reading, testing, or movement. Enforce size and complexity budgets with lint rules: warn in the low hundreds of lines and set a hard cap around 600-800. Split a module that exceeds the budget; do not raise the limit by default. ### Promote repetition into one canonical component, then delete the copies [#promote-repetition-into-one-canonical-component-then-delete-the-copies] When a pattern appears three times, move it to the shared package and delete the copies. Keep product-specific UI in the app. A useful shared component provides one place to fix behaviour or appearance without collecting product exceptions. ### Offer a closed set of options and force the choice [#offer-a-closed-set-of-options-and-force-the-choice] Offer named typography roles, semantic colour tokens, and approved components. Enforce the set where possible. Closed options reduce inconsistent one-offs and give people and agents the same choices. ## Language & naming [#language--naming] ### Fix the ubiquitous language first [#fix-the-ubiquitous-language-first] Keep a short glossary of preferred domain terms and rejected synonyms. Use those terms in code, UI, APIs, and agent instructions. A rename updates the glossary, schema, and tests in one change. Use [`/domain-modeling` and `/grill-with-docs`](https://www.aihero.dev/skills) to test the language before implementation. ### Name after meaning, not implementation [#name-after-meaning-not-implementation] Name the concept, not its implementation: `Deck`, not `CardStack`; `Source Record`, not `articles`. If a name is awkward in a sentence about the system, reconsider it. Names can change. Treat a rename as tested migration work across data, API, and UI. ## Toolchain & operation [#toolchain--operation] ### Decide the toolchain once, reuse everywhere [#decide-the-toolchain-once-reuse-everywhere] Settle the package manager, linter, formatter, TypeScript config, and task runner in shared pinned packages. Exact-pin fast-moving frameworks and apply a short release cooldown. Record exceptions with a removal path. A shared toolchain reduces relearning and makes fleet-wide changes cheap. The current packages are [`@howells/lint`](https://github.com/howells/lint) and [`@howells/typescript-config`](https://github.com/howells/typescript-config). ### A small, consistent command surface at the root [#a-small-consistent-command-surface-at-the-root] Expose `dev`, `build`, `lint`, `typecheck`, `test`, and `check` at the root. Put one-off operations in script files instead of crowding `package.json`. Keep Git hooks quick enough that nobody needs to bypass them. ### One typed env boundary [#one-typed-env-boundary] Parse environment variables through one typed schema at a deliberate runtime boundary. Separate server secrets from client-safe values and keep deployment mode out of `.env`. Never write secrets to Git or logs. Use [`@howells/envy`](https://github.com/howells/envy) to catch missing or malformed values before deployment. ### Always work against the current docs [#always-work-against-the-current-docs] Check current primary documentation before using a library, model, or API. Keep a local clone when the source matters often. Treat aggregators and model memory as leads, then verify the exact surface you call. ### When you do it three times, build the tool [#when-you-do-it-three-times-build-the-tool] On the third manual repetition, make the work repeatable with a script, package, skill, or component. Publish it when others can reuse it. Examples include [`@howells/*`](https://github.com/howells), [`howells/skills`](https://github.com/howells/skills), and [patternmode](https://patternmode.com). ## Building forward [#building-forward] ### Steal good ideas [#steal-good-ideas] Read strong open-source implementations before designing a new structure. Copy a proven mechanism only after understanding the constraint it solves. Adaptation beats invention when the constraints match. ### Build for people and agents [#build-for-people-and-agents] Design CLIs and APIs that people and agents can inspect and rehearse. Provide schema introspection, dry-run modes, JSON output, structured errors, recovery hints, and meaningful exit codes. Each repo keeps one short `AGENTS.md` with commands, constraints, and non-goals. Other assistant configs point to it. More guidance lives at [agentsurface.dev](https://agentsurface.dev). # Agent skill packaging (/docs/adr/0001-agent-skill-packaging) # Agent skill packaging [#agent-skill-packaging] Project Docs are canonical. One generated Agent Skill Distribution exposes them through thin platform wrappers. ## Considered options [#considered-options] * Maintain separate hand-written skills for each coding assistant. * Maintain the Project Docs as the source of truth, with generated or adapted Skill Wrappers for each assistant. Separate hand-written skills were rejected because they would drift as the Scaffold Baseline changes. ## Status [#status] Accepted and partly shipped. `pnpm sync:skill` generates `skills/scaffold/references/` from `docs/`. No generated reference is maintained by hand. * **Claude Code:** reads `skills/scaffold/SKILL.md` and `skills/scaffold/references/` directly. * **Codex:** uses `skills/scaffold/agents/openai.yaml`. * **Cursor:** has no wrapper. A future wrapper should consume the generated references without changing the sync contract. # Scaffold repo deviations (/docs/adr/0002-scaffold-repo-deviations) # Scaffold repo deviations [#scaffold-repo-deviations] Scaffold is a single-app documentation site, so it omits product-monorepo machinery. This ADR records those choices as required by [Getting started, step 8](/docs/getting-started). ## Considered options [#considered-options] * Adopt the full published baseline verbatim (Turbo, `@howells/husky`/lint-staged, Vitest, workspace packages, `oxlint.config.ts`). * Deviate deliberately for a single-app content site, and record the deviations here. The full baseline would add unused configuration. This repo has one package, no shared test surface, and low commit volume. ## Deviations [#deviations] * **No `turbo` / `turbo.json`.** Turbo orchestrates work across packages. This is a single package, so there is nothing to orchestrate. * **No `@howells/husky` / `lint-staged`.** `pnpm check` is the local gate. The repo is single-author and low commit volume, so a staged-file pre-commit hook adds friction without protecting anything a manual `check` does not already cover. * **No `vitest`.** The documentation-integrity script and static build are the test surface. * **`pnpm-workspace.yaml` with empty `packages`.** The workspace file exists for pnpm settings (build allowlists, minimum-release-age exclusions), but declares no workspace packages because there are none. * **No `oxlint.config.ts`.** Lint targets are passed as CLI arguments instead: `howells-check src next.config.mjs source.config.ts`. Scoping on the command line keeps the small, fixed set of lint targets visible in `package.json` without a separate config file. * **The `format` script names its targets.** This excludes generated output and vendored reference trees. ## Consequence [#consequence] These deviations apply only to this documentation site. Product repos still adopt Turbo, hooks, tests, and workspace packages when needed. # Agent workflow (/docs/reference/agent-workflow) # Agent workflow [#agent-workflow] The model owns the development loop. Repo instructions supply local constraints; installed skills supply specialist methods. ## AGENTS.md [#agentsmd] Every repo should have a small `AGENTS.md`. It should cover: * communication expectations * editing constraints * search preferences * repo-specific rules that are easy for an agent to violate Keep it short, direct, and operational. ## General development skills [#general-development-skills] Let the coding assistant handle investigation, planning, implementation, testing, review, and verification. Do not wrap it in another lifecycle runtime by default. Use Matt Pocock's globally installed skills for explicit methods such as domain modelling, requirement grilling, architecture improvement, or technical writing. Do not copy their instructions into each repo. [Development skills](/docs/reference/development-skills) maps the installed engineering flow, Howells specialists, stack-specific skills, and explicit opt-ins. Use that page for routing; keep this page focused on repository workflow. Leave durable evidence when a decision needs it: a focused spec, ADR, test, or review. Do not leave project decisions only in chat history. ## Independent skills [#independent-skills] `~/Sites/skills` is the independent skill collection. Treat it as reusable agent tooling, not as a package inside each product repo. Install or update independent skills through the skill installer, usually globally for the assistant doing the work. Do not vendor the skill sources into product repositories, and do not paste their full instructions into repo-local `AGENTS.md` files. Common Codex install forms: ```bash npx skills@latest add howells/skills --list npx skills@latest add howells/skills --agent codex --global npx skills@latest add howells/skills --skill '*' --agent codex --global ``` The current Howells specialist routes are maintained in [Development skills](/docs/reference/development-skills). That map includes structure, recovery, browser QA, UI direction, code quality, agent surfaces, and plugin packaging without duplicating each skill's procedure here. Do not copy an independent skill's instructions into every repo. Install or invoke the skill when the task needs it, and keep repo-local `AGENTS.md` focused on the current codebase. ## AI, Mastra, and MCP [#ai-mastra-and-mcp] For AI-capable repos, keep the agent surface explicit: * use `@howells/ai` before raw provider SDKs in app code * use Motif's SDK or agent-readable CLI before raw fal.ai clients for image generation, image editing, media utilities, or creative automation * use repo-local `packages/ai` for product-specific model and provider composition * use `packages/agents` when prompts, evaluators, tools, or agent definitions are shared * use Mastra when the repo needs real agent orchestration, memory, workflow state, or observability * use `packages/mcp` or `packages/mcp-server` for MCP contracts and transports * validate model IO and tool schemas with `zod` Do not hide reusable agent or MCP contracts inside a route handler. That makes them harder to test, harder to expose to coding assistants, and harder to reuse from CLIs. When implementing Mastra code, verify the current API before writing against it. Prefer installed package docs under `node_modules/@mastra/*/dist/docs` when packages are present, and keep the TypeScript target/module setup on ES2022-compatible settings. For broader agent-facing software design, use [agentsurface.dev](https://agentsurface.dev) and [Agentic Development](/docs/reference/agentic-development). That guidance covers Mastra, but also covers API shape, CLI ergonomics, MCP, discovery, tool design, retrievability, orchestration, testing, evaluation, browser access, and sandboxing. ## Rules and project instructions [#rules-and-project-instructions] Do not install a full rules system into every repo by default. Use project-local rules or instruction files when: * the repo has multiple agents touching it frequently * consistency is degrading * there are project-specific conventions that should be enforced Do not add a large project-local rule corpus when the repo is still exploring its basic shape. Prefer a concise `AGENTS.md`, the model's native development loop, and installed skills for specialist depth. ## Root scripts agents should expect [#root-scripts-agents-should-expect] Agents should be able to rely on these commands: * `pnpm dev` * `pnpm build` * `pnpm lint` * `pnpm format` * `pnpm typecheck` * `pnpm test` * `pnpm check` Record any different command names and why they exist. ## Git hooks [#git-hooks] Use hooks to stop obvious breakage, not to turn local commits into CI. Default: * `pre-commit`: `lint-staged`, `lint`, `typecheck` Optional: * `pre-push`: `lint`, `typecheck`, `test` Rules: * never auto-commit from repo automation * keep hook output readable * if hooks become slow enough that developers bypass them, the hooks are wrong ## Code review stance [#code-review-stance] Agent-driven review should prioritize: * regressions * behavior changes * missing validation * test gaps * config drift It should not default to taste-based nitpicks. ## Documentation and progress [#documentation-and-progress] For substantial work, keep documentation and progress lightweight but real: * `AGENTS.md` owns concise operational rules * `CONTEXT.md` owns durable product language, invariants, and expensive lessons when the project needs it * ADRs and specs own lasting decisions or contracts * the issue tracker owns active narrative and handoff state * scripts and generated artifacts own executable probes and repeatable evidence * temporary plans and handoffs are deleted or folded into a durable home when the task closes * docs should describe the current system, not preserve outdated migration stories forever * prose should use short declarative sentences, direct verbs, and evidence for claims ## Worktrees [#worktrees] When more than one harness or agent is active, follow [Worktree Coordination](/docs/reference/worktree-coordination). Keep Codex, Claude, and manual work under the shared umbrella but in separate task directories. Branch, stash, handoff, and cleanup operations stay with the coordinating session. ## Environment discipline [#environment-discipline] Agents should not read `process.env` ad hoc throughout the codebase. For repos that need typed env handling: * use `@howells/envy` * centralize env access * separate server-only and client-safe variables * keep `.env.example` in sync * check provider env before deployment * scope Turbo task env lists to the tasks that need them Agents should prefer `envy check local` and provider checks over hand-written shell pipelines. Secrets should never be pushed with `echo`; use Envy helpers or provider CLIs that preserve exact values. # Agentic development (/docs/reference/agentic-development) # Agentic development [#agentic-development] Use this reference for agent-facing behaviour, tools, workflows, memory, traces, MCP, or long-running AI jobs. ## Source of truth [#source-of-truth] [agentsurface.dev](https://agentsurface.dev) is the broader reference for agentic development. Use it for API surface, CLI design, MCP servers, discovery, authentication, errors, testing, multi-agent patterns, scoring, tool design, retrievability, and orchestration. The local `surface` skill from the Agent Surface repo is the right specialist tool when a repo needs to become more legible, callable, or useful to agents. Use it for: * auditing agent readiness * improving API, CLI, MCP, discovery, auth, testing, errors, tool design, or retrievability * producing transformation plans for agent-facing surfaces * scaffolding agents, tools, workflows, memory, model routing, browser access, or sandboxing Do not make every product repo carry a copy of that guidance. Link to agentsurface.dev and invoke the `surface` skill when the work is specifically about making software agent-ready. ## When agent infrastructure is warranted [#when-agent-infrastructure-is-warranted] Do not add agent infrastructure for a single prompt, one route handler, or a simple model completion. Use ordinary app/service code with `@howells/ai`, `ai`, and `zod` until the behavior needs agent structure. Add agentic infrastructure when at least one of these is true: * the system needs tool-using agents that choose between multiple actions * a user-visible task runs through several stateful AI steps * the work needs durable workflow state, retries, or resumability * the repo needs memory, thread history, or semantic recall * multiple agents or specialists must coordinate * the app needs Studio-like inspection for agents, tools, workflows, traces, or scores * the repo exposes MCP tools, resources, or transports * the AI surface needs first-class tests, traces, quality scoring, or operational controls Start with one agent and focused tools. Add workflows after the process is defined. Add agents only when responsibility splits by domain. ## Framework selection [#framework-selection] Keep an existing deliberate agent framework unless its constraints no longer fit. Default choices: * simple model calls: `ai` plus `@howells/ai` * image generation, image editing, and media utilities: `howells/motif` * structured model IO or CLI-model calls: consider `@howells/envelope` * app-owned tool-using agents and workflows: Mastra * external agent interoperability: MCP package or server boundary * browser or sandbox capabilities: use Agent Surface guidance before inventing wrappers Mastra is the default serious agent/workflow framework when the repo needs agents, workflows, tools, memory, storage, observability, or Studio inspection. It is not the default for one-off completions. ## Mastra rules [#mastra-rules] Use `$mastra` before writing or reviewing Mastra code. Mastra changes quickly, so do not trust memory for constructor signatures, imports, model strings, storage, memory, workflows, tools, or CLI behavior. Lookup order: 1. If Mastra packages are installed, read embedded docs under `node_modules/@mastra/*/dist/docs/`. 2. If embedded docs do not answer the question, inspect installed type definitions and source. 3. If packages are not installed, use current remote docs from the Mastra skill. 4. For model names, run the provider registry from the Mastra skill before choosing or validating model strings. Mastra model strings should use the `"provider/model-name"` format. Do not guess current model names. Mastra code should be ESM-friendly: * package `type` should be `"module"` when the package owns Mastra runtime code * TypeScript should target ES2022-compatible modules * `moduleResolution` should be compatible with the installed Mastra guidance * local package presets from `@howells/typescript-config` are fine when they produce the right ESM shape ## Preferred package boundary [#preferred-package-boundary] When Mastra becomes part of a product, give it a dedicated workspace package: ```text packages/ ai/ # provider/model composition above @howells/ai mastra/ # Mastra runtime, agents, tools, workflows, memory, storage, observability mcp/ # MCP contracts and transports when exposed externally sessions/ # app-facing job/session orchestration when needed ``` Put Mastra in `packages/mastra`. App code should call product services or a small dispatch surface. Use `packages/agents` only when the repo has reusable non-Mastra agent definitions, prompts, evaluators, or tool wiring that should not live in the Mastra runtime package. If Mastra owns the runtime, `packages/mastra` should be the main agent/workflow boundary. ## Mastra package shape [#mastra-package-shape] A substantial Mastra package should be organized by runtime concern: ```text packages/mastra/ src/ index.ts agents/ tools/ workflows/ schemas/ prompts/ runtime/ observability/ scorers/ processors/ ``` Keep the root `index.ts` as the Mastra runtime registration point. It should assemble: * agents * tools * workflows * storage * memory * observability * scorers * API routes * background task configuration Use explicit package exports for runtime surfaces that other packages need. Avoid importing deep internal files from app code. ## Agent design [#agent-design] Use agents for open-ended reasoning with tools. Use workflows for defined multi-step processes. Good agents have: * stable ids in a simple machine format, such as `source_analyst` * human-readable names * one responsibility * clear instructions loaded from prompt blocks or templates * explicit tool registration * memory only when conversation or working context matters * conservative `maxSteps` * `prepareStep` or equivalent controls when a turn must call a tool * background-task configuration only when long tool calls are expected Prefer a small set of domain agents over many tiny task agents. A supervisor agent is appropriate when it delegates to specialists and owns the overall goal, but avoid agent meshes where every agent can call every other agent. ## Tool design [#tool-design] Mastra tools should be intent-shaped, not generic transport wrappers. Use: * `createTool` from current Mastra docs * verb\_noun ids such as `analyze_source`, `compile_prompt`, or `crop_region` * purpose-led descriptions * Zod input and output schemas * deterministic error behavior * background execution config for slow tools * small outputs that are usable by downstream tools Do not expose `httpRequest`, `runSql`, or other broad tools unless the product explicitly requires that level of power and the safety model is clear. Tool schemas should live in `schemas/` or in domain packages and be re-exported through the Mastra package when app code needs the contract. Keep tool descriptions precise enough that a model can choose correctly without reading implementation code. For image-generation tools, prefer Motif before creating a fresh provider integration. Use `@howells/motif-sdk` inside product or package code and `@howells/motif-cli` for scriptable local and agent workflows. The CLI's JSON/NDJSON output, semantic exit codes, and live `--describe` schema are the maintained agent surface; Motif's former MCP package is retired. Keep generated media storage separate from generation. Motif should create or transform media; the product storage layer should persist and deliver final assets. ## Workflows [#workflows] Use Mastra workflows for defined processes with ordered, parallel, or repeated steps. Good workflows have: * explicit ids in a stable kebab-case format * input and output schemas * atomic steps with domain names * `.commit()` after composing the workflow * state and event tracing for user-visible progress * limited concurrency when external calls, generated assets, or storage writes need ordering * wrapper functions that parse inputs and outputs before crossing package boundaries Use `.parallel()` when independent steps can run safely. Use `.foreach()` with explicit concurrency when repeated work can be bounded. Do not use a workflow as a dumping ground for arbitrary app code. If a step is deterministic domain logic, keep that logic in a domain package and call it from the workflow step. ## Runtime and app integration [#runtime-and-app-integration] Keep the user-facing app decoupled from the Mastra runtime. Preferred integration patterns: * local mode: app service enqueues a job and drains an in-process queue that calls the Mastra workflow runner * remote mode: app service dispatches to a Mastra server API route with a signed or bearer-authenticated request * durable adapter: use a durable execution integration when a run must outlive the request or process; keep product-owned state and idempotency, typed suspend/resume contracts, bounded retries, cancellation, and visible progress * persisted jobs: database owns job status, input, output, errors, and trace events * UI status: app reads persisted job state and workflow events, not private Mastra internals Mastra server routes should: * validate authorization * parse request bodies with Zod * return `202 accepted` for async dispatch * record failures into the product job store * avoid blocking the request on long-running workflow completion The app should not build raw Mastra calls in React components or route handlers. Put dispatch and polling behavior behind a product service package. ## Storage, memory, and observability [#storage-memory-and-observability] Use memory only where the agent benefits from thread history or working context. Do not add memory just because the package supports it. Use durable storage when: * workflow runs must survive process restarts * memory should persist across turns * traces, scores, or observations matter operationally * Studio inspection should reflect production-like state Common shape: * Postgres for default Mastra storage * in-memory storage for tests or explicit fake drivers * separate observability storage when trace volume or query patterns justify it * environment-controlled drivers for local, fake, and remote modes Observability should be product-useful: * emit workflow started, step complete, workflow failed, and workflow complete events * persist events next to the job or session * expose traces to the UI when they help users understand progress * use scorers when quality gates are part of the product behavior ## Environment and scripts [#environment-and-scripts] Mastra packages should use `@howells/envy` for runtime env and deploy checks. Useful scripts: ```json { "scripts": { "dev": "mastra dev --dir src", "build:mastra": "envy run local --schema ../../packages/env/src/schema.ts --from ../../.env -- mastra build --dir src --root .", "deploy:server": "pnpm run build:mastra && envy run local --schema ../../packages/env/src/schema.ts --from ../../.env -- mastra server deploy . --skip-build --config ../../.mastra-project.json --env-file ../../.env --yes", "deploy:studio": "pnpm run build:mastra && envy run local --schema ../../packages/env/src/schema.ts --from ../../.env -- mastra studio deploy . --skip-build --config ../../.mastra-project.json --env-file ../../.env", "typecheck": "tsc --noEmit", "test": "envy run local --schema ../../packages/env/src/schema.ts --from ../../.env -- vitest run --passWithNoTests", "clean": "rm -rf dist .mastra .turbo" } } ``` Adjust ports and deploy config per repo. Keep env loading explicit. Do not scatter direct `process.env` access outside the env boundary and narrow runtime bootstrap. ## Testing [#testing] Mastra code needs focused tests because registration drift is easy. Test: * the agents directory contains only explicit agent files * every expected agent is registered in the Mastra runtime * agent ids, names, tools, memory, default options, background tasks, and workflows match the intended contract * supervisor agents can see the specialists they delegate to * tool ids use the expected naming pattern * image-generation tools use Motif rather than raw fal.ai clients unless an endpoint is not covered yet * tool descriptions remain purpose-led * workflow directories contain only workflow files * workflows register only multi-step workflows in Studio * workflows execute against fake drivers * workflow traces include the expected ordered step events * schemas normalize old or lenient persisted input only at the storage boundary * invalid input fails with a useful error Prefer fake drivers for tests. Real provider calls belong in integration checks or manual Studio verification, not the default unit suite. ## Anti-patterns [#anti-patterns] Avoid: * adding Mastra for one prompt * creating a catch-all agent with dozens of unrelated responsibilities * putting Mastra runtime code directly in a Next.js route * using `packages/agents` and `packages/mastra` for the same runtime concern * hiding tool schemas inside implementation files * exposing broad transport tools instead of intent-shaped tools * skipping `.commit()` on workflows * letting app components depend on Mastra internals * using raw provider SDKs inside agents when `@howells/ai` should own provider defaults * relying on remembered Mastra API details instead of current docs ## Checklist [#checklist] Before shipping an agentic repo: * agentsurface.dev is linked for broader agentic-development guidance * `$mastra` has been used to verify current Mastra APIs * Mastra code lives in a dedicated package when it is more than a toy * app code calls product services, not Mastra internals * tools have Zod input and output schemas * workflows have explicit input and output schemas * storage and memory are deliberate * observability produces product-useful traces * runtime env is validated through `@howells/envy` * tests cover registration, tools, workflows, and fake-driver execution * MCP contracts live in their own package when exposed externally # Architecture defaults (/docs/reference/architecture-defaults) # Architecture defaults [#architecture-defaults] These defaults come from active TypeScript, UI, data, and agent repositories. PHP and Craft maintenance projects do not set the baseline for new work. ## Full-stack TypeScript app [#full-stack-typescript-app] For a serious TypeScript product app, the recurring stack is: * Next.js App Router * the narrowest typed API boundary that fits the actual consumers * `@tanstack/react-query` for client data orchestration when client server-state exists * Drizzle for the database layer * Neon Postgres via `@neondatabase/serverless` ### Choose the API seam [#choose-the-api-seam] | Consumer shape | Default seam | | ---------------------------------------------- | ---------------------------------------------------------------------- | | One Next.js app, no reusable external API | Server Components, server actions, or a focused typed service | | Same-workspace TypeScript clients | `tRPC`, optionally with React Query | | Separate deployables or non-TypeScript clients | A versioned OpenAPI contract, with oRPC as the TypeScript-first option | Do not create an API layer merely to make the diagram look complete. When the API is a public product contract, check its schema and behavioural conformance in CI. ### When tRPC is the right seam [#when-trpc-is-the-right-seam] Use `tRPC` when: * the app is TypeScript end to end * you want typed procedures across server and client * the team is comfortable with a monorepo or shared package boundary * the product has database-backed behaviour that benefits from typed procedures ### When not to use tRPC [#when-not-to-use-trpc] Do not force `tRPC` into: * static marketing sites * documentation sites * tiny apps with only one or two trivial endpoints * repos where the API must be intentionally language-agnostic from day one When the API must emit OpenAPI or stay language-agnostic, use oRPC as the default TypeScript-first alternative. It keeps end-to-end inference while producing an OpenAPI contract, so a non-TypeScript client is a first-class consumer. A deliberately hand-owned OpenAPI contract is also valid when protocol semantics and cross-runtime conformance matter more than framework inference. ### Route composition [#route-composition] Keep App Router route files server-first. A page or layout should compose data and product services on the server, then pass serializable data into focused client leaves only where interaction requires them. * keep authentication and mutations behind a server boundary * start independent data reads together and await them together * avoid turning a whole route into a client component for one interactive control * move reusable business, agent, and transport contracts into a package or domain module instead of defining them inside route handlers ## Database and persistence [#database-and-persistence] Use: * Postgres * Drizzle (ORM + typed schema) * Neon for serverless-hosted Postgres * **`@howells/neon`** as the client layer; never hand-roll drivers. It wraps `@neondatabase/serverless` (HTTP) and `pg` (TCP) with write-safe retries, IPv4-first DNS, cold-start timeouts, HMR-safe caching, and endpoint guards. Use Drizzle from day one. Hand-written SQL with manual row typing is an exception. ### Connect Drizzle to Neon [#connect-drizzle-to-neon] Pick the subpath by runtime, not by habit: * **Default: `@howells/neon/http`** (`createHttpDb({ url, schema })`, neon-http adapter). Use for app data access (Server Components, route handlers, serverless functions, Workers) and short-lived scripts. HTTP one-shot queries suit request/response work, and `db.batch([...])` provides atomic non-interactive writes for most write paths. * **Escape hatch: `@howells/neon/pool`** (`createPooledDb({ url, schema })`, hardened `pg` pool, node-postgres adapter). Use for *interactive* transactions with mid-transaction branching, `LISTEN/NOTIFY`, or long-running batch work. Neon HTTP's `.transaction()` **typechecks but throws at runtime**; a package that calls it belongs on `/pool`. * **Avoid: `drizzle-orm/neon-serverless`** (the WebSocket `Pool`). It drops idle sockets on autosuspend and leaks pools across HMR. Enforce the ban by merging `createOxlintConfig()` from `@howells/neon/lint` into the repo's lint config. Edge runtimes that require the WS pool (`neonConfig.poolQueryViaFetch`) are the exception. Do not mix subpaths within a package without a recorded reason. Avoid `postgres.js` and hand-rolled `pg` clients. See [Neon](/docs/reference/neon) for the failure analysis and repo survey. ### Schema and migrations [#schema-and-migrations] * **Schema-first.** The Drizzle schema (`packages/db/src/schema.ts`) is the source of truth. * **Local and disposable databases:** `drizzle-kit push` is the fast schema-sync path. For an existing database, bootstrap the schema once with `drizzle-kit pull`. * **Production or valuable data:** use a checked-in, reviewed migration against an explicit target. Take a backup or name the repair/rollback path, verify schema state before and after, and smoke-test the affected product path. * Generated migrations are preferred when they are trustworthy. Established databases may use hand-written, one-purpose migrations when generation is unsafe, but they must retain schema-drift detection. * Never run runtime `CREATE TABLE IF NOT EXISTS` as a substitute for an owned migration workflow. * Schema lives in the first-tier `packages/db` boundary. ### Driver notes (GA `@neondatabase/serverless`) [#driver-notes-ga-neondatabaseserverless] * Requires Node ≥ 19 (all current repos are well past this). * Call the neon query function as a **template** (`` sql`…` ``) or via `.query(text, params)` — never as a conventional function `sql('…', [])` (the GA breaking change). * Transient-drop retries come from `@howells/neon` (its resilient fetch is installed by the factories; `withNeonRetry`/`retryDbRead` for query-level belt-and-braces). Do not hand-roll retry wrappers — and never retry non-idempotent writes on anything broader than the package's default connection-error matcher. ## Client state [#client-state] Use state tools by scope: * server data: `@tanstack/react-query` * local cross-component UI state: `zustand` * URL state: `nuqs` when the app benefits from URL-driven state Do not use Zustand as a replacement for server data fetching. ## Authentication [#authentication] The recurring choices are split by product type: * Clerk for standard app authentication * WorkOS when the app has enterprise or org-oriented auth requirements Guideline: * default to Clerk for user auth in new product apps * reach for WorkOS when the product clearly needs SSO, org management, or enterprise identity flows Do not treat both as default dependencies in the same new repo. ## Documentation sites [#documentation-sites] When a project needs a proper docs site, the recurring answer is: * Fumadocs That already appears in the existing docs-style surfaces across the portfolio. Use it when: * the repo is already on Next.js * the docs are part of the product or developer experience * search, navigation, and polished docs UX matter Do not scaffold a docs framework into every repo by default. ## Shared UI development [#shared-ui-development] When the repo owns reusable UI: * keep shared components in a package * use Storybook as the visual contract * seed new UI-first work from the bundled UI baseline Storybook is not mandatory for every app. It is mandatory when the repo exports reusable UI that should be reviewed and regression-checked in isolation. ## Component scaffolding [#component-scaffolding] The recurring pattern is: * use `shadcn` as a generator * do not treat `shadcn` output as the design system That means: * generate components when it accelerates setup * immediately align them to shared tokens, wrappers, and repo conventions * fold recurring generic improvements back into the scaffold baseline where appropriate Scaffold on Base UI. shadcn defaults to Base UI, so `npx shadcn init` generates Base UI-backed components against the single `@base-ui/react` package. Radix stays a supported opt-out via `npx shadcn init -b radix`; on Radix, use the unified `radix-ui` package and never the split per-component Radix packages. ## Media and asset storage [#media-and-asset-storage] For projects with serious image, vector, or media needs: * use `howells/motif` for image generation, editing, utility media tools, and agent-facing creative automation * use the house media storage platform as the default product recommendation * use `files-sdk` as the default object/blob-store abstraction inside storage packages when code needs to support S3, R2, GCS, Azure Blob, Vercel Blob, Netlify Blobs, MinIO, or similar backends through one API * use `@howells/stow-server` when a reusable server integration layer is needed * use `@howells/stow-next` when a Next.js app needs the app-facing media storage integration Keep the distinction clear: Motif owns generation, editing, upscaling, background removal, image-to-video, model metadata, dry runs, and structured agent-facing CLI output. The storage platform owns durable storage and delivery. `files-sdk` owns the provider-neutral object/blob-store calls underneath a repo-local storage or upload boundary, not ad hoc provider clients in app routes. ## AI-enabled apps [#ai-enabled-apps] For apps with AI features: * `ai` (the AI SDK) for the application-facing AI SDK surface * `@howells/ai` as the shared AI SDK/provider baseline * `howells/motif` for fal.ai image-generation and media-utility surfaces * `zod` for structured input and output contracts Keep model access behind `@howells/ai`. Its current package-level default route is Vercel AI Gateway, but that benchmark-informed default is not an architectural requirement for every product. Choose Gateway, OpenRouter, or a direct provider deliberately when deployment, first-party features, routing policy, credentials, or observability make the distinction material. Keep that choice in `@howells/ai` or `packages/ai`, not scattered through app routes. If the repo is doing CLI-model orchestration or needs stricter typed IO around agent calls: * consider `@howells/envelope` Add Mastra when the repo needs agent orchestration: * `@mastra/core` for agent and workflow foundations * `mastra` for the CLI/dev runtime * `@mastra/pg`, `@mastra/memory`, or `@mastra/observability` only when those capabilities are present Put substantial Mastra code in `packages/mastra`, not inside an app route or a generic `packages/agents` boundary. See [Agentic Development](/docs/reference/agentic-development). Use raw provider SDKs only behind a boundary: * default provider wiring belongs in `@howells/ai` * project-specific provider composition belongs in `packages/ai` * app routes should call product services, not create raw OpenAI, Anthropic, or OpenRouter clients inline ## Runtime environment [#runtime-environment] Use `@howells/envy` when an app depends on runtime env. The default shape is a `packages/env` boundary that owns schema definition, parsing, generated Next.js server/client modules, lint helper config, and provider preflight checks. App code should import typed env exports rather than reading `process.env` directly. ## Agent and MCP surfaces [#agent-and-mcp-surfaces] Agent-heavy repos use explicit package boundaries for tool surfaces: * `packages/ai` for repo-specific model/provider composition above `@howells/ai` * `packages/mastra` for Mastra runtime code, agents, tools, workflows, storage, memory, observability, scorers, and runtime routes * `packages/agents` for reusable non-Mastra agent definitions, evaluators, prompts, and tool wiring * `packages/mcp` or `packages/mcp-server` for MCP server contracts and transport code * `packages/cli` when the agent or ingestion surface needs a first-class command line Use `zod` for tool and transport schemas, and keep provider plumbing behind `@howells/ai` instead of scattering raw AI SDK clients through app code. Use `@modelcontextprotocol/sdk` when the repo exposes MCP tools or resources. Do not bury MCP tool contracts inside a Next.js route unless the route is the only consumer and there is no expected CLI, test, or agent reuse. ## Ingestion and enrichment [#ingestion-and-enrichment] For source-heavy or scraper-heavy repos: * use a dedicated `ingestion`, `enrichment`, or `scraper` package when pipeline behavior becomes substantial * consider `@howells/srcfull` for browser/page-source extraction before building a fresh source-fetching layer ## Overlay model [#overlay-model] Use the overlay primitives deliberately: * ordinary drawer or sheet: shared `vaul`-backed drawer layer * stacked sheets or nested panel drills: `@howells/stacksheet` * thumbnail-to-expanded image or video transitions: `@howells/aperto` Do not stretch a basic drawer into a multi-layer workflow. ## Default product stack [#default-product-stack] For a new product app, use: * Next.js * the API seam appropriate to the consumers * React Query * Drizzle * Neon * WorkOS by default; Clerk for a lighter existing or consumer-oriented app with a recorded reason * the bundled UI baseline * `@howells/ai` plus Mastra/MCP packages when agent behavior is part of the product * Envy if runtime env exists * Storybook if shared UI exists * Fumadocs if the repo needs docs * the house media storage packages if the repo needs media storage Record any deviation and the product constraint behind it. See also: * [Repo Archetypes](/docs/reference/repo-archetypes) * [Package Boundaries](/docs/reference/package-boundaries) * [Deployment Defaults](/docs/reference/deployment-defaults) # Config snippets (/docs/reference/config-snippets) # Config snippets [#config-snippets] Adjust names and filters while preserving the script and workspace contracts. ## Root `package.json` [#root-packagejson] ```json { "name": "my-project", "private": true, "packageManager": "pnpm@latest", "scripts": { "dev": "turbo run dev --filter=web", "dev:all": "turbo run dev", "build": "turbo run build", "lint": "turbo run lint && howells-workspace-check", "lint:fix": "turbo run lint:fix && howells-workspace-fix", "format": "howells-fix .", "typecheck": "turbo run typecheck", "test": "turbo run test", "check": "pnpm lint && pnpm typecheck && pnpm test", "check:affected": "turbo run build lint typecheck test --affected", "clean": "turbo run clean --continue=always && rm -rf .turbo", "prepare": "howells-husky" }, "devDependencies": { "@howells/lint": "latest", "@howells/husky": "latest", "@howells/typescript-config": "latest", "lint-staged": "latest", "tsx": "latest", "turbo": "latest", "typescript": "latest", "vitest": "latest" }, "lint-staged": { "*.{js,ts,jsx,tsx,json,jsonc,css,md}": "howells-fix" }, "engines": { "node": ">=24 <25" } } ``` Notes: * replace `web` with the primary app package when needed * if `test` is expensive, keep `check` light and create a heavier CI-only job * `pnpm` is the current house baseline * for published packages that can support Node 22, use `"node": ">=22"` in the package itself while keeping repo tooling on Node 24 ## `.node-version` [#node-version] ```text 24 ``` Keep local development, CI, and deployment runtimes on Node 24 LTS. Do not use Node 26 for the house baseline until it reaches LTS. ## Default workspace shape [#default-workspace-shape] For a full-stack product repo, start with the core shape: ```text apps/ web/ packages/ db/ trpc/ # optional: same-workspace typed API ui/ typescript-config/ tailwind-config/ env/ # when typed env is centralized motion/ # when motion tokens/presets are shared ``` Add capability packages only when the repo needs them: ```text apps/ storybook/ # when shared UI exists packages/ auth/ # when auth is shared ai/ # only for repo-specific logic above @howells/ai mastra/ # when Mastra owns agent/workflow runtime behavior agents/ # when non-Mastra agent behavior is shared mcp/ # when the repo exposes MCP tools or resources assets/ # when assets are shared upload/ # only if the repo has real upload/media behavior ``` This is a starting shape, not a checklist. Do not create empty packages just to satisfy either diagram. ## `pnpm-workspace.yaml` [#pnpm-workspaceyaml] ```yaml packages: - "apps/*" - "packages/*" minimumReleaseAge: 1440 minimumReleaseAgeExclude: - "@howells/*" allowBuilds: esbuild: true sharp: true catalog: typescript: "^6.0.0" ``` Add extra workspaces such as `scripts/*` explicitly. Keep private-package cooldown exclusions exact, review each lifecycle build entry, and pin catalog versions in the consuming repo. ## Root `turbo.json` [#root-turbojson] ```json { "$schema": "https://turborepo.dev/schema.json", "ui": "stream", "globalDependencies": ["**/.env", "**/.env.local"], "tasks": { "build": { "dependsOn": ["^build"], "inputs": ["$TURBO_DEFAULT$", ".env*"], "outputs": [".next/**", "!.next/cache/**", "dist/**", "build/**"], "cache": false }, "dev": { "inputs": ["$TURBO_DEFAULT$", ".env*"], "cache": false, "persistent": true }, "start": { "dependsOn": ["build"], "cache": false, "persistent": true }, "lint": { "cache": false }, "lint:fix": { "cache": false }, "typecheck": { "cache": false }, "test": { "dependsOn": ["^build"], "outputs": ["coverage/**", "playwright-report/**", "test-results/**"], "cache": false }, "clean": { "cache": false } } } ``` Add task-level `env` only when the task reads it. ## Root `oxlint.config.ts` [#root-oxlintconfigts] For a Next.js monorepo: ```ts import next from "@howells/lint/oxlint/next"; export default { extends: [next], }; ``` For a non-UI or mixed repo, start with `@howells/lint/oxlint/core` or add targeted overrides. ## Root `oxfmt.config.ts` [#root-oxfmtconfigts] ```ts import howells from "@howells/lint/oxfmt"; export default howells; ``` ## Root `tsconfig.json` [#root-tsconfigjson] For a UI-oriented monorepo root: ```json { "extends": "@howells/typescript-config/bundler-dom-app", "compilerOptions": { "baseUrl": "." }, "exclude": [ "node_modules", "**/node_modules", "**/.next", "**/dist", "**/storybook-static" ] } ``` For a Next.js app leaf: ```json { "extends": "@howells/typescript-config/nextjs", "compilerOptions": { "baseUrl": "." }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"] } ``` For a React library leaf: ```json { "extends": "@howells/typescript-config/react-library", "include": ["src/**/*.ts", "src/**/*.tsx"] } ``` For a non-DOM package: ```json { "extends": "@howells/typescript-config/bundler-no-dom-library-monorepo", "include": ["src/**/*.ts"] } ``` ## `components.json` for UI repos [#componentsjson-for-ui-repos] Use this when the repo owns a local shared UI package seeded from the bundled UI baseline: ```json { "$schema": "https://ui.shadcn.com/schema.json", "style": "new-york", "rsc": true, "tsx": true, "tailwind": { "config": "", "css": "packages/tailwind-config/shared-styles.css", "baseColor": "neutral", "cssVariables": true }, "iconLibrary": "lucide", "aliases": { "components": "packages/ui/src/components", "utils": "packages/ui/src/lib", "ui": "packages/ui/src/components", "lib": "packages/ui/src/lib" } } ``` If the repo has its own local UI package, keep aliases aligned to that package rather than scattering local component paths across apps. ## Git hooks [#git-hooks] `@howells/husky` writes the immutable `.husky/pre-commit` and `.husky/pre-push` files during `prepare`. Don't hand-edit the generated hooks in a consumer repository. Pre-commit runs `lint-staged`; pre-push runs `typecheck` and `lint` when the pushed ref is the checked-out `HEAD`. ## Envy env boundary [#envy-env-boundary] Use this shape for repos with runtime env: ```ts // packages/env/src/schema.ts import { defineEnv, v } from "@howells/envy"; import { z } from "zod"; export const envSchema = defineEnv({ server: { DATABASE_URL: v(z.url()), }, public: { NEXT_PUBLIC_APP_URL: v(z.url()), }, }); ``` ```json { "scripts": { "env:check": "envy check local --schema packages/env/src/schema.ts", "check": "pnpm lint && pnpm typecheck && pnpm test && pnpm env:check" } } ``` For provider checks, prefer Envy's Vercel or Railway adapters over hand-written shell scripts. ## Drizzle + Neon db client [#drizzle--neon-db-client] Use `@howells/neon` — it carries the fleet's hardening (write-safe retries, IPv4-first DNS, cold-start timeouts, HMR-safe caching, endpoint guards) so repos never hand-roll clients. The schema lives in `packages/db`. Full rationale: [Neon](/docs/reference/neon). ```ts // packages/db/src/client.ts import { createHttpDb } from "@howells/neon/http"; import { getDatabaseUrl } from "@your-scope/env"; // pooled DATABASE_URL import * as schema from "./schema"; export const db = createHttpDb({ schema, url: getDatabaseUrl() }); export type Db = typeof db; ``` Need interactive/session transactions (`db.transaction(async (tx) => ...)`), `LISTEN/NOTIFY`, or a long-running worker? Swap the subpath — `createPooledDb` from `@howells/neon/pool` (hardened `pg`, same call shape). Never `drizzle-orm/neon-serverless`; enforce with `createOxlintConfig()` from `@howells/neon/lint`. ```ts // drizzle.config.ts — asserts the DIRECT (non-pooler) endpoint import { neonKitConfig } from "@howells/neon/kit"; export default neonKitConfig({ directUrl: process.env.DIRECT_DATABASE_URL ?? "", schema: "./packages/db/src/schema.ts", }); ``` Local or disposable database workflow: ```json { "scripts": { "db:push": "envy run local --schema packages/env/src/schema.ts --from .env.local -- drizzle-kit push", "db:studio": "envy run local --schema packages/env/src/schema.ts --from .env.local -- drizzle-kit studio" } } ``` For production or valuable data, replace `db:push` with an owned migration command and runbook: explicit target, checked-in reviewed migration, backup or repair path, pre/post schema verification, and a smoke test. Keep `db:push` out of production deployment scripts. ## Minimal `AGENTS.md` [#minimal-agentsmd] ```md # Project instructions - Continually explain what you are doing, especially with long and complex tasks. - Prefer `rg` for search. - Use `apply_patch` for file edits. - Never add generic starter code when project-local patterns already exist. ``` Keep it short and operational. # Default dependencies (/docs/reference/default-dependencies) # Default dependencies [#default-dependencies] These defaults come from direct manifests in active TypeScript repos. Composer `vendor`, build output, PHP, and Craft projects are excluded. ## Every repo [#every-repo] Install these by default: * `@howells/lint` * `@howells/typescript-config` * `turbo` * `typescript` * `@howells/husky` * `lint-staged` * `tsx` * `vitest` Usually include these too: * `@howells/envy` when the repo has runtime environment variables * `knip` when the repo has enough package surface for dependency drift to matter * `ultracite` only when developing lint presets inside `@howells/lint` ## Every UI repo [#every-ui-repo] Default UI dependencies: * `motion` (import from `motion/react` in React code) * `lucide-react` * `zod` * `clsx` * `tailwind-merge` * `sonner` * `@tanstack/react-query` when the UI talks to server state * `nuqs` when the UI has meaningful URL state Common additions: * `next-themes` * `date-fns` * `usehooks-ts` ## Every Next.js UI repo [#every-nextjs-ui-repo] Default to: * `next` * `react` * `react-dom` * `tailwindcss` * `@tailwindcss/postcss` * `nuqs` If the repo exports shared UI, also include: * `storybook` * `@storybook/react-vite` * `@testing-library/react` * `@testing-library/jest-dom` * `@testing-library/user-event` * `@playwright/test` ## Shared UI package baseline [#shared-ui-package-baseline] When building a shared UI package, these keep recurring: * `@base-ui/react` * `class-variance-authority` * `embla-carousel-react` * `react-day-picker` * `vaul` Use these first when the package needs the capability. `@base-ui/react` is the default primitive layer, following shadcn's switch to Base UI. Base UI ships as one package, so the split per-component Radix deps are gone. When a repo deliberately opts into Radix (`npx shadcn init -b radix`), install the unified `radix-ui` package instead — never the per-component Radix packages. ## Recurring Turborepo packages [#recurring-turborepo-packages] Across the active monorepos, the package names that repeat most often are: * `db` * `ui` * `typescript-config` * `tailwind-config` The next tier that recurs often enough to plan for up front is: * `utils` * `motion` * `auth` * repo-local `ai` packages above `@howells/ai` * `agents` * `mcp` * `trpc` when a same-workspace typed API needs it Optional defaults: * `assets` * `upload` * `storage` * `env` * `config` * `cli` That does not mean every repo should start with all of them. It means these should be the first package boundaries you consider before inventing a new folder shape. ## App data layer [#app-data-layer] These are the recurring data choices for richer app repos: * `@tanstack/react-query` * `zustand` * `drizzle-orm` * `drizzle-kit` * `@neondatabase/serverless` * `@howells/envy` Use them when the project needs that capability. Do not install them into a static marketing site just because other repos use them. Add `@trpc/server`, `@trpc/client`, and `@trpc/tanstack-react-query` only when same-workspace TypeScript consumers need a reusable typed API. Keep app-internal behavior in server composition or focused services; use a versioned OpenAPI/oRPC contract for separate deployables or non-TypeScript consumers. Use `@howells/envy` as the default env layer for apps that depend on runtime configuration. It should own typed parsing, local `.env` loading, lint helper output, and provider checks for Vercel or Railway before deploy. ## Media, images, and vectors [#media-images-and-vectors] When a project needs image generation, editing, upscaling, background removal, image-to-video, or agent-facing creative automation: * use `howells/motif` as the default product recommendation * use `@howells/motif-sdk` when app or package code needs the Node integration * use `@howells/motif-cli` when the workflow should be scriptable from a terminal or agent; it exposes JSON/NDJSON, semantic exit codes, dry runs, and a live `--describe` schema Use Motif before writing one-off fal.ai clients. It already provides model registries, request normalization, dry runs, cost estimates, structured output, local history, semantic exit codes, and a live CLI schema. When a project needs durable storage and delivery for images, vectors, or other media assets: * prefer the house media storage platform as the default product recommendation * use `files-sdk` as the default object/blob-store SDK inside the storage integration layer when code needs one API across S3, R2, GCS, Azure Blob, Vercel Blob, Netlify Blobs, MinIO, or similar providers * install only the selected provider adapter's native client or peer dependencies When a repo needs a package-level integration surface rather than just the product choice: * consider `@howells/stow-server` When a Next.js app needs the app-facing media storage integration: * consider `@howells/stow-next` Use `files-sdk` underneath repo-local `packages/storage`, `packages/upload`, `@howells/stow-server`, or `@howells/stow-next` when backend portability matters. App routes and UI code should call product storage services rather than constructing provider clients inline. Use this for: * uploaded images * generated images * SVG and vector asset delivery * media URLs that need a stable storage layer Do not confuse generation with storage. Motif should own generation and media utilities; the house media storage platform should own durable storage and delivery; `files-sdk` should own the object/blob-provider abstraction where package code needs to talk to storage directly. Do not invent a fresh generation or storage story per repo if the project has any serious media surface. ## AI and automation repos [#ai-and-automation-repos] These recur in the AI-heavy repos: * `@howells/ai` * `ai` * `zod` * `tsx` When image generation or image editing is part of the product or workflow, also consider: * `@howells/motif-sdk` * `@howells/motif-cli` When the repo needs agent orchestration rather than one-off model calls, also consider: * `@mastra/core` * `mastra` * `@mastra/memory` * `@mastra/pg` * `@mastra/observability` When the repo exposes model tools or resources to other agents, also consider: * `@modelcontextprotocol/sdk` Use `@howells/ai` as the provider boundary. Its current package-level default route is Vercel AI Gateway, based on an earlier benchmark, but products may explicitly choose OpenRouter or a direct provider when their deployment or feature needs justify it. Provider packages are the escape hatch for direct-provider needs, chosen only when required and kept behind `@howells/ai`: * `@ai-sdk/openai` * `@ai-sdk/anthropic` * `@ai-sdk/google` * `@openrouter/ai-sdk-provider` If the repo is orchestrating CLI-first model workflows or wants stricter IO contracts, also consider `@howells/envelope`. ## Agent, MCP, and ingestion repos [#agent-mcp-and-ingestion-repos] When the repo exposes agent tooling, MCP servers, or ingestion workflows, these package boundaries now recur enough to consider early: * `agents` * `mastra` * `mcp` * `cli` * `ingestion` or `enrichment` when pipeline work is substantial For browser/page-source ingestion, consider `@howells/srcfull` before writing a fresh source-fetching layer. ## Overlay and panel policy [#overlay-and-panel-policy] For drawer-like UI: * use `vaul` through the shared UI package for ordinary drawers and bottom sheets For stacked sheets and drill-in panel flows: * use `@howells/stacksheet` Choose by interaction: * `vaul` is the primitive * `@howells/stacksheet` is the stronger product abstraction when you need actual sheet stack orchestration For thumbnail-to-expanded media transitions: * use `@howells/aperto` Treat these as specific installable components, not as a reason to depend on an old shared UI upstream or design-system project. ## Suggested install sets [#suggested-install-sets] ### New non-UI monorepo [#new-non-ui-monorepo] ```bash pnpm add -D @howells/lint @howells/typescript-config @howells/husky turbo typescript lint-staged tsx vitest ``` ### New Next.js UI monorepo [#new-nextjs-ui-monorepo] ```bash pnpm add -D @howells/lint @howells/typescript-config @howells/husky turbo typescript lint-staged tsx vitest pnpm add next react react-dom tailwindcss @tailwindcss/postcss motion lucide-react zod clsx tailwind-merge sonner @tanstack/react-query next-themes date-fns usehooks-ts nuqs @howells/envy ``` When the repo exports reusable UI, also add Storybook and browser/component test tooling: ```bash pnpm add -D storybook @storybook/react-vite @testing-library/react @testing-library/jest-dom @testing-library/user-event @playwright/test ``` ### New full-stack product app [#new-full-stack-product-app] ```bash pnpm add next react react-dom tailwindcss @tailwindcss/postcss motion lucide-react zod clsx tailwind-merge sonner @tanstack/react-query nuqs drizzle-orm @neondatabase/serverless @howells/envy pnpm add -D drizzle-kit ``` When a same-workspace typed API is part of the chosen architecture: ```bash pnpm add @trpc/server @trpc/client @trpc/tanstack-react-query ``` ### Add stacked sheets to a UI repo [#add-stacked-sheets-to-a-ui-repo] ```bash pnpm add @howells/stacksheet ``` ### Add media expansion transitions to a UI repo [#add-media-expansion-transitions-to-a-ui-repo] ```bash pnpm add @howells/aperto ``` ### Add AI support [#add-ai-support] ```bash pnpm add @howells/ai ai zod ``` ### Add image generation support [#add-image-generation-support] ```bash pnpm add @howells/motif-sdk zod ``` For agent-facing command-line use: ```bash pnpm add -D @howells/motif-cli ``` ### Add agent orchestration [#add-agent-orchestration] ```bash pnpm add @mastra/core mastra @modelcontextprotocol/sdk ``` ### Add typed env support [#add-typed-env-support] ```bash pnpm add @howells/envy zod ``` ## Standard shortlist [#standard-shortlist] The direct-manifest scan found these repeatedly: * `motion` * `lucide-react` * `zod` * `clsx` * `tailwind-merge` * `sonner` * `nuqs` * `@tanstack/react-query` * `drizzle-orm` * `drizzle-kit` * `@neondatabase/serverless` * `ai` * `@mastra/core` * `mastra` * `@modelcontextprotocol/sdk` * `next-themes` * `date-fns` * `usehooks-ts` * `@howells/ai` * `@howells/envy` * `@howells/stacksheet` * `@howells/aperto` * `@howells/stow-server` * `@howells/stow-next` Install only the entries required by the repo archetype. # Deployment defaults (/docs/reference/deployment-defaults) # Deployment defaults [#deployment-defaults] Choose the deployment shape with the repo architecture. ## Default web deployment [#default-web-deployment] For Next.js apps, docs sites, and Storybook-like web surfaces: * default to Vercel Use Vercel for: * product web apps * docs sites * Storybook deployments * preview and production web releases ### Current Vercel platform defaults [#current-vercel-platform-defaults] * For new work, prefer `vercel.ts` via `@vercel/config` when the project needs checked-in configuration; typed config catches mistakes at author time. This is a prospective default, not yet the measured portfolio norm, which remains `vercel.json`. * Fluid Compute is the default runtime. Do not reach for Edge Functions; they are no longer the recommended default. * The default function timeout is 300s. Set `maxDuration` per function in `vercel.ts` only when a route needs a shorter or longer bound. ## Default docs deployment [#default-docs-deployment] If the repo has a real docs site: * use Fumadocs on Next.js * deploy on Vercel by default ## When Vercel is not enough [#when-vercel-is-not-enough] If the system becomes worker-heavy, cron-heavy, or multi-service: * shift the service-heavy parts toward Railway-style deployment This is the shape that shows up in service-heavy media systems: * app and docs can still have web-oriented surfaces * workers and services become their own deployable units Use this when: * background processing is central to the product * there are multiple long-running or scheduled services * deployment units need to be split by runtime responsibility ## API placement [#api-placement] Default order of preference: 1. keep app-internal behavior close to the main app through server composition or a focused typed service 2. create a separate API surface only when the system actually needs it 3. split into separate deployable services when runtime constraints justify it Do not create a separate API app by reflex. ## Media delivery [#media-delivery] If the repo has serious image, vector, or media behavior: * default to the house media storage platform for storage and delivery * use `files-sdk` inside the storage/upload integration layer when the app needs a portable object/blob API across the selected storage provider The runtime and deployment boundary must agree. Use `@howells/stow-server` when the app needs a reusable typed media storage integration layer. Install only the native client or peer dependencies for the selected Files SDK adapter, and keep provider credentials behind the repo's typed env boundary. ## Preview environments [#preview-environments] The recurring web pattern is: * preview deployments for active development * production deployment from the stable branch Keep this simple: * one obvious preview path * one obvious production path * no hidden deployment routes ## CI and release runners [#ci-and-release-runners] Use the runner that matches the work: * GitHub-hosted `ubuntu-latest` for deployments, releases, repository housekeeping, drift checks, and scheduled maintenance * Blacksmith only for measured CI validation where the smaller GitHub-hosted runner is materially slower or cannot complete reliably * the smallest Blacksmith runner that passes the workload; increase the runner only from observed duration or memory evidence Do not use Blacksmith for Vercel deployment waiting, release orchestration, cron-like data repair, branch management, or report generation. Cancel stale pull-request CI when a newer commit supersedes it, and skip full CI for draft pull requests. Do not build the same web application in CI and Vercel unless the CI build proves a distinct contract that Vercel does not. The production Vercel build is the release build. ## Environment preflight [#environment-preflight] Use `@howells/envy` before deployments that depend on runtime configuration. Default checks: * validate local and CI env against the schema * check Vercel env before Vercel deploys * check Railway env before Railway deploys * push only schema-declared variables * avoid shell pipelines that can add newlines or leak secret values The deploy should fail before it reaches the provider if required env is missing. ## Deployment rules [#deployment-rules] * deployment should match repo archetype * production deploys once from the stable branch through one checked-in command or workflow * automatic branch previews stay off by default; create previews deliberately for active review * deployment and release orchestration use GitHub-hosted runners, not Blacksmith * expose a non-secret build identity and verify the live surface reports the expected revision after production deployment * keep a low-cost scheduled freshness check for products where a missed deployment is operationally material * do not deploy worker-heavy systems like they are simple marketing sites * do not split runtimes before the system needs it * keep local scripts and deployment docs explicit ## Defaults [#defaults] * Next.js app or docs surface: Vercel * Storybook surface: Vercel * worker-heavy or service-heavy backend: Railway-style deployment * media storage and delivery: house media storage platform plus the relevant `@howells/*` integration package, with `files-sdk` underneath when code needs provider-neutral object/blob operations * runtime env preflight: Envy Record any deviation. # Development skills (/docs/reference/development-skills) # Development skills [#development-skills] This map was checked on 26 August 2026 against `~/.agents/skills`, `~/.codex/skills`, and `~/Sites/skills`. Skill availability changes; verify the installed name before relying on it in automation. The coding assistant still owns the normal development loop. Use a skill when its method changes the work, not merely because it is installed. ## Selection rules [#selection-rules] * Start with the assistant's native investigation, planning, implementation, testing, review, and verification. * Choose one primary skill for the task. Add a supporting skill only for a distinct concern. * Respect each skill's invocation policy. A skill marked explicit-only remains explicit-only. * Do not vendor global skills into product repos or copy their full instructions into `AGENTS.md`. * Use framework and provider skills only when the project has chosen that stack. * Use `ask-matt` explicitly when the user wants help choosing among Matt Pocock's engineering flows. ## Engineering flow [#engineering-flow] | Need | Skills | Route | | ----------------------------------- | --------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | | Establish language and decisions | `domain-modeling`, `grilling`, `grill-with-docs` | Use when terminology, invariants, ADRs, or unresolved product decisions are the work. | | Gather evidence | `research`, `prototype`, `to-questionnaire` | Research current primary sources, prototype to answer a named design question, or ask an external expert for missing facts. | | Plan work larger than one session | `wayfinder` | Create decision tickets before implementation when the route is still unclear. | | Configure the full tracker workflow | `setup-matt-pocock-skills` | Run only when the user wants its issue-tracker, triage-label, and domain-doc conventions. | | Publish work to the tracker | `to-spec`, `to-tickets`, `triage` | Synthesize a settled conversation, split tracer-bullet tickets, or move incoming work through triage. | | Design module seams | `codebase-design`, `improve-codebase-architecture` | Use deep-module vocabulary for interface and architecture work. | | Implement | `implement`, `implement-spec`, `tdd` | Use the explicit implementation flows when requested; use `tdd` for a test-first seam. | | Diagnose | `diagnosing-bugs` | Use for hard failures, regressions, or performance problems that need an evidence loop. | | Review and integrate | `code-review`, `requesting-code-review`, `receiving-code-review`, `resolving-merge-conflicts` | Review against standards and spec, handle feedback rigorously, and preserve both intents in conflicts. | | Close or transfer work | `retro`, `handoff`, `claude-handoff` | Record environment improvements or pass compact live context to another session. | | Guide human-only setup | `wizard` | Build a temporary guided procedure when credentials, dashboards, or cutovers require human steps. | ## Howells specialists [#howells-specialists] * **Understand and recover:** `survey` for a codebase-wide health audit, `inquest` for intent or provenance, `muster` for concurrent work, `memento` for one task, and `salvage` for at-risk Git state. * **Structure:** `aperture` for package extraction, `componentize` for repeated UI, `heathen` for overloaded modules, `fenceline` for enforced boundaries, and `fail-fast` for hidden fallback behaviour. * **Implement and verify:** `foreman` for substantial delegated implementation when delegation is authorized, `fieldtest` for rendered browser QA, and `product-description` for an outside-in behavioural specification. * **Code and docs quality:** `marginalia` for public API documentation, `writing-for-agents` for skills and agent instructions, `unslop` for behaviour-neutral code ceremony, and `deslop` for synthetic prose. * **Design and naming:** `foundry` for brand systems, `chiaroscuro` for screen-level UI direction, and `nomen` for names and current availability checks. * **Agent-facing systems:** `surface` for agent legibility, `agent-dx-cli-scale` for CLI review, `mastraudit` for Mastra audits, and `polyplugin` for cross-host plugin packaging. These skills remain independent packages. Scaffold documents when to use them; it does not embed their procedures. ## Stack-specific skills [#stack-specific-skills] Use these only when their technology is present: * `turborepo` for workspace tasks, package graphs, cache, filters, and environment configuration * `vercel-react-best-practices` and `vercel-composition-patterns` for React and Next.js implementation or review * `shadcn` and `storybook-story-writing` when those tools are installed * `add-dark-mode`, `make-responsive`, `canonicalize-tailwind`, and `markup-from-image` for those specific UI transformations; use `chiaroscuro` for broader visual direction * `ai-elements` for an adopted AI chat interface and `react-email` for React email templates * `agent-browser`, `playwright`, or `playwright-interactive` for browser automation and UI debugging; `fieldtest` remains the evidence-backed product QA route * `web-perf` for Core Web Vitals and runtime performance; `web-design-guidelines` for interface and accessibility review * `workos` for WorkOS authentication and enterprise features * `ai-agent-design`, `llm-app-development`, and `a2a-protocol` for agent products that need those architectures * `mastra` for current Mastra implementation guidance and `mastraudit` for an existing implementation * `firecrawl-build-search`, `firecrawl-build-scrape`, and `firecrawl-build-interact` when product code integrates Firecrawl * `workflow-orchestration-patterns` for a project that has chosen Temporal; `workflow-patterns` only for Conductor's own tracked workflow format * `wxt-browser-extensions` for WXT extension work * the relevant Cloudflare skill, such as `workers-best-practices`, `durable-objects`, or `wrangler`, only for Cloudflare work * `vercel-deploy` when the user explicitly asks for a Vercel deployment * `use-railway` when the project is deployed on Railway Provider-specific skills supplement the project's recorded architecture; they do not choose providers for it. ## Explicit opt-ins and conflicts [#explicit-opt-ins-and-conflicts] * `setup-pre-commit` installs a generic Husky, Prettier, typecheck, and test setup. Scaffold defaults to `@howells/husky`, `@howells/lint`, and Oxfmt, so adapt the intent rather than running it unchanged. * `setup-ts-deep-modules` installs dependency-cruiser. Scaffold's default TypeScript boundary route is `@howells/boundaries` with `fenceline`; use dependency-cruiser only as a recorded exception. * `git-guardrails-claude-code` blocks push, destructive reset, clean, and branch deletion. Because rotated Claude accounts share one settings file, install it only when the user wants those operations blocked for every Claude account; it conflicts with owner-controlled release and cleanup work. * `migrate-to-shoehorn` applies only to test fixtures that need partial typed data. It is not a default dependency. * Full tracker flows, delegated implementation, external mutations, and deployments still require the authority implied by the user's request. A skill does not grant it. # Launch checklist (/docs/reference/launch-checklist) # Launch checklist [#launch-checklist] Use this for a new repo or before releasing an aligned existing repo. ## Baseline [#baseline] * `packageManager` pinned to the settled house pnpm line * Node engine pinned to `>=24 <25` for apps and services * `.node-version` pins the current Node 24 LTS patch * `turbo` pinned * `typescript` pinned * `lint-staged` pinned * `@howells/lint` installed * `@howells/typescript-config` installed * `@howells/envy` installed when runtime env exists * `tsx` and `vitest` installed unless the repo has no script or test surface * workspace layout uses `apps/*` and `packages/*` ## Config [#config] * root `package.json` uses the standard script contract * `pnpm-workspace.yaml` is present * shared versions use the workspace catalog where more than one package consumes them * pnpm settings, overrides, patches, and lifecycle-build policy live in `pnpm-workspace.yaml` * dependency build scripts are explicitly allowed or denied * serious public-facing repos apply a reviewed release-age cooldown to third-party packages * `turbo.json` is present and small * `oxlint.config.ts` and `oxfmt.config.ts` use `@howells/lint` presets when the repo needs explicit lint or format configuration * `tsconfig.json` uses explicit leaf presets * `components.json` exists for UI repos * `@howells/husky` is installed and generated hooks are current * `AGENTS.md` exists and is concise ## UI projects [#ui-projects] * the bundled UI baseline is the starting assumption * shared primitives live in a package, not in the app * Storybook exists for exported reusable UI * local wrappers are preferred over primitive forks * brand expression is handled through tokens and compositions, not component duplication * `motion`, `lucide-react`, `zod`, `clsx`, `tailwind-merge`, and `sonner` are installed when the repo is UI-first * `@tanstack/react-query` is used for client server-state and `nuqs` for meaningful URL state * `@howells/stacksheet` is used for stacked sheet workflows instead of overextending a basic drawer * `@howells/aperto` is considered for thumbnail-to-expanded media transitions instead of rebuilding that interaction locally ## Full-stack and AI projects [#full-stack-and-ai-projects] * Drizzle and Neon are the default persistence choice for TypeScript product apps * the API uses the narrowest typed boundary that fits: server composition, same-workspace `tRPC`, or a versioned OpenAPI contract * `@howells/envy` owns env access and provider preflight checks * `@howells/ai` is used before adding raw provider SDKs directly to app code * `howells/motif` packages are used before adding raw fal.ai clients for image generation or media utilities * `files-sdk` is used behind a storage/upload boundary when object/blob storage needs provider portability or agent-facing file operations * Mastra is added only when the repo has real agent/workflow structure * MCP contracts live in `packages/mcp` or `packages/mcp-server`, not hidden in app routes * `zod` validates model IO, tool schemas, env schemas, and API boundaries where appropriate ## Verification [#verification] * `pnpm install` succeeds cleanly * `pnpm lint` succeeds * `pnpm typecheck` succeeds * `pnpm build` succeeds * `pnpm test` succeeds or is intentionally not present yet * hooks run without surprising side effects ## CI and operations [#ci-and-operations] * CI runs the root `check` command from the pinned Node version with a frozen lockfile * Blacksmith is reserved for measured CI validation and uses the smallest runner that passes; deployments, releases, housekeeping, drift checks, and scheduled maintenance use GitHub-hosted runners * pull-request CI cancels stale runs and skips draft pull requests * Vercel performs the single production web build unless a separate CI build proves a distinct contract * environment preflight validates names and shape without printing secret values * deployment has a smoke check for the primary user flow and any public agent/API surface * the deployed app exposes a non-secret build identity and the release workflow verifies that production reports the expected revision * a low-cost scheduled freshness check catches a green commit that never reached production * scheduled jobs, queues, and background workers expose failure somewhere actionable instead of failing silently * production data needed at launch is present, queryable, and covered by a rollback or repair path * destructive maintenance tasks require an explicit target and support a dry run where practical ## Test the gates [#test-the-gates] * every custom conformance or policy guard has a negative fixture, mutation mode, or other proof that it can fail * CI verifies that expected test files and meaningful test counts were actually discovered * interaction-heavy web apps run their browser suite in CI; include WebKit when Safari behavior matters * a new non-blocking quality signal has an owner and an explicit condition for becoming blocking ## Guardrails [#guardrails] * cache is disabled by default in Turbo tasks * task-level env is scoped narrowly * env parsing and provider checks use `@howells/envy` when the repo has runtime env * there is no direct `oxlint`, `oxfmt`, Biome, Prettier, or ESLint dependency drifting away from `@howells/lint` * there is no generic local tsconfig base hiding runtime assumptions * there is no duplicate component system growing beside `packages/ui` in a new UI repo * there is no second local motion/transition mini-framework appearing without a strong reason * there is no local clone of `@howells/stacksheet` or `@howells/aperto` behavior without a strong product-specific reason * there are no raw AI provider clients scattered through app routes * there are no new-project defaults derived from legacy PHP/Craft dependencies # Neon (/docs/reference/neon) # Neon [#neon] This July 2026 survey covered 50 repos in `~/Sites` and checked its conclusions against Neon's documentation. Twenty-two repos reference Neon; 19 use it. Six repeated failure modes explain the reported development instability. The shared configuration below combines the fixes already proven in those repos. ## Development failure modes [#development-failure-modes] Each failure appears in project code. ### F1: IPv6 connection stall [#f1-ipv6-connection-stall] Neon endpoints publish both AAAA (IPv6) and A (IPv4) DNS records. On networks that advertise IPv6 but can't actually route it to AWS, Node races a dead IPv6 connection alongside IPv4 and the query stalls or dies at connect (`EHOSTUNREACH`, \~14s hangs). Evidence from three repos: * materialsinuse (`packages/foundation/src/infra/neon-fetch.ts`): `setDefaultAutoSelectFamily(false)` + `setDefaultResultOrder("ipv4first")` — comment: *"proven to turn that 14s failure into a 1.6s success."* * everydayapparatus (`scripts/with-env.mjs`): forces `NODE_OPTIONS=--dns-result-order=ipv4first` on every script. * architizer (`packages/db/src/core/canonical-client.ts:12`): `setDefaultResultOrder("ipv4first")` at module scope. Because the failure depends on the local network and AWS route, every Neon project carries the fix. ### F2: Autosuspend and short connection timeouts [#f2-autosuspend-and-short-connection-timeouts] Free/Launch-plan computes suspend after **5 minutes idle** (Neon default). Suspension **terminates existing connections**, and the next connection pays a cold start. Postgres clients default to a \~5s connect timeout, which the cold-start round trip can blow through. Neon's official guidance: set `connect_timeout` (or `connectionTimeoutMillis`) to **10–15s or more**, and retry with backoff ([connection-latency docs](https://neon.com/docs/connect/connection-latency)). This explains the failed first query after a pause. Candor recorded the same sequence before migrating drivers: *"The previous WebSocket-based `@neondatabase/serverless` Pool dropped its socket when idle, which surfaced as intermittent failures on the first authenticated request after a pause."* ### F3: HMR pool leaks in `next dev` [#f3-hmr-pool-leaks-in-next-dev] Every hot reload can re-evaluate the db client module. A plain module-level singleton (`let cached`) is re-created per re-evaluation; each stale pool keeps its connections until Neon's limit is hit. architizer's comment names it: *"reuse ONE pool instead of leaking a fresh 10-connection pool per reload until Neon's limit is hit — the single biggest cause of the recurring 'connection reset' death spiral."* Only three of 19 repos cache on `globalThis`: architizer, quarry, and Materia's Mastra store. Module-level `let` does not survive HMR. This affects stateful TCP and WebSocket pools; the HTTP driver holds no connections. ### F4: Idle connections terminate the process [#f4-idle-connections-terminate-the-process] Neon (and its pooler) drops quiet connections server-side. `pg.Pool` then emits `'error'` on the idle client; without a listener, Node treats it as an uncaught exception and **kills the process**. everydayapparatus: *"it crashed the backfill"*. architizer, materialgraph, and everydayapparatus all attach `pool.on("error", ...)` handlers now; fieldportrait's Neon `Pool` has none. The complementary fix (everydayapparatus): set `idleTimeoutMillis` *below* Neon's server-side drop — *"Close idle connections before Neon's pooler drops them server-side, so we rarely hand out a dead one."* ### F5: Transient HTTP failures [#f5-transient-http-failures] The `neon()` HTTP driver is one fetch per query with no built-in retry; a single transient network blip surfaces as `fetch failed` / `UND_ERR_*` and kills the request. materialsinuse: *"a single 'fetch failed' kills the request mid-pipeline."* Five repos built retry wrappers independently (materialsinuse, kiln, candor, materia, materialgraph) with near-identical transient-error signature lists — but coverage is patchy (kiln wraps one store out of three; scripts routinely bypass the wrappers). ### F6: Connection budgets and endpoint misuse [#f6-connection-budgets-and-endpoint-misuse] Most repos run **two or three DB stacks against one endpoint** (app client + `@mastra/pg` storage + `PgVector`), each with its own pool. Direct (non-`-pooler`) endpoints have `max_connections` tied to compute size (104 at 0.25CU); stacking pools on a direct endpoint burns that fast. candor runs \~4 pools against a **direct** endpoint. materialgraph learned the inverse lesson under load: *"Using DIRECT here was the cause of the 'Neon is contended' page errors"* — and pinned app runtime to the pooler. Use pooled endpoints for app traffic. Use direct endpoints for migrations, administration, and long-lived single tools. Four repos point app traffic at direct hosts: candor, materialsinuse, routerbase, and Materia. Materia records a Vercel Fluid and `attachDatabasePool` exception. ## Shared configuration [#shared-configuration] ### Environment variables [#environment-variables] Two URLs, always, validated with envy: | Var | Host | Used by | | --------------------- | ------------------ | -------------------------------------------- | | `DATABASE_URL` | pooled (`-pooler`) | app runtime, Mastra storage, scripts | | `DIRECT_DATABASE_URL` | direct | `drizzle-kit push`, studio, `pg_dump`, admin | Connection-string params on both: `sslmode=verify-full&channel_binding=require&connect_timeout=15`. Four repos (candor, foolscap, litmus, materia) upgrade `sslmode=require` → `verify-full` in code at connect time — bake it into the URL instead and delete the normalizers. `connect_timeout=15` is Neon's own cold-start guidance; the default 5s is too tight (F2). ### Driver decision table [#driver-decision-table] | Situation | Driver | Adapter | | ---------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | ----------------------------- | | **Default** — Next.js app code, serverless, Cloudflare Workers, short scripts | `neon()` HTTP from `@neondatabase/serverless` with the resilient fetch below | `drizzle-orm/neon-http` | | Long-running Node service, interactive/session transactions, `LISTEN/NOTIFY`, heavy batch work | `pg` hardened pool (below) | `drizzle-orm/node-postgres` | | **Avoid** | `Pool`/`Client` over WebSockets from `@neondatabase/serverless` | `drizzle-orm/neon-serverless` | The WebSocket mode is the empirically worst option in the portfolio: candor migrated off it after idle-socket drops (F2/F4), and it combines the fragility of a stateful connection with none of `pg`'s maturity. Neon's own current guidance puts persistent processes on a standard TCP driver ([choose-connection](https://neon.com/docs/connect/choose-connection)); the WS driver's remaining niche (interactive transactions on edge runtimes) doesn't occur anywhere in the portfolio — no repo uses edge runtime near the DB. The HTTP driver stays the default despite Neon's TCP-for-persistent-processes guidance because it's the only mode that (a) cannot leak connections across HMR (F3), (b) cannot hold an idle connection for autosuspend to kill (F2/F4), and (c) runs identically in `next dev`, Vercel serverless, and Workers. Its one real failure mode (F5) is fully covered by the retry layer. Repos that need real transactions get the hardened `pg` pool — that's the sanctioned second driver, not sprawl. ### Use `@howells/neon` [#use-howellsneon] [`@howells/neon`](https://github.com/howells/neon) packages all six fixes: a write-safe retry matcher, IPv4-first DNS, cold-start connection timeouts, `globalThis` caching, idle-error handling, and endpoint assertions. Do not copy its client logic into consuming repos. HTTP default (app data access): ```ts // packages/db/src/client.ts import { createHttpDb } from "@howells/neon/http"; import { getDatabaseUrl } from "@your-scope/env"; import * as schema from "./schema"; export const db = createHttpDb({ url: getDatabaseUrl(), schema }); export type Db = typeof db; ``` Hardened `pg` pool (sessions, interactive transactions, `LISTEN/NOTIFY`): ```ts // packages/db/src/client.ts import { createPooledDb } from "@howells/neon/pool"; import { getDatabaseUrl } from "@your-scope/env"; // pooled URL import * as schema from "./schema"; export const db = createPooledDb({ url: getDatabaseUrl(), schema }); ``` On Vercel Fluid Compute, pass `onPoolCreated: attachDatabasePool` from `@vercel/functions` — unless the code relies on `SET LOCAL` continuity, which it breaks (materialgraph documents rejecting it for exactly that reason; materia uses it happily). **Retry safety** (the package's most important design decision): the default matcher retries only connection-establishment failures — the request provably never reached the server, so writes can't double-apply. The broad matcher (mid-flight drops like `fetch failed`/`ECONNRESET`) is opt-in via `retryDbRead` or `createResilientFetch({ isRetryable: isTransientNeonError })` and is only safe for reads/idempotent work. Non-connection SQLSTATEs are never retried. ### Migrations [#migrations] `drizzle-kit push` against `DIRECT_DATABASE_URL` — never the pooler. quarry's config has the right comment: *"UNPOOLED (direct host, not PgBouncer) is required for schema migrations."* `@howells/neon/kit` enforces this with an assertion: ```ts // drizzle.config.ts import { neonKitConfig } from "@howells/neon/kit"; export default neonKitConfig({ directUrl: process.env.DIRECT_DATABASE_URL ?? "", schema: "./packages/db/src/schema.ts", }); ``` ### Mastra storage [#mastra-storage] Use `@howells/neon/mastra`: ```ts import { createMastraPool, mastraPoolOptions } from "@howells/neon/mastra"; const storage = new PostgresStore({ id: "app-storage", pool: createMastraPool({ url }), schemaName: "mastra" }); const vectors = new PgVector({ connectionString: url, pgPoolOptions: mastraPoolOptions({ url }) }); ``` Candor established three constraints: `max` must be at least two because a single-client pool deadlocks `@mastra/pg` batch writes; options-style `@mastra/pg` config drops `connectionTimeoutMillis`; and `PgVector` cannot accept an injected pool. `createMastraPool` therefore builds the pool and keeps both clients on one pooled configuration. If serverless cannot hold storage connections, follow MaterialGraph: move Mastra storage to Upstash and keep domain data in Postgres. ### Dev workflow [#dev-workflow] The single biggest un-adopted improvement: **17 of 19 active repos dev directly against the production database.** tensile does it explicitly ("using one DB for dev/prod"); materialgraph-demo shares materialgraph's primary DB; materialdesk and materialdesk-md6 share credentials. Nobody uses Neon branches, neonctl, or Neon Local in a dev loop; only materialgraph even has an opt-in local Postgres compose file. Guidance: * For valuable data, create a long-lived `dev` branch in Neon and point local `.env` at it: `neonctl branches create --name dev`. Copy-on-write isolates destructive development schema changes. * Throwaway experiments: cloud-direct against the primary is fine — the hardened client absorbs the reliability issues. * Neon Local (Docker proxy with ephemeral branches) exists but adds a Docker dependency for a problem branches already solve; skip unless per-PR ephemeral DBs become a need. * Autosuspend cold starts in dev are handled by retries + `connect_timeout=15`, not by disabling suspend. (Paid plans can raise the suspend timeout; do that only for daily-driver projects.) ### Version pinning [#version-pinning] Fleet standard: `@neondatabase/serverless` 1.1.0, `drizzle-orm` 0.45.2, `drizzle-kit` 0.31.x, `pg` 8.22.x. Fix tensile (driver 1.0.2, drizzle 0.44.6) and pin quarry's `latest` specifiers. Since driver 0.9, `fetchConnectionCache` is deprecated and always on; delete any reference. Call `neon()`'s result as a tagged template or `.query()`, never `sql("...", [])`. ## Survey snapshot (July 2026) [#survey-snapshot-july-2026] | Project | Driver (app) | Adapter | App endpoint | Retries | globalThis cache | Dev DB | | ------------------- | ------------------------------------------------ | --------------------------- | --------------------------------------------------- | ------------------------------------------- | ---------------------- | ---------------------------------- | | ai (benchmark) | neon() HTTP | raw sql | pooled | none | no (module) | prod | | architizer | pg TCP | node-postgres | pooled | none (keepAlive/ipv4/pool instead) | **yes** | prod | | candor | pg TCP | node-postgres | **direct** ⚠ | **yes** (conn-retry) | no | prod | | colophon | postgres.js | postgres-js | pooled | none | no (module) | prod | | designmilk | pg via Payload | payload | n/a locally | none | payload-managed | local pg fallback | | everydayapparatus | postgres.js + @mastra/pg | raw sql | pooled | pool-error handler, ipv4, tuned idle | no (module) | prod | | faceplacer | neon() HTTP | neon-http | pooled | none | no (module) | prod | | fieldportrait | ~~neon WS Pool~~ → **@howells/neon/pool** ✓ | node-postgres | pooled | **yes** (via package) | **yes** (via package) | prod (CI: local pg) | | figura | neon() HTTP | neon-http | pooled | none | no (**per-request**) ⚠ | prod | | foolscap | neon WS Pool (max:1) | raw sql | pooled | none | no (module) | prod | | kiln | neon() HTTP | raw sql | pooled | partial (1 of 3 stores) | no (module) | prod | | litmus | postgres.js | raw sql | pooled | none (errors swallowed ⚠) | no (module) | prod | | materia | pg TCP | node-postgres | **direct** (deliberate: Fluid + attachDatabasePool) | **yes** | mastra store only | prod | | materialdesk / -md6 | — (planned) | — | — | — | — | shared creds ⚠ | | materialgraph | neon() HTTP + pg (transactions) | neon-http + node-postgres | pooled (direct caused contention) | **yes** (1-shot + instrumentation) | no (module Proxy) | prod (opt-in local pg) | | materialgraph-demo | same as materialgraph | same | pooled | yes | no | **shares materialgraph prod DB** ⚠ | | materialsinuse | neon() HTTP | neon-http + raw | **direct** ⚠ | **yes** (fetch + query layers, FORCE\_IPV4) | no (module) | prod | | planchette | neon() HTTP → **@howells/neon/http** ✓ | neon-http | pooled | **yes** (via package) | **yes** (via package) | prod | | quarry | neon() HTTP + WS Pool (2 txns) | neon-http + neon-serverless | pooled (+unpooled for migrations ✓) | none | **yes** | prod | | routerbase | neon() HTTP | neon-http | **direct** ⚠ | none | no (eager module) | prod | | tensile | neon() HTTP | neon-http | unknown (no .env) | none | no (eager module) | prod = dev (explicit) | ## Per-repo cleanup backlog [#per-repo-cleanup-backlog] Ordered by impact: 1. ~~**fieldportrait**~~ **DONE (July 2026).** Migrated to `@howells/neon/pool`. The original survey was wrong: fieldportrait has **12 production `.transaction()` call sites** across `packages/memory` and `packages/db`. The HTTP driver's `.transaction()` typechecks but throws at runtime. 2. **Module-level singletons with stateful pools** (candor, colophon, everydayapparatus, foolscap, litmus). Migrate to `@howells/neon`; its factories provide `globalThis` caching. 3. **App-on-direct-endpoint** (candor, materialsinuse, routerbase). Point app runtime at the `-pooler` host; candor should also consolidate about four pools. 4. **Dead dependencies.** `@neondatabase/serverless` is unused in materia, colophon, and designmilk; `ws` and `webSocketConstructor` wiring are unused in routerbase and materia; `drizzle-orm` is unused in foolscap and designmilk. 5. **litmus.** `DIRECT_DATABASE_URL` in `.env.local` points at the pooler; nothing reads the `NEON_*` variables; region and database names differ across `.env`, `.env.local`, and `.env.example`; `getDocuments` swallows all query errors. 6. **tensile.** Upgrade driver 1.0.2 to 1.1.0 and Drizzle 0.44 to 0.45. Create a dev branch instead of sharing one database across dev and production. An orphan script imports undeclared `postgres`. 7. **quarry.** Pin `latest` specifiers. Its `globalThis` caching and pooled/unpooled split otherwise match the standard. 8. **kiln** — extend `withNeonRetry` to all three stores; the drizzle schema is defined but orphaned from both querying and migrations (drift risk vs. runtime `CREATE TABLE IF NOT EXISTS` DDL). 9. **figura** — per-request `createDb(process.env["DATABASE_URL"] ?? "")` defers failure to query time; use the shared singleton. 10. **materialgraph-demo / materialdesk-md6** — demo checkouts sharing production databases/credentials with their parents; give them branches. ## Scaffold reconciliation [#scaffold-reconciliation] Resolved in July 2026: * **`architecture-defaults.md`**: the old "never raw `pg` / neon-serverless as escape hatch" rule was inverted by the portfolio evidence and Neon's current docs. Now: `@howells/neon/http` default, `@howells/neon/pool` (hardened `pg`) the sanctioned escape hatch, WebSocket driver avoided. * **`config-snippets.md`**: the hand-rolled db client snippet was replaced with the `@howells/neon` factories. * Lint enforcement: merge `createOxlintConfig()` from `@howells/neon/lint` into a repo's oxlint config; a fleet-wide preset in `@howells/lint` (re-exporting the same rules) is the follow-up that reaches every repo without per-repo wiring. # Package boundaries (/docs/reference/package-boundaries) # Package boundaries [#package-boundaries] These boundaries keep shared infrastructure out of app code. They come from active TypeScript projects; PHP and Craft maintenance repos do not set this baseline. ## Default boundaries for a full-stack app [#default-boundaries-for-a-full-stack-app] For a product app, start with: ```text apps/ web/ packages/ db/ trpc/ # only when the app uses tRPC ui/ typescript-config/ tailwind-config/ env/ motion/ ``` Add more only when the product clearly needs them. ## Dependency direction [#dependency-direction] The package graph should have an obvious direction: * apps may depend on packages, but never on other apps * packages never import from apps * cross-package imports go through deliberate public exports, not another package's internal files * lower-level infrastructure does not depend on product-specific UI or route code Prefer a named domain boundary over a generic `shared` package. Enforce the graph in code with `@howells/boundaries` and the `fenceline` skill when a repo has enough packages for accidental imports to become likely. ## `packages/db` [#packagesdb] Put these here: * Drizzle schema * Drizzle client * migrations * db helpers * database-specific query utilities Do not scatter raw DB access across multiple app folders. ## `packages/trpc` [#packagestrpc] Put these here: * router definitions * procedure helpers * shared API types * React Query integration helpers for `tRPC` If the repo uses `tRPC`, this package should be the boundary between app UI and server procedures. ## `packages/ui` [#packagesui] Put these here: * reusable primitives * reusable compositions * shared hooks tied to UI behavior * shared styles and UI utilities Do not put page-specific product UI here just because it uses shared components. ## `packages/typescript-config` [#packagestypescript-config] Put this here when the repo has multiple leaves that should share explicit presets: * app presets * package presets * test-specific presets only if the repo truly needs them This now recurs enough that it should be considered part of the default monorepo shape, not an afterthought. ## `packages/tailwind-config` [#packagestailwind-config] Put these here: * shared Tailwind CSS setup * shared stylesheets * design-token wiring for apps and UI packages If multiple apps or packages consume the same styling baseline, keep that contract here instead of duplicating CSS setup. ## `packages/env` [#packagesenv] Put these here: * env schema * server/client env parsing * typed env exports * Envy setup and generated Next.js env boundary files Use `@howells/envy` as the default implementation. Do not read `process.env` throughout the codebase outside this boundary. ## `packages/motion` [#packagesmotion] Put these here: * durations * easings * springs * motion presets This boundary now recurs enough that it should be deliberate, not accidental. ## Add only when needed [#add-only-when-needed] ### `packages/auth` [#packagesauth] Use when: * auth logic is non-trivial * multiple apps or packages depend on auth behavior Do not extract this too early in a small repo. ### `packages/assets` [#packagesassets] Use when: * multiple apps or packages consume the same images, icons, or generated asset metadata * design-system or product surfaces need a shared asset contract This shows up often enough that it should be a deliberate choice when a repo has more than one surface. Generated asset metadata can point at Motif runs, model IDs, prompts, references, or output files, but do not put Motif client implementation here. Keep generation calls in `packages/ai`, `packages/agents`, `packages/mastra`, `packages/mcp`, or a product service that uses `@howells/motif-sdk`. ### `packages/upload` or `packages/storage` [#packagesupload-or-packagesstorage] Use when: * the repo has serious upload or media behavior * media storage integration is not isolated to one small feature * object/blob storage needs provider portability, agent file tools, or shared upload/download/list/delete behavior Use `files-sdk` as the default SDK inside this boundary when the repo needs to talk to S3, R2, GCS, Azure Blob, Vercel Blob, Netlify Blobs, MinIO, or similar providers through one API. Keep the `Files` instance, provider adapter configuration, key naming, metadata policy, and approval-sensitive operations here. Export product-level functions; do not leak native provider clients or adapter setup into app routes. ### `packages/ai` [#packagesai] Use when: * repo-specific models, prompts, adapters, or orchestration logic need a home above one app * more than one surface depends on the same model logic Use `@howells/ai` as the shared baseline before creating fresh provider wrappers here. This package can compose `ai`, provider adapters, model registries, and app-specific structured-output helpers, but it should not become a place for domain business logic. When repo-specific image generation belongs with the model layer, use `@howells/motif-sdk` here instead of raw fal.ai clients. ### `packages/agents` [#packagesagents] Use when: * agent definitions, evaluators, prompts, or tool wiring are reused by more than one surface * model-facing orchestration has become more than a single app feature Keep provider setup behind `@howells/ai`; this package should own product-specific agent behavior. Use this for non-Mastra agent assets. When Mastra owns runtime orchestration, prefer `packages/mastra`. ### `packages/mastra` [#packagesmastra] Use when: * the repo needs Mastra agents, tools, workflows, memory, storage, observability, scorers, processors, or Studio inspection * agent behavior is more than a single prompt or simple server action * workflows need durable runs, traces, or background execution * app code needs to dispatch jobs to a local or remote Mastra runtime Keep `packages/mastra` organized by runtime concern: `agents`, `tools`, `workflows`, `schemas`, `prompts`, `runtime`, `observability`, `scorers`, and `processors`. App routes and React components should not import Mastra internals. Put dispatch and polling behind product services, and expose only deliberate package exports. See [Agentic Development](/docs/reference/agentic-development) before adding this package. ### `packages/mcp` or `packages/mcp-server` [#packagesmcp-or-packagesmcp-server] Use when: * the repo exposes MCP tools or resources * tool schemas, transports, or server setup need to be shared with apps, CLIs, or tests Do not bury MCP server contracts inside a web app route if another surface needs to consume or test them. Use `@modelcontextprotocol/sdk` here rather than hand-rolling protocol objects. For image-generation tools, prefer Motif's maintained SDK or agent-readable CLI before creating a fresh protocol surface. Add an MCP server only when the product has a demonstrated MCP client need that the CLI cannot serve. ### `packages/cli` [#packagescli] Use when: * ingestion, agent, or operations workflows need a first-class command line * scripts have grown into reusable commands with options, validation, and tests Do not create this for one-off maintenance scripts. ### `packages/core` [#packagescore] Use when: * the repo has substantial shared non-UI business logic Do not create `core` as a junk drawer. ### `packages/utils` [#packagesutils] Use sparingly for: * genuinely shared, low-level helpers with no stronger domain home Do not let `utils` become the first place code goes. In your repos it exists often, but it is weaker than `db`, `ui`, `trpc`, `auth`, or `ai` as a boundary. ## Keep in `apps/web` [#keep-in-appsweb] Keep these app-local: * routes * page compositions * app-specific loaders/actions * product-specific UI assembly * local feature folders that are not reused anywhere else The app should assemble shared infrastructure, not own it. ## Do not extract [#do-not-extract] Do not create packages for: * one tiny helper * one feature used once * speculative future reuse * vague categories like `shared`, `common`, or `utils` without a real boundary A package must express a dependency boundary. ## Signs of a useful boundary [#signs-of-a-useful-boundary] * multiple apps depend on it * changing it should not require editing route files directly * it has a coherent reason to exist * it reduces duplication without hiding behavior ## Signs of a weak boundary [#signs-of-a-weak-boundary] * everything imports everything * package names are generic and meaningless * moving code into the package did not reduce coupling * the package exists only because monorepos are fashionable ## Priority order [#priority-order] Create boundaries in this order: 1. `db` 2. `ui` 3. `typescript-config` 4. `tailwind-config` 5. `motion` 6. `env` 7. `trpc` when a same-workspace typed API needs it 8. `ai` / `mastra` / `agents` / `mcp` when agent behavior is part of the product # Repo archetypes (/docs/reference/repo-archetypes) # Repo archetypes [#repo-archetypes] Choose one of these shapes for new TypeScript or native Apple work. PHP and Craft maintenance projects follow their existing architecture. ## 1. Full-stack product app [#1-full-stack-product-app] Use this for: * authenticated product apps * dashboard-style products * apps with real database-backed behavior * products with both UI and backend logic Default stack: * Next.js App Router * the narrowest typed API boundary that fits the consumers * React Query when the client owns server-state * Drizzle * Neon * WorkOS by default; Clerk only when a lighter existing or consumer-oriented app has a concrete reason * bundled UI baseline * `@howells/envy` when runtime env exists * `@howells/ai`, Mastra, and MCP packages when agent behavior is product behavior Typical workspace: ```text apps/ web/ storybook/ # only if shared UI exists packages/ db/ trpc/ # only for a same-workspace typed API ui/ typescript-config/ tailwind-config/ env/ motion/ auth/ # when auth is shared ai/ # only for repo-specific logic above @howells/ai mastra/ # when Mastra owns agent/workflow runtime behavior agents/ # when agent behavior is shared mcp/ # when the repo exposes MCP tools or resources assets/ # when multiple surfaces share assets upload/ # only if needed ``` ## 2. UI system or design-system repo [#2-ui-system-or-design-system-repo] Use this for: * shared UI foundations * component libraries * token systems * motion and transition primitives Default stack: * scaffold UI-baseline monorepo shape * Storybook * playground or docs app Typical workspace: ```text apps/ playground/ storybook/ web/ # optional docs/system site packages/ ui/ typescript-config/ tailwind-config/ motion/ transition/ ``` Use this model for new shared UI work. ## 3. Docs or content site [#3-docs-or-content-site] Use this for: * product docs * developer docs * editorial or content-led sites with light app behavior Default stack: * Next.js * Fumadocs when the site is really documentation * plain markdown only when the surface is still small Typical workspace: ```text apps/ web/ packages/ ui/ # optional shared site components content/ # optional if content transforms are non-trivial ``` Do not install a huge application architecture unless the site actually needs it. ## 4. Published package [#4-published-package] Use this for: * OSS packages * reusable libraries * packages with their own docs/demo site Default stack: * package at repo root or minimal monorepo * `tsup` or repo-specific build tooling if needed * docs/demo site only if the package benefits from one * Node 24 for development and CI * Node `>=22` as the runtime floor only when the library does not need Node 24 APIs Examples in your ecosystem: * `@howells/stacksheet` * `@howells/aperto` * `@howells/envelope` * `@howells/envy` * `@howells/lint` * `@howells/typescript-config` This archetype should stay lean. Do not force app-style monorepo complexity into a package repo. ## 5. Worker or service-heavy system [#5-worker-or-service-heavy-system] Use this for: * multi-service backends * cron-heavy systems * background jobs and ingestion pipelines * systems with multiple deployable runtimes Default shape: * `pnpm` monorepo * shared packages for core logic * services or workers as their own deployable units * Railway-style deployment when the system stops fitting a pure Vercel shape Typical workspace: ```text apps/ web/ # optional api/ # optional packages/ db/ core/ jobs/ storage/ services/ workers/ crons/ ``` ## 6. AI pipeline or research repo [#6-ai-pipeline-or-research-repo] Use this for: * ingestion pipelines * model orchestration * generation workflows * classifier or embedding jobs Default stack: * `tsx` * `zod` * `ai` * `@howells/ai` * Motif's SDK or agent-readable CLI when image generation, editing, media utilities, or creative automation are central * Mastra when the repo needs agent orchestration, workflow state, memory, or observability * `@modelcontextprotocol/sdk` when the repo exposes MCP tools or resources * Drizzle if persistence is needed * the house media storage packages if generated assets are stored * `mastra`, `agents`, `mcp`, or `cli` packages when the repo exposes reusable agent tooling * `@howells/srcfull` if browser or page-source ingestion is central Define script and data-pipeline conventions explicitly. ## 7. Native Apple client [#7-native-apple-client] Use this for: * SwiftUI iPhone, iPad, or macOS products * a native client paired with an existing service or domain core * TestFlight or App Store distribution Default shape: * keep the SwiftUI client thin over a versioned service or domain contract * generate Xcode projects with XcodeGen when the repo uses it; do not hand-edit generated project files * keep persisted `Codable` models backwards-compatible and test upgrades against earlier stored data * verify real simulator or device interaction and run the architecture-appropriate native CI lane * own TestFlight/App Store credentials and distribution in a runbook * verify a separately deployed API is live before the client assumes a merged contract exists Do not force Node scripts or a web package graph onto a native repo just to resemble the TypeScript archetypes. ## Choose an archetype [#choose-an-archetype] Use these defaults: * product app: full-stack product app * shared components: UI system repo * docs-first surface: docs or content site * reusable library: published package * multiple workers/services: worker or service-heavy system * model workflows and ingestion: AI pipeline or research repo * SwiftUI product or native companion: native Apple client If two archetypes apply, choose the dominant one and add only the secondary capabilities needed. # Shared package candidates (/docs/reference/shared-package-candidates) # Shared package candidates [#shared-package-candidates] Separate established packages from patterns that still need evidence. ## Established packages [#established-packages] These should be treated as canonical shared packages now: * `@howells/lint` * pinned Oxlint/Oxfmt, Ultracite, and React Doctor presets * `@howells/typescript-config` * explicit thin tsconfig presets * `@howells/stacksheet` * the preferred abstraction for stacked sheet flows * `@howells/envelope` * strict structured IO wrapper for CLI-model workflows * `@howells/ai` * shared AI SDK/provider baseline for AI-capable apps and agent packages * `@howells/motif-sdk` * shared fal.ai image-generation, editing, utility media, model registry, and cost-estimation surface * `@howells/motif-cli` * agent-friendly image-generation CLI with dry runs, structured output, local history, and series workflows * `@howells/envy` * typed env parsing, lint helpers, and deployment env preflight checks * `@howells/stow-server` * the reusable server integration surface when a repo needs to talk to the house media storage platform directly * `@howells/stow-next` * the reusable Next.js-facing media storage integration * `@howells/srcfull` * shared source-fetching layer for browser/page-source ingestion workflows ## Continue standardising: AI provider baseline [#continue-standardising-ai-provider-baseline] `@howells/ai` is already common enough to be the default provider baseline for AI-capable repos. Recommendation: * keep provider selection, model defaults, and shared AI SDK wiring in `@howells/ai` * keep product-specific prompts, tools, and workflows in repo-local `packages/ai` or `packages/agents` * do not publish repo-specific agent packages just because several repos use Mastra Mastra and MCP should standardize as architecture choices before becoming more shared package surface. ## Continue standardising: image generation [#continue-standardising-image-generation] Motif should be the default surface for fal.ai image generation, image editing, upscaling, background removal, image-to-video, model metadata, dry runs, cost estimates, and structured agent-facing command-line work. Recommendation: * use `@howells/motif-sdk` for product or package code * use `@howells/motif-cli` for scriptable local and agent workflows * use the CLI's JSON/NDJSON output, semantic exit codes, and live `--describe` schema when an agent needs the tool surface * keep durable media storage separate through the house media storage platform * do not write raw fal.ai clients in app routes unless Motif cannot cover the endpoint yet ## Strong candidate: motion tokens [#strong-candidate-motion-tokens] This is the clearest next shared package candidate. Why: * `motion` is one of the highest-frequency UI dependencies * the bundled UI baseline includes a small motion package snapshot * both repos are solving the same problem: durations, easings, springs, presets Recommendation: * converge on one shared motion token package instead of letting every major UI repo carry its own copy This package should be small and boring: * durations * easings * springs * a few named presets It should not become a second animation library. ## Strong candidate: transition primitives [#strong-candidate-transition-primitives] The bundled transition snapshot suggests another promising shared layer. Why: * transitions and overlay enter/exit behavior are recurring * the same interaction patterns reappear across UI repos * keeping transition primitives separate from raw components is cleaner than burying them inside each app Recommendation: * stabilize transition primitives inside repos that need them * only publish them separately if they prove reusable outside one product family ## Medium candidate: drawer and side-panel layer [#medium-candidate-drawer-and-side-panel-layer] You have repeated `vaul` wrappers across several active UI repos. That is a signal. Recommendation: * do not publish a generic drawer package yet * first collapse the repeated wrappers into the scaffold UI baseline or a repo-local shared UI package * publish only if that API becomes stable and broadly useful outside your repos For now: * keep simple drawers in shared UI packages * use `@howells/stacksheet` when the flow becomes stack-oriented ## Keep local for now [#keep-local-for-now] These are useful patterns, but they should stay repo-local for now: * repo-local env packages * project-local domain packages * repo-specific auth wrappers * repo-specific TRPC wrappers They encode app boundaries, not cross-project standards. The env exception is implementation, not ownership: keep a repo-local `packages/env` boundary, but build it on `@howells/envy` instead of publishing another env package per app. ## Product and package choices [#product-and-package-choices] Keep these choices separate: * the house media storage platform is the product recommendation * `@howells/stow-server` is the package recommendation For new projects, the default decision should be: * if the repo needs image, vector, or media storage, start by asking whether it should use the house media storage platform * if the repo needs typed server-side integration, reach for `@howells/stow-server` * if package code needs portable object/blob operations, put `files-sdk` inside the storage/upload package rather than calling S3, R2, GCS, Azure Blob, Vercel Blob, or similar provider clients directly from apps ## Standardisation order [#standardisation-order] Reduce duplicated package work in this order: 1. prefer `@howells/lint` and `@howells/typescript-config` everywhere 2. use `@howells/envy` for repo-local env boundaries instead of creating more package-specific env tooling 3. keep `@howells/ai` as the shared AI/provider baseline instead of scattering raw provider clients 4. use `howells/motif` for image generation and media utility workflows instead of scattering raw fal.ai clients 5. standardize `@howells/stacksheet` as the default stacked-panel abstraction 6. unify motion tokens into one shared package 7. stabilize the bundled UI baseline through real consuming repos before publishing more UI internals # Stack decisions (/docs/reference/stack-decisions) # Stack decisions [#stack-decisions] These are the current default decisions for new TypeScript product work and shared config repos. ## Core tools [#core-tools] The default toolchain for new TypeScript work: * **Package manager:** `pnpm` * **Task runner:** Turborepo * **Language:** TypeScript * **Lint and format:** `@howells/lint` (Oxlint / Oxfmt) * **TypeScript config:** `@howells/typescript-config` * **Env parsing:** `@howells/envy` * **Git hooks:** `@howells/husky` with `lint-staged` * **Runtime:** Node 24 LTS Shared config packages and each root `package.json` pin exact versions. This page records only major-version policy. ## Current major-version lane [#current-major-version-lane] Review this table when a compatibility-significant major changes; keep exact minor and patch versions in workspace catalogs and lockfiles. | Surface | Current major for new work | Adopted/reviewed | | ------------ | -------------------------: | ---------------- | | Node.js | 24 | 2026-08-26 | | pnpm | 11 | 2026-08-26 | | Next.js | 16 | 2026-08-26 | | React | 19 | 2026-08-26 | | TypeScript | 6 | 2026-08-26 | | Tailwind CSS | 4 | 2026-08-26 | | Turborepo | 2 | 2026-08-26 | | Vitest | 4 | 2026-08-26 | | Storybook | 10 | 2026-08-26 | | AI SDK | 7 | 2026-08-26 | For published packages, test every runtime major still claimed in `engines`, even if the new-project lane has moved on. Dropping an existing runtime floor waits for a deliberate package major. Treat persisted classifications, machine-readable output shape, schema meaning, and nullability as compatibility surfaces too: a semantic break may require a major even when function names do not change. ## Package manager [#package-manager] * Use `pnpm`. * Pin `packageManager` in the root `package.json`. * Prefer one lockfile at the repo root. * Put shared dependency versions in the `pnpm-workspace.yaml` catalog. * Keep pnpm settings, overrides, patches, and lifecycle-build policy in `pnpm-workspace.yaml`, not the ignored `package.json#pnpm` field. * Explicitly allow or deny dependency build scripts. * For serious public-facing repos, cool down newly published third-party versions; keep first-party or private exclusions exact and reviewed. * Test a frozen-lockfile install from a clean checkout in CI. * Use Node 24 LTS for development, CI, apps, and services. * Default workspace layout is: ```yaml packages: - "apps/*" - "packages/*" ``` ## Node version policy [#node-version-policy] Use Node 24 LTS as the Howells stack baseline. Defaults: * app and service repos: pin `engines.node` to the Node 24 range (`>=24 <25`) * CI: Node 24 * local version files: pin the latest Node 24 LTS patch * published packages: keep runtime support back to Node 22 when the package does not need Node 24 APIs, but build and test on Node 24 Do not start new work on Node 20. It is end-of-life. Do not standardize on Node 26 until it reaches LTS. ## Turborepo [#turborepo] Use Turbo as an orchestrator, not as a place to hide complexity. * Keep `turbo.json` small. * Keep cache disabled by default. * Keep `globalDependencies` limited to `.env` files. * Scope environment variables at the task level, not globally. * Put package-specific exceptions in leaf packages when needed. * Avoid deprecated `turbo run --parallel`; let persistent `dev` tasks run through task config. Active repos keep caching off because stale-cache failures have cost more than slower local runs. ## TypeScript [#typescript] Use `@howells/typescript-config` and select the leaf preset explicitly: * Next.js app: `@howells/typescript-config/nextjs` * Bundler DOM app: `@howells/typescript-config/bundler-dom-app` * React library: `@howells/typescript-config/react-library` * Non-DOM app: `@howells/typescript-config/bundler-no-dom-app` * Non-DOM package: `@howells/typescript-config/bundler-no-dom-library-monorepo` or `tsc-no-dom-library` Rules: * do not reintroduce a vague `base.json` * keep shared presets thin * keep local `paths` and `baseUrl` in the consumer repo only ## Linting and formatting [#linting-and-formatting] Prefer the Oxlint/Oxfmt lane through `@howells/lint`. Default preset selection: * non-React or server repo: `@howells/lint/oxlint/core` * React package: `@howells/lint/oxlint/react` * Next.js app: `@howells/lint/oxlint/next` * formatting: `@howells/lint/oxfmt` Rules: * avoid repo-local lint wrappers unless the repo has a genuinely unique constraint * do not install direct `oxlint`, `oxfmt`, Biome, Prettier, or ESLint dependencies in consumer repos * use `howells-check`, `howells-fix`, `howells-oxlint`, and `howells-oxfmt` instead of raw tool binaries * prefer inline suppressions over broad config weakening * keep format and lint behavior consistent across repos For env access, use `@howells/envy` lint helpers with Oxlint when a repo needs to enforce "no direct `process.env`" strongly. ## Environment variables [#environment-variables] Use `@howells/envy` for repos with runtime configuration. Default approach: * put the schema in `packages/env` * parse explicitly by default * expose separate server and client env modules * allow direct `process.env` only inside the env boundary * run local env checks in `pnpm check` * run Vercel or Railway env checks before deploy Do not keep hand-written dotenv loading, ad hoc `process.env` reads, or provider env setup scripts once Envy can own that surface. ## Shared Git hooks [#shared-git-hooks] Use `@howells/husky` for the standard immutable hook set. It owns Husky and keeps the hook behavior consistent across repositories. Default approach: * `prepare`: `howells-husky` * `pre-commit`: run `lint-staged` * `pre-push`: run `typecheck` and `lint` when the pushed ref is the checked-out `HEAD` Change the shared package when the house hook contract needs to change. Don't edit generated `.husky` files in consuming repositories. ## UI stack [#ui-stack] For new UI repos: * Next.js App Router * React * Tailwind CSS v4 * Base UI primitives (`@base-ui/react`) * `motion` for animation, imported from `motion/react` in React code * Storybook for reusable exported components * the bundled UI baseline as the starting point for shared UI packages Use Base UI as the primitive layer for new repos. shadcn now defaults to Base UI, so `npx shadcn init` scaffolds Base UI-backed components. Base UI ships as a single package, `@base-ui/react` — do not split it into per-component packages. Radix stays a supported deliberate opt-out. Choose it with `npx shadcn init -b radix` when a repo has a concrete reason. On Radix, use the unified `radix-ui` package. Do not install the split per-component Radix packages. Share structural decisions; keep brand and product expression local. ### Next.js baseline [#nextjs-baseline] * Turbopack is the default bundler. * Adopt Cache Components (`use cache`) as the caching model. * Use `proxy.ts` for request interception. `middleware.ts` is deprecated. This repo ships a `proxy.ts` example itself — markdown content negotiation on the docs routes. ## Client data fetching [#client-data-fetching] Use `@tanstack/react-query` for all client-side data fetching. No raw `fetch` in components. Rules: * Create a `lib/api.ts` with typed hooks (`usePersonas`, `useEvaluation`, etc.) wrapping `useQuery` and `useMutation` * Create a `lib/query-provider.tsx` client component with `QueryClientProvider` * Server Components fetch data directly from the database or internal packages — React Query is only for client components * Mutations should invalidate related query keys on success * SSE streams and one-shot fire-and-forget fetches are the only exceptions to using React Query Pattern: ```ts // lib/api.ts export function usePersonas() { return useQuery({ queryKey: ["personas"], queryFn: () => apiFetch("/api/personas"), }); } export function useUpdatePersona() { const qc = useQueryClient(); return useMutation({ mutationFn: ({ id, data }) => apiFetch(`/api/personas/${id}`, { method: "PUT", ... }), onSuccess: (_, { id }) => { qc.invalidateQueries({ queryKey: ["personas"] }); qc.invalidateQueries({ queryKey: ["personas", id] }); }, }); } ``` React Query owns loading, errors, caching, and invalidation. Do not rebuild that state with `useEffect`, `useState`, and `fetch`. ## Dependency standard [#dependency-standard] Active repos share a dependency baseline as well as configuration. An August 2026 direct-root scan across the local Git checkouts, deduplicated by repository path, found the strongest signals in `typescript` (54), `@howells/lint` (44), `@howells/typescript-config` (32), `turbo` (31), `lint-staged` (29), `vitest` (25), `tsx` (20), and `zod` (19). This broad inventory includes client repositories and documentation mirrors, so use it to rank recurrence rather than to claim that every checkout independently chose the package. The packages that recur most often in UI work are: * `motion` * `lucide-react` * `zod` * `clsx` * `tailwind-merge` * `sonner` * `nuqs` * `next-themes` * `date-fns` * `@tanstack/react-query` * `usehooks-ts` * `@base-ui/react` * `@howells/envy` Repeated package names across Turborepos define these default boundaries: * first tier: `db`, `ui`, `typescript-config`, `tailwind-config` * second tier: `utils`, `motion`, `auth`, `mastra`, `agents`, `mcp`, repo-local `ai` packages above `@howells/ai`, and `trpc` when a same-workspace API needs it * optional but frequent: `assets`, `upload`, `storage`, `env`, `config` The detailed policy lives in [Default Dependencies](/docs/reference/default-dependencies). For media-heavy projects, there is also a platform-level default: * use `@howells/motif-sdk` for product image generation, editing, and fal utility integration * use `@howells/motif-cli` for scriptable and agent-facing creative automation; prefer its JSON/NDJSON output, semantic exit codes, and live `--describe` schema * prefer the house media storage platform for image, vector, and general media storage/delivery * use `files-sdk` behind storage/upload packages when project code needs a portable object/blob API across S3-compatible storage, R2, GCS, Azure Blob, Vercel Blob, Netlify Blobs, MinIO, or similar providers Full-stack apps also share this architecture: * server composition for app-internal behavior, `tRPC` for same-workspace typed clients, and OpenAPI/oRPC for separate consumers * React Query for server state * Drizzle plus Neon for persistence The detailed guidance lives in [Architecture Defaults](/docs/reference/architecture-defaults). ## AI, agents, and MCP [#ai-agents-and-mcp] AI-capable repos use this starting shape. Default package choices: * `ai` for the Vercel AI SDK surface * `@howells/ai` for shared provider defaults and house wrappers * Motif's SDK or CLI when image generation, image editing, media utilities, or agent-facing automation are part of the product * `zod` for structured model IO and tool schemas * `@mastra/core` and `mastra` when the repo needs agent orchestration, memory, observability, or workflow structure * `@modelcontextprotocol/sdk` when the repo exposes MCP tools, resources, or transports * provider packages such as `@ai-sdk/openai`, `@ai-sdk/anthropic`, `@ai-sdk/google`, and `@openrouter/ai-sdk-provider` only when a direct-provider need justifies them ### Model access [#model-access] `@howells/ai` is the authority for this choice. Its current package-level default is Vercel AI Gateway, selected after an April 2026 benchmark, but this is not a portfolio-wide requirement. OpenRouter and direct providers remain deliberate route choices behind the same boundary, and the default should be revalidated as models and routing systems change. Per-provider `@ai-sdk/*` packages are the escape hatch for direct-provider needs, and they still sit behind `@howells/ai`. Keep model-string selection behind that boundary rather than hardcoding provider strings across app routes. Default boundaries: * `packages/ai` for repo-specific model/provider composition above `@howells/ai` * `packages/mastra` for Mastra runtime code, agents, tools, workflows, storage, memory, observability, scorers, and runtime routes * `packages/agents` for reusable non-Mastra product agents, evaluators, prompts, and tool wiring * `packages/mcp` or `packages/mcp-server` for MCP contracts and server code * `packages/cli` when ingestion or model workflows need a real command line Do not scatter raw provider clients through app routes. Keep provider plumbing behind `@howells/ai` or a repo-local `packages/ai` boundary. Use [Agentic Development](/docs/reference/agentic-development) for Mastra, agentsurface.dev, MCP, tool design, workflows, memory, and observability guidance. ## Documentation [#documentation] If the repo needs a docs site: * prefer Fumadocs for Next.js-based docs experiences * otherwise keep docs as plain Markdown until the repo actually needs a full docs UI Do not install a docs framework out of habit. # Stack in practice (/docs/reference/stack-in-practice) # Stack in practice [#stack-in-practice] This snapshot records services, packages, and skills used across active repos. [Stack decisions](/docs/reference/stack-decisions) defines the new-project standard. ## Services [#services] Counts are approximate repo footprints; use them to identify defaults, not as adoption metrics. | Category | Default | Also in use | | --------------------- | ----------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- | | **Hosting** | Vercel | Docker for containerised services; Cloudflare Workers occasionally | | **Database** | Neon (Postgres) + Drizzle ORM | Upstash Redis for caching/rate-limits; Turso once | | **LLM access** | Provider access through `@howells/ai` | Choose Gateway, OpenRouter, or a direct `@ai-sdk/*` provider explicitly when the product needs a particular route | | **Embeddings** | Voyage | — | | **Search / scraping** | Exa + Firecrawl (usually paired) | Tavily, Bright Data, ScrapingBee | | **Agent browsing** | Kernel | the `agent-browser` skill for local automation | | **Media / voice** | fal.ai (image/video), `@howells/motif` in front of it | ElevenLabs for voice | | **Object storage** | Cloudflare R2, via the S3-compatible SDK | Vercel Blob occasionally | | **Auth** | WorkOS for serious product apps | Clerk in a smaller set of lighter or existing apps | | **LLM observability** | Langfuse | — | | **Product analytics** | PostHog | Vercel Analytics in a small number of existing sites | | **Data warehouse** | Snowflake (where the data lives there) | — | Two prospective defaults are not yet widespread: * **Errors:** Sentry. * **Transactional email:** Resend. A note on LLM access: `@howells/ai` is the authority for provider and model selection. Its current package-level default route is Vercel AI Gateway, selected after an April 2026 benchmark, with OpenRouter and direct providers available behind the same boundary. That is an implementation default inside `@howells/ai`, not a portfolio-wide requirement or evidence that every consuming project explicitly chose Gateway. Revalidate it when models and routing systems materially change. ## Models and media [#models-and-media] Do not duplicate exact model rosters here. Language and embedding choices live in `@howells/ai`; media-generation and transformation choices live in `howells/motif`. Product code asks those packages for a tier or task instead of scattering fast-decaying model IDs through routes, prompts, and documentation. New AI work starts on AI SDK 7. Existing AI SDK 6 products migrate deliberately because the provider and tool APIs are a compatibility-significant change, not a fleet-wide cosmetic bump. ## Packages [#packages] The dependency baseline is consistent: the same \~15 packages carry most repos. Authoritative pinned versions live in [Stack Decisions](/docs/reference/stack-decisions) and [Default Dependencies](/docs/reference/default-dependencies); this is just the measured shape. * **Tooling spine**: `typescript`, `@howells/lint`, `@howells/typescript-config`, `turbo`, `lint-staged`, `vitest`, `tsx`, `@howells/husky`, and `@howells/envy` where runtime configuration exists. * **UI repos**: `react` / `react-dom`, `tailwindcss` (+ `@tailwindcss/postcss`), `next`, `zod`, `lucide-react`, and `motion` (not `framer-motion` in current first-party work). * **Common UI**: `clsx` + `tailwind-merge`, `class-variance-authority`, `@base-ui/react` / `@radix-ui/*`, `@tanstack/react-query`, `nuqs`, `next-themes`, `sonner`, `cmdk`, and the `@patternmode/*` kit (`stacksheet`, `scrollframe`, `swatch`, `aperto`). * **Data & AI**: `drizzle-orm` (+ `drizzle-kit`), `@neondatabase/serverless`, `ai` (Vercel AI SDK), `@howells/ai`, `@mastra/*` when orchestration is needed, `@modelcontextprotocol/sdk` for MCP. * **Testing**: `vitest` for unit/integration, `@playwright/test` for E2E, `@testing-library/*`. `tRPC` is deliberately rare; most repos favour server actions, the AI SDK, or plain typed fetch over a tRPC layer. ## Skills and tools [#skills-and-tools] Measured from a month of actual invocations (both slash commands I type and skills invoked mid-task), not from repo mentions. Skills are installed globally and invoked on demand, never vendored into repos, so a repo grep badly undercounts them. Claude Code and Codex handle the development loop directly. Matt Pocock's skills provide general methods; the Howells collection remains specialist. **Most-used specialists**, roughly in order of how often I reach for them: * **chiaroscuro**: UI design direction and Tailwind v4 systems. My most-invoked individual skill by a wide margin; design direction is central to how I work, not a side concern. * **/chrome** and **fieldtest**: browsing/dogfooding and evidence-backed rendered QA. * **foreman**: foreman-mode delegation, where the main loop plans and reviews while subagents write the code. * **grill-with-docs** / **domain-modeling**: Matt Pocock's skills for pinning the ubiquitous language before building. * **marginalia**: concise JSDoc on public APIs. * **mastraudit**: auditing Mastra implementations against current guidance. * **research** / **firecrawl-deep-research**: primary-source repository research and broader web research. * On-demand: **componentize**, **heathen**, **aperture**, **fenceline** (structure/boundaries); **nomen** (naming); **deslop** (prose); **surface** (agent-readability); **foundry** (brand systems). **External skills I lean on:** Matt Pocock's engineering set (`domain-modeling`, `grill-with-docs`, `improve-codebase-architecture`, and the writing skills), the superpowers marketplace, and Vercel Labs' `agent-browser`. See [Development skills](/docs/reference/development-skills) for the current installed routing map. # UI projects (/docs/reference/ui-projects) # UI projects [#ui-projects] Seed new UI packages from Scaffold's bundled baseline. The shared source lives here rather than in a separate upstream repo. ## What the bundled baseline owns [#what-the-bundled-baseline-owns] The scaffold now includes the old UI-system files directly under [Bundled UI Baseline](/docs/reference/ui-baseline). Use those files as a starting point for: * `packages/ui` for primitives and shared compositions * `packages/tailwind-config` for tokens and shared CSS entrypoint * `packages/motion` for shared motion constants and helpers * `packages/transition` for transition primitives * `apps/storybook` as the visual contract * `apps/playground` or a docs app as integration and system surfaces ## Default rule [#default-rule] If a new project has a UI, do not start by inventing a fresh component system. Start from the bundled UI baseline and only diverge when one of these is true: * the product has a domain-specific component that does not belong in shared UI * the project needs an app-local composition over shared primitives * the visual language needs new tokens or wrappers but not new primitive behavior ## Reuse and local ownership [#reuse-and-local-ownership] Reuse from the bundled baseline: * primitive controls * base form fields * common overlays and menus * shared motion timing and transition patterns * token structure * Storybook conventions Own locally in the project: * page-level compositions * brand-specific token values * domain-specific compound components * app-specific wrappers around shared primitives ## Avoid [#avoid] * do not copy-paste shared components into app code as a default workflow after the repo has a `packages/ui` boundary * do not fork primitives just to tweak spacing or visual tone * do not let every UI repo invent its own Tailwind token naming * do not treat shadcn output as the final design system The structural baseline is bundled in this scaffold. The aesthetic layer remains project-specific. ## Baseline UI stack [#baseline-ui-stack] For a new UI repo, prefer: * Next.js App Router * React * Tailwind CSS v4 * Base UI primitives (`@base-ui/react`) * `motion` * `lucide-react` * Storybook for reusable exported components Base UI is the house default primitive layer. shadcn scaffolds Base UI-backed components by default (`npx shadcn init`), and Base UI ships as one package — `@base-ui/react`. Radix is a supported opt-out via `npx shadcn init -b radix`; on Radix, use the unified `radix-ui` package, never the split per-component Radix packages. ## Reusable Howells UI packages [#reusable-howells-ui-packages] Do not use an old shared UI upstream as the UI layer for new projects. Only use specific installable components when the interaction matches the package. Use: * `@howells/stacksheet` for typed stacked sheets, drill-in panels, and multi-layer modal flows * `@howells/aperto` for styled thumbnail-to-expanded media transitions and media lightboxes Do not use legacy provenance as a reason to skip a repo-local `packages/ui` boundary when the repo owns shared primitives. The reusable packages are relevant as specific installable components, not as a shared UI system. ## Overlay standard [#overlay-standard] Use different primitives for different overlay jobs. For a simple drawer or mobile bottom sheet: * use the shared drawer component from the repo UI package * that drawer can stay `vaul`-backed under the hood For stacked sheets, panel drills, or multi-layer modal flows: * prefer `@howells/stacksheet` * do not try to stretch a plain `vaul` drawer into a stacked workflow For thumbnail-to-expanded media interactions: * prefer `@howells/aperto` * keep custom gallery/product behavior local to the app around the package This distinction already shows up in your ecosystem: * `vaul` wrappers recur inside shared UI packages * `@howells/stacksheet` is the stronger abstraction when the interface needs real stack orchestration * `@howells/aperto` is the reusable media-transition component when the interface needs a polished image or video expansion pattern ## Storybook rule [#storybook-rule] If the repo exports user-facing reusable UI, Storybook is required. Keep the Storybook surface proportionate, but give shared UI a visible contract and visual regression checks. ## Maintaining the bundled baseline [#maintaining-the-bundled-baseline] The bundled baseline is still a starting point, not a frozen design system: * keep generic primitives in `packages/ui` * keep local wrappers thin until repeated needs prove a stronger shared primitive * update the scaffold baseline when the same improvement appears across multiple active repos ## Migrate an existing project [#migrate-an-existing-project] When moving an older UI repo toward the new standard: 1. adopt the bundled token structure and shared CSS entrypoint first 2. migrate obvious primitives second 3. migrate shared compositions only after the primitive contract is stable 4. keep page-level product code local Migrate in stages; alignment does not require a one-shot visual rewrite. # Worktree coordination (/docs/reference/worktree-coordination) # Worktree coordination [#worktree-coordination] All managed worktrees live under one machine-wide umbrella: ```text ~/Sites/.worktrees/ codex/// claude/// manual/// .control/ ``` This page is canonical. `~/Sites/WORKTREES.md` symlinks here so people and both harnesses read the same policy. The umbrella coordinates paths; it does not share working directories. Codex and Claude never write to the same worktree. Each task owns one branch, worktree, and harness namespace. ## Why the namespaces matter [#why-the-namespaces-matter] Git already records every worktree in the repository's common Git directory, regardless of where it lives. Putting paths under one umbrella makes that state inspectable across harnesses without making either harness responsible for the other's lifecycle. The namespace answers who created the tree: * `codex/` is owned by Codex Desktop or Codex CLI work. * `claude/` is owned by Claude Desktop or Claude Code work. * `manual/` is for deliberate human-created worktrees. * `.control/` contains shared hooks or scripts, never project work. Existing worktrees outside the umbrella are grandfathered. Don't move or remove them solely to make the directory tree look tidy; finish or recover their work first. ## Machine configuration [#machine-configuration] ### Codex [#codex] Set the root-level Codex Desktop worktree settings to: ```toml [desktop] git-worktree-root = "~/Sites/.worktrees/codex" worktree-auto-cleanup-enabled = false worktree-keep-count = 15 worktree-upstream-refresh-mode = "best-effort" ``` Use the absolute expanded path if the settings UI or installed Codex version does not expand `~`. The Desktop **Worktree root** field is the root setting: it applies to managed Codex worktrees for every saved project on that macOS profile. Rotated Codex accounts on one macOS user share `~/.codex/config.toml`. If a launcher uses another Codex home, symlink its config to that file. ### Claude Code [#claude-code] Set `worktree.baseRef` to `fresh` and use a global `WorktreeCreate` hook that creates the target under `~/Sites/.worktrees/claude//`. The hook should: 1. resolve the repository's common Git directory rather than assuming the current directory is the primary checkout; 2. fetch and prune `origin` without prompting; 3. start from `origin/HEAD`, falling back to `origin/main`, `origin/master`, then `HEAD`; 4. fail if the target path or branch already exists; 5. copy only explicitly ignored files selected by `.worktreeinclude`. Keep the hook in `~/Sites/.worktrees/.control/`. Every rotated Claude account home should symlink its `settings.json` to the canonical `~/.claude/settings.json`, so new accounts cannot silently lose the hook. ### Claude Desktop [#claude-desktop] Set the Worktrees custom root to `~/Sites/.worktrees/claude`. Disable age-based archive cleanup and automatic archive-on-PR-close. Claude Desktop and Claude Code share the namespace but still create distinct task directories and branches. ## Starting work [#starting-work] Before creating a tree: 1. run `git worktree list --porcelain` from any checkout of the repository; 2. inspect `git status --short` in the intended source checkout; 3. fetch and prune the remote; 4. choose a unique task name and harness-prefixed branch, such as `codex/current-baseline` or `claude/auth-cleanup`; 5. create the tree from the freshest trustworthy base. One task owns one branch and one worktree. A subagent may work inside its assigned tree, but branch, worktree, stash, and cleanup operations stay with the coordinating session. ## Handoffs and stashes [#handoffs-and-stashes] A stash is emergency transport, not durable task state. Prefer a focused commit and push when work must survive a session or move between harnesses. A handoff records: * worktree path; * branch and current commit; * owner or harness; * clean or dirty state; * validation already run; * exact remaining work. Never apply or drop a stash you did not create until its repository, base commit, files, and owner are understood. ## Cleanup [#cleanup] Automatic deletion stays off. A worktree can be removed only when all of these are true: 1. its owner is known and finished; 2. `git status --short` is clean; 3. its branch is merged, intentionally abandoned, or safely pushed; 4. no stash or untracked artifact depends on it; 5. no live process or agent session is using its path. Then remove it through Git, delete the local branch only when safe, and run `git worktree prune`. Never use age, directory count, or a merged pull request as the only deletion signal. ## Audit commands [#audit-commands] ```bash git worktree list --porcelain git branch --format='%(refname:short) %(worktreepath) %(upstream:short) %(upstream:track)' git stash list --date=local ``` Run these per repository when tidying. The Git registry is authoritative; the umbrella directory is the human-readable map. # Bundled UI baseline (/docs/reference/ui-baseline) # Bundled UI baseline [#bundled-ui-baseline] This directory began with files recovered from `~/Sites/patternmode` at commit `1ffeb6ddb9d5d449a3618f3b73f79b93590cd463^`. Package manifests and primitive choices follow current Scaffold guidance, including Base UI (`@base-ui/react`). Treat it as reference source, not an installable upstream. Copy only the required package shapes, then rename scopes and imports. ## Included source [#included-source] * `source/packages/motion` for durations, easings, springs, scales, and motion presets. * `source/packages/transition` for shared transition primitives and preset structure. * `source/packages/tailwind-config` for Tailwind CSS 4.3 wiring. Its stylesheet still contains recovered product-specific Mtag, side-panel, and header tokens; do not copy it wholesale. * `source/packages/ui/README.md` for the intended component package shape. * `source/docs/design-context.md` as an archived explanation of the recovered token values. It is not current implementation guidance. * `source/apps/web/content/docs` as historical PatternMode package documentation. Verify it against the package source before reuse. ## Use it [#use-it] For a new UI repo: 1. Start with local workspace packages such as `packages/ui`, `packages/tailwind-config`, `packages/motion`, and `packages/transition` only when the repo needs them. 2. Copy only the relevant files from `source/packages/*`; remove product-specific tokens and utilities. 3. Rename `@patternmode/*` package names and imports to the repo's actual package scope or workspace aliases. 4. Keep shared primitives in `packages/ui`; keep page-specific or domain-specific compositions in the app. 5. Use Storybook when the repo exports reusable UI. The `@patternmode/*` names are placeholders. Rename them in the consuming repo. Scaffold does not build this vendored source, so run the consuming repo's typecheck, tests, Storybook build, and app build before use.