# New Project Docs (/docs/overview) # New Project Docs [#new-project-docs] This docs set is the canonical starting point for a new Howells project. It is not a generic starter guide. It reflects the conventions that are already converging across your recently active repos: * the bundled UI baseline for shared UI, tokens, motion, and Storybook * Arc for the software delivery lifecycle across vision, ideation, implementation, testing, review, audit, launch, and commit workflows * 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 MCP packages for AI-capable product and agent work * `howells/motif` packages for image generation, image editing, utility media tools, and agent-facing 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 16.2 App Router, React 19.2, Tailwind CSS 4.3, Radix, and Storybook when the repo exports reusable UI * 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, Neon, `tRPC`, and React Query * Default AI-capable shape: shared provider baseline through `@howells/ai`, image generation through `howells/motif`, product orchestration through repo-local `ai` or `agents`, and MCP in its own package when exposed ## 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) * [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 Optimizes For [#what-this-standard-optimizes-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 * Clear agent workflows so Arc and supported coding assistants remain assets instead of entropy generators * 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/ ``` Only add more top-level globs when the repo genuinely needs them. 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 16.2 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, 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 `tRPC`, React Query, Drizzle, and Neon as the default starting architecture * 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`, `trpc`, `ui`, `typescript-config`, `tailwind-config`, `env`, and `motion`; add `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 * `.husky/pre-commit` * `AGENTS.md` Use the snippets in [Config Snippets](/docs/reference/config-snippets). Also decide the first package boundaries up front using [Package Boundaries](/docs/reference/package-boundaries). A lot of repo drift starts when app code absorbs infra that should have been extracted on day one. ## 4. Install the shared config packages [#4-install-the-shared-config-packages] For the current house baseline: * `@howells/lint` * `@howells/typescript-config` * `turbo` * `typescript` * `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] At the root, keep these script names unless the repo has a real reason not to: * `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 agent configuration only when the repo will actually use that assistant or tool surface. * 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 Arc for structured delivery workflows such as vision, ideation, implementation, testing, review, audit, launch, refactor planning, and commit preparation * keep repo-local rules small; use project-specific instructions only when the repo has conventions Arc and the shared 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 Do not cargo-cult a full rules or workflow system into every project. Start with `AGENTS.md`, then add only the Codex, Claude Code, Cursor, MCP, or workflow support the repo actually uses. ## 7. Verify the baseline before feature work [#7-verify-the-baseline-before-feature-work] Before real implementation begins, these should work: ```bash pnpm install pnpm lint pnpm typecheck pnpm build pnpm test ``` If those commands are already messy on day one, the repo standard is wrong. ## 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 not `tRPC` for a TypeScript product API * 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 The goal is not lockstep. The goal is to avoid re-deciding the same defaults 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 are the principles I actually build by, teased out of how I work across my active projects rather than assembled as an aspirational wishlist. Some I hold to closely; at least one I break constantly, and I've said so where that's true. One thread runs through many of them: constraints are leverage. Strict types, closed sets of options, and boundaries a tool enforces don't only keep humans consistent — they coerce coding agents into doing the right thing by default, and that matters more every month. Where a principle has a tool behind it, I've linked it. ## Correctness & failure [#correctness--failure] ### Make the wrong thing impossible to compile [#make-the-wrong-thing-impossible-to-compile] Model your types so a nonsensical state can't be expressed: mutually exclusive options become discriminated unions, fields only valid together are grouped, and `any` gives way to `unknown` narrowed at the edges. Everything crossing a boundary — a request body, an env var, a third-party response — is parsed by a schema before the code trusts it. Strictness isn't just insurance for humans; it's the most reliable lever you have over a coding agent. A model will happily write plausible-but-wrong code against a loose type, but it can't fight a strict one — when the only thing that compiles is the correct call, the compiler does the enforcing for you. Tighten the model until it refuses everything except what you actually want. Type-aware linting carries the same idea past types into patterns the compiler alone can't catch. ### Fail loud, never fall back silently [#fail-loud-never-fall-back-silently] When something breaks, throw with the real reason. Swallowing an error and degrading into a plausible-looking fallback is worse than crashing: it turns a billing failure, an auth failure, or a data-loss bug into output that looks like success, and it destroys the one thing you need to fix it — the trace that points at the cause. A silent fallback rarely helps the end user either; it just moves the failure somewhere further from where it started. Missing configuration is an error, not a skip. No empty catch blocks, no default substitution to cover an absent value. When in doubt, let it break where it broke. The [`/fail-fast`](https://github.com/howells/skills/tree/main/fail-fast) skill exists to hunt these down. ## Shape & structure [#shape--structure] ### Design from the data model up [#design-from-the-data-model-up] Start with the data. The shape of your domain — the tables, their relationships, the constraints — is the foundation everything else rests on, and getting it right early clarifies the rest. A good schema makes the UI almost fall out of it: the screens, the states, and the queries follow from how the data is actually shaped, and a whole class of "how do I represent this?" problems never arises because the model already answered them. Treat the Drizzle schema as the blueprint if you let it be one — design it deliberately, up front, and grow the app upward from there rather than bolting a data model onto UI you've already built. ### The lightest shape that fits [#the-lightest-shape-that-fits] Start from the smallest structure that fits the work and add machinery only when a real second concern appears. A single-purpose tool doesn't need a monorepo, a task runner, workspace boundaries, or a test harness it will never exercise; that apparatus up front leaves dead configuration that rots and misleads whoever reads it next. When a piece of machinery stops fitting, remove it rather than working around it — let the repo grow into heavier structure when the work demands it, not in anticipation. I'll be honest: this is the one I break most. Left to instinct I reach for the full works — scaffold every package and boundary on day one — and regret it a week later when half of it is unused ceremony. It's here as much to remind me as to state a rule: the lightest shape is almost always right, and I almost always have to talk myself into it. ### Don't over-optimise [#dont-over-optimise] Reach for the simplest thing that could work, and make it hurt before you make it fast. It's tempting — I feel the pull constantly — to jump straight to the specialist tool: a vector database before you've tried Postgres, a cache before you've measured the query, an abstraction before you've felt the pain it's meant to solve. Resist it. Tend the problem in the boring, already-present tool first; most of the time it's enough, and you'll have learned what you actually need before spending complexity on it. Premature optimisation trades a harder-to-change system for a problem you didn't have yet. ### Boundaries are mechanical, not conventional [#boundaries-are-mechanical-not-conventional] Package and module boundaries are only real if a tool enforces them. Apps never import apps; packages never import apps; shared infrastructure is reached only across an explicit boundary — a workspace package or a versioned HTTP contract, never a sneaky deep import into someone else's database layer. Encode the dependency direction as a lint or task-runner rule so a violation fails the build, rather than trusting everyone to remember it. The shape is usually the same: a domain core that owns the logic, thin app clients that validate, persist, and dispatch, and orchestration — agent runtimes included — that coordinates without becoming the place behaviour lives. ### One source of truth; derive the rest [#one-source-of-truth-derive-the-rest] A fact should live in exactly one place, and everything else should be generated from it rather than kept in sync by hand. An enum, a schema, a config value, an API's capability list, a version number — pick the canonical home and derive the rest with a build step or a script. Hand-maintained copies drift the moment the original moves; a derived copy cannot. Reach for codegen before copy-paste and discipline. The same instinct scales up to architecture. When more than one thing needs the same data, that data gets its own home — a dedicated service behind a versioned API when the consumers are separate deployables, or a shared package when they live in the same workspace. Shared domain knowledge lives in one owned place that others consume across an explicit boundary, never copied into each consumer or reached for behind its back. ### Many small files, budgeted [#many-small-files-budgeted] Prefer many small files to a few large ones — a file that does one thing, ideally with one main export, is easier for both a person and an agent to hold in their head and change safely. Push this further than feels natural: one function per file, one component (and each meaningful sub-component) in its own file. That granularity keeps every unit independently readable, testable, and moveable, and it makes an agent's job easier — a focused file is a focused context. Treat size and complexity as a budget the linter checks, not a matter of taste: a soft warning in the low hundreds of lines, a hard cap around 600–800 that forces a split, and complexity ceilings on individual functions. When a module crosses the line, that's the signal to decompose it, not to bump the limit. ### Promote repetition into one canonical component, then delete the copies [#promote-repetition-into-one-canonical-component-then-delete-the-copies] When a pattern shows up a third time, extract it to the shared package and delete the local copies in the same move — a consolidation isn't done until the duplicates are gone. Keep shared components generic and composable; page- or product-specific UI stays in the app and never leaks into the shared layer, which otherwise fills with special cases and stops being reusable. The goal isn't reuse for its own sake — it's one place to fix a bug and one place to change a look. ### Offer a closed set of options and force the choice [#offer-a-closed-set-of-options-and-force-the-choice] Reduce the menu of decisions to a small, known-good set, and make the system enforce picking from it. Most freedom in a codebase is freedom to be subtly inconsistent, so take it away where it doesn't earn its keep. Typography becomes a handful of named roles, and raw `text-sm`/`font-medium` in markup is a lint error rather than a judgement call. Colour is a fixed set of semantic tokens, not arbitrary hex. Components live as a bounded library in one known folder that you compose from, not a scatter of one-offs invented per page. The same instinct runs through the whole stack: a closed set of options, selected rather than reinvented. This is coherence by construction instead of by review. It's also leverage over agents — a model given ten roles to choose from picks one; given free rein it invents an eleventh slightly-different grey every time. Constrain the choices and both people and models land in the same place by default. ## Language & naming [#language--naming] ### Fix the ubiquitous language first [#fix-the-ubiquitous-language-first] Before building much, pin down the words. Every project keeps a short glossary of its domain terms — the preferred word for each concept and the synonyms to avoid — and code, UI, APIs, and agent instructions all use those exact terms. A shared, precise vocabulary is what lets people and agents talk about the system without quietly meaning different things, and it stops the slow rot where one concept ends up called three names across the schema, the routes, and the UI. Renaming a concept means updating the glossary, the schema, and the tests together, in one sweep. Matt Pocock's grilling skills are the sharpest tool I've found for this — an agent interrogates a feature against your codebase's language before any code is written, building the glossary and the decision record as it goes. He's been renaming them; the current forms are [`/domain-model` and `/grill-with-docs`](https://www.aihero.dev/skills). ### Name after meaning, not implementation [#name-after-meaning-not-implementation] Name things for the concept they represent, not the mechanism behind them — a component is `Deck`, not `CardStack`; a term is `Source Record`, not `articles`. A good name passes a sayability test: if it's awkward to say out loud in a sentence about the system, it's probably wrong. Naming is genuinely hard, and there's no getting it perfect first time — you make your best guess and move on. The trick is not treating that guess as permanent. Naming is never finished: when a better word appears, renaming is real, tested migration work worth doing, propagated through data, API, and UI in one pass, not left as a sticky note of regret. ## Toolchain & operation [#toolchain--operation] ### Decide the toolchain once, reuse everywhere [#decide-the-toolchain-once-reuse-everywhere] Settle the toolchain — package manager, linter, formatter, TypeScript config, task runner — once at the baseline level, and have every repo consume those shared, pinned config packages instead of hand-rolling its own. A repo that adds a bespoke lint or format setup is buying itself drift for no gain. Pin versions so upgrades are deliberate; exact-pin the fast-moving frameworks that ship breaking changes without warning, and put a short cooldown on brand-new releases so a just-published package can't land automatically. When a repo genuinely must diverge, that exception is named and given a removal path, not left as an open-ended escape hatch. The deeper payoff is fluency. Doing things one way, everywhere, means you understand that one way *deeply* — you always have a reference point to return to and a pattern to repeat, instead of re-learning a slightly different setup in every repo. And it makes change cheap: when you find a better approach, a shared baseline lets you roll it out across every project at once, instead of hunting down twenty bespoke variations. This is why I keep the pinned configs as their own packages — [`@howells/lint`](https://github.com/howells/lint) for the Oxlint and Oxfmt presets, [`@howells/typescript-config`](https://github.com/howells/typescript-config) for the tsconfig presets. ### A small, consistent command surface at the root [#a-small-consistent-command-surface-at-the-root] Every repo is operable from the root through the same short list of commands — `dev`, `build`, `lint`, `typecheck`, `test`, and a single `check` that runs the whole gate. Consistent naming is the point: anyone, and any agent, can walk up to any repo and drive it without learning bespoke incantations. And keep that surface small — a root `package.json` littered with dozens of scripts, half of them one-offs someone ran once, is noise that buries the handful of commands that matter. One-offs belong in a script file, not bolted onto the canonical set. The standard verbs stay stable and mean the same thing everywhere; everything else stays out of the way. Keep the gate itself honest but fast: git hooks catch obvious breakage on commit and never try to be CI in miniature — if a hook gets slow enough that people start bypassing it, the hook is wrong. ### One typed env boundary [#one-typed-env-boundary] Environment variables go through a single typed boundary — one schema that parses and validates everything the app needs — instead of `process.env` reads scattered through the codebase. Importing that module shouldn't read the environment; you parse at a deliberate runtime boundary, so a missing or malformed variable fails loudly at startup rather than as a mysterious `undefined` three layers deep. Keep server-only and client-safe variables separated, and keep `.env` for secrets only — deployment-mode configuration is a different kind of thing and doesn't belong next to your credentials. Nothing that is a secret should ever reach git or a log. Missing or wrong env vars cost more time than almost anything else, because the build inevitably errors somewhere far from the cause. So typecheck your environment and confirm it's wired up correctly before you rely on it — [`@howells/envy`](https://github.com/howells/envy) exists precisely to catch a missing or malformed secret before a deploy, not after. ### Always work against the current docs [#always-work-against-the-current-docs] Models are confidently out of date. When you build against any library, model, or API, make sure you — and your agents — are looking at the *latest* documentation, not what a model half-remembers from training. Pull the current docs in explicitly; keep local clones when a source matters enough; don't assume a tool like Context7 has it right, because it often doesn't. The upside is that documentation is genuinely LLM-friendly now — dense, structured, easy to feed straight into context — so there's little excuse for working from a stale memory of an API that changed three versions ago. Verify the surface you're calling actually exists before you call it. ### When you do it three times, build the tool — and publish it [#when-you-do-it-three-times-build-the-tool--and-publish-it] The moment you catch yourself doing the same thing by hand for the third time, stop and make it repeatable: a script, a package, a skill, a shared component. And when the result is good enough to help someone else, publish it. Most of my published work started exactly this way — a pattern extracted from repetition and given a home: the [`@howells/*`](https://github.com/howells) config and infra packages, the [Arc](https://github.com/howells/arc) workflow, the shared UI in [patternmode](https://patternmode.com). Extraction compounds — every reusable piece you publish is one you, and everyone downstream, stops rewriting. ## Building forward [#building-forward] ### Steal good ideas [#steal-good-ideas] Nobody has this fully figured out — the whole field is improvising in real time — but some people are improvising better than most, and their code is right there to read. When you're deciding how to structure something, go look at how the good open-source repos did it (midday.ai was a favourite, RIP) and take what's genuinely better. This isn't cargo-culting, it's calibration: reading how thoughtful teams solved the same problem is the fastest way to raise your own baseline, and adapting a proven pattern beats inventing an unproven one. ### Build for two audiences — and bet on the second [#build-for-two-audiences--and-bet-on-the-second] Everything you build now has two audiences: the people who use it and the agents that read, drive, and extend it. Today that split feels lopsided toward people. It won't stay that way — within a few months the agentic surface will be the one that matters most, so build for it now rather than retrofitting it later. In practice that means interfaces an agent can actually use: CLIs and APIs that are self-describing (introspect the schema before acting), safe to rehearse (dry-run, then apply), and machine-readable by default (`--json`, structured errors with a code and a recovery hint, real exit codes). Every repo carries a short, operational `AGENTS.md` — how to work here, what not to touch, the commands to rely on — written as terse rules, not prose about the product, and treated as the canonical source other assistant configs merely point at. Record explicit non-goals so one tool doesn't quietly grow into another's runtime. Making software legible to agents is its own discipline now; [agentsurface.dev](https://agentsurface.dev) is where I keep that thinking. # Agent Skill Packaging (/docs/adr/0001-agent-skill-packaging) # Agent Skill Packaging [#agent-skill-packaging] Scaffold's Project Docs should remain the source of truth, with one agent skill distribution surface exposed to Codex, Claude Code, and Cursor through platform-specific Skill Wrappers. This keeps the Scaffold Baseline coherent across supported coding assistants while still allowing each platform to use its own metadata, command, rules, or install conventions. ## 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 shipped in part. The single-source-of-truth decision holds: `scripts/sync-skill.ts` (run via `pnpm sync:skill`) is the sync mechanism, generating `skills/scaffold/references/` from `docs/` so no distribution surface is hand-maintained. Not every assistant surface envisioned above ships yet, so this records what actually exists. * **Claude Code is served natively.** The skill directory itself is the Claude Code skill format: `skills/scaffold/SKILL.md` plus `skills/scaffold/references/` *is* a valid Claude Code skill. Claude Code needs no separate wrapper — the absence of one is not a gap. * **Codex is served through `skills/scaffold/agents/openai.yaml`.** This is the one platform-specific wrapper that ships today. * **Cursor has no wrapper yet.** Cursor wrapper generation is deferred as future platform work. It is deliberately *not* bolted onto `scripts/sync-skill.ts`, which stays focused on producing the shared `references/` tree from `docs/`; a Cursor surface can be layered on later without reshaping the sync contract. # Scaffold Repo Deviations (/docs/adr/0002-scaffold-repo-deviations) # Scaffold Repo Deviations [#scaffold-repo-deviations] The scaffold repository deliberately deviates from its own published baseline because it is a single-app documentation site, not a product monorepo. It drops the workspace, orchestration, hook, and test machinery the baseline assumes, and gates on a static build instead of a test suite. [Getting Started step 8](/docs/getting-started) asks every repo to record intentional deviations early rather than re-deciding the same defaults each time; this ADR is scaffold's own record. ## Considered Options [#considered-options] * Adopt the full published baseline verbatim (turbo, husky/lint-staged, vitest, workspace packages, `oxlint.config.ts`). * Deviate deliberately for a single-app content site, and record the deviations here. Full-baseline compliance was rejected because most of that machinery orchestrates or protects concerns this repo does not have: no second package to build, no shared test surface, no high-volume multi-author commit stream. Carrying it unused would be dead configuration that rots and misleads readers about what the baseline is *for*. ## Deviations [#deviations] * **No `turbo` / `turbo.json`.** Turbo orchestrates work across packages. This is a single package, so there is nothing to orchestrate. * **No `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` / `test` script; `check` gates on `build` instead of `test`.** For a content site the meaningful correctness gate is that every docs page still statically generates. `pnpm check` therefore runs `lint && typecheck && build && sync:skill --check` — the build *is* the test. * **`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 enumerates root files explicitly.** It runs `howells-oxfmt --write AGENTS.md README.md package.json pnpm-workspace.yaml next.config.mjs source.config.ts postcss.config.mjs tsconfig.json src` rather than the directory form `howells-fix .`. This is intentional: it formats exactly the tracked source directory plus the specific root config files, and avoids sweeping generated output (`.next`, build artifacts) or vendored reference trees. The explicit list is the deliberate scope, not an oversight to migrate. ## Consequence [#consequence] These deviations are scoped to the fact that scaffold is a docs site. They do not weaken the published baseline for product repos, which should still adopt turbo, hooks, a real test suite, and workspace packages where those concerns apply. # Agent Workflow (/docs/reference/agent-workflow) # Agent Workflow [#agent-workflow] This is the baseline for repos that will be worked on with coding assistants, Arc, and reusable specialist skills. Claude Code is served natively by the skill directory itself, Codex through a platform wrapper (`agents/openai.yaml`), and a Cursor surface is deferred future work rather than something that ships today. ## 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 and direct. The best `AGENTS.md` files change behavior without becoming documentation sludge. ## Arc [#arc] Use Arc as the higher-level software delivery lifecycle when a task needs structure rather than raw code generation. Current Arc entry points: * `/arc:vision` for a concise project north star in `docs/vision.md` * `/arc:ideate` for turning ideas into concrete feature specs * `/arc:implement` for scope-aware implementation with TDD and verification * `/arc:testing` for characterization tests around existing code before risky change * `/arc:review` for expert review of plans, specs, or implementation approaches * `/arc:audit` for verified codebase health reports * `/arc:refactor` for structural refactor discovery and RFC-style plans * `/arc:launch` for go-live and shareability readiness * `/arc:commit` for clean atomic commits, with push or publish only when requested In some assistants, the same workflows may be available as skills or commands such as `$ideate`, `$implement`, `$review`, `$audit`, `$refactor`, `$testing`, `$launch`, and `$commit`. Arc's current full runtime is more than prompt text. It includes: * `skills/` for the lifecycle workflows * `commands/` for Claude slash-command stubs * `agents/` for specialist research, review, build, and workflow support * `disciplines/` for TDD, debugging, verification, branch finishing, and subagent coordination * `references/` for Arc-owned architecture, testing, review, platform, and delivery guidance * `rules/`, `templates/`, `scripts/`, and tests for workflow support Prefer the full Arc install for repos where agent workflows are central. Arc should expose the same lifecycle guidance through each supported assistant rather than maintaining separate hand-written workflow forks. Prompt-only installs are acceptable for lightweight routing, but they do not include Arc's bundled agents, references, disciplines, templates, scripts, or rules. Arc's specialist agents are support machinery. Users should normally start with a lifecycle workflow, not a specialist agent. Completed workflow activity may be logged to `.arc/log.md`; keep that as local operational history rather than product documentation. Do not route specialist work through Arc just because Arc exists. Arc owns the delivery lifecycle. Brand systems, UI direction, browser QA, package extraction, prose cleanup, boundary enforcement, plugin packaging, and naming should use the independent skills collection when that gives a sharper tool. ## 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 ``` Use independent skills when a task is cross-repo and specialist: * `aperture` for extracting reusable packages, features, components, hooks, or utilities * `chiaroscuro`, `brand`, and `foundry` for visual direction, brand systems, and Tailwind v4 identity work * `fieldtest` for rendered browser QA with evidence-backed findings * `componentize` for UI reuse audits and scoped shared-component promotion * `fenceline` for JavaScript and TypeScript boundary enforcement with `@howells/boundaries` * `fail-fast` for removing hidden fallbacks and permissive compatibility paths * `heathen` for oversized files, god components, and safe decomposition plans * `marginalia` for concise JSDoc on public APIs and complex exports * `mastraudit` for Mastra implementation and package-boundary audits * `nomen` for naming and availability checks * `deslop` for cleaning AI-sounding prose * `polyplugin` for dual Claude Code and Codex plugin packaging 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 `howells/motif` before raw fal.ai clients for image generation, image editing, media utilities, CLI automation, or MCP image tools * 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 Codex or Arc, 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`, Arc for delivery workflow, and independent skills for specialist depth. ## Root Scripts That Agents Should Expect [#root-scripts-that-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` If a repo chooses different names, it is increasing friction for no real gain. ## 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] When using Arc-style workflows, keep documentation and progress lightweight but real: * plans go in `docs/` when the repo benefits from them * progress logs should capture decisions, not every keystroke * docs should describe the current system, not preserve outdated migration stories forever ## 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 when a repo is not just calling a model, but exposing agent-facing behavior, agent-owned 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 Agentic Infrastructure Is Warranted [#when-agentic-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 when the process is defined. Add more agents only when responsibilities split by domain, not just by implementation task. ## Framework Selection [#framework-selection] Use the existing stack first. If the repo already has a deliberate agent framework, deepen that rather than starting over. 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 Ground Rules [#mastra-ground-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 ``` Use `packages/mastra` instead of burying Mastra inside an app route. Keep app code as a client of 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 `howells/motif` before creating a fresh provider integration. Use `@howells/motif-sdk` inside product or package code, `@howells/motif-cli` for scriptable local and agent workflows, and `@howells/motif-mcp` when image generation, editing, upscaling, background removal, image-to-video, model metadata, or history should be exposed through MCP. 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 * 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 are the recurring architecture choices across your active repos. They are not universal laws. They are the default answers when a new project needs these capabilities and there is no strong reason to do something else. These defaults intentionally ignore PHP/Craft maintenance surfaces. The useful signal for new work is the direct TypeScript, UI, data, and agent dependency surface. ## Full-Stack TypeScript App [#full-stack-typescript-app] For a serious TypeScript product app, the recurring stack is: * Next.js 16.2 App Router * `tRPC` for the typed API layer * `@tanstack/react-query` for client data orchestration * Drizzle for the database layer * Neon Postgres via `@neondatabase/serverless` This pattern shows up strongly in current full-stack product apps. ### Default package set [#default-package-set] * `@trpc/server` * `@trpc/client` * `@trpc/tanstack-react-query` * `@tanstack/react-query` * `drizzle-orm` * `drizzle-kit` * `@neondatabase/serverless` ### When this is the right default [#when-this-is-the-right-default] Use this 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 real database-backed behavior, not just static pages ### When not to use it [#when-not-to-use-it] 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 alternative. It keeps end-to-end TypeScript inference while producing an OpenAPI contract, so a non-TypeScript client is a first-class consumer. Reach for oRPC in that case rather than hand-rolling REST handlers. ## Database and Persistence [#database-and-persistence] The clear default is: * Postgres * Drizzle (ORM + typed schema) * Neon for serverless-hosted Postgres * `@neondatabase/serverless` as the driver — never raw `pg`/`node-postgres`, and `postgres.js` only for a concrete long-running-service need This is one of the strongest repeated patterns in the current portfolio. Use Drizzle from day one — hand-written SQL with manual row typing is not the baseline. ### Connecting Drizzle to Neon (the rule) [#connecting-drizzle-to-neon-the-rule] Pick the adapter by runtime, not by habit. Both adapters use the same `@neondatabase/serverless` package: * **Default — `drizzle-orm/neon-http`** (`neon(DATABASE_URL)` → `drizzle(sql, { schema })`). Use for all app data access (Server Components, route handlers, serverless/edge functions) and short-lived scripts. HTTP one-shot queries are lowest-latency for request/response work, are edge-safe, and `db.transaction([...])` still gives atomic batched (non-interactive) writes — which covers almost every write path, including multi-table ingestion. * **Escape hatch — `drizzle-orm/neon-serverless`** (`Pool` over WebSockets; set `neonConfig.poolQueryViaFetch = true` on edge). Use **only** when a runtime genuinely needs *interactive* (session) transactions — multi-step logic that branches mid-transaction — or node-postgres compatibility. Typically long-running workers/CLIs. Open and close the pool within the request/process. Do not mix adapters within one package without a reason, and do not reach for `postgres.js`/`node-postgres` as a default — that is driver sprawl. ### Schema and migrations [#schema-and-migrations] * **Schema-first.** The Drizzle schema (`packages/db/src/schema.ts`) is the source of truth. * **`drizzle-kit push`** is the workflow for syncing schema to the database. Do not hand-author migration files or runtime DDL. For an existing database, bootstrap the schema once with `drizzle-kit pull`, then own it via push. * Schema lives in the first-tier `packages/db` boundary. ### Driver notes (GA `@neondatabase/serverless` ≥ 1.0) [#driver-notes-ga-neondatabaseserverless--10] * 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). * Over HTTP, wrap reads/writes in **retry-on-transient-drop** logic; connections can blip during maintenance. ## 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 as of July 2026, 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, structured CLI output, and MCP tools. 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 that genuinely need AI features, the recurring pattern is: * `ai` (AI SDK v6) 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 Default model access is the AI Gateway. On AI SDK v6 the global provider is the Vercel AI Gateway, so pass a `"provider/model"` string into AI SDK calls and requests route through the Gateway — one credential, provider switching without new SDK wiring. Per-provider `@ai-sdk/*` packages are the escape hatch for direct-provider needs, still behind `@howells/ai`. Keep the model-string boundary 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` When the repo is doing real agent orchestration, add Mastra deliberately: * `@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] Recent agent-heavy repos are converging on 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` This avoids the common mistake of stretching a simple drawer primitive into a multi-layer workflow it was not designed to own. ## Short Version [#short-version] For a new product app, the default answer is usually: * Next.js * `tRPC` * React Query * Drizzle * Neon * Clerk * 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 Only deviate when the product constraints actually justify 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] These are the canonical starting snippets for a new repo. Adjust names and filters, but do not casually change the overall contract. ## Root `package.json` [#root-packagejson] ```json { "name": "my-project", "private": true, "packageManager": "pnpm@11.10.0", "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": "husky" }, "devDependencies": { "@howells/lint": "^1.0.0", "@howells/typescript-config": "^0.1.6", "husky": "9.1.7", "lint-staged": "17.0.8", "tsx": "^4.22.4", "turbo": "2.10.4", "typescript": "6.0.3", "vitest": "^4.1.8" }, "lint-staged": { "*.{js,ts,jsx,tsx}": "howells-fix", "*.{json,jsonc,css,md}": "howells-oxfmt --write" }, "engines": { "node": ">=24.18.0 <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@11.10.0` is the current house baseline * for published packages that can support Node 22, use `"node": ">=22.22.3"` in the package itself while keeping repo tooling on Node 24 ## `.node-version` [#node-version] ```text 24.18.0 ``` 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/ # typed app API layer 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/*" ignoredBuiltDependencies: - esbuild - sharp - unrs-resolver ``` If the repo genuinely needs extra workspaces such as `scripts/*`, add them explicitly. ## 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 } } } ``` Only add task-level `env` when a task actually needs 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. ## `.husky/pre-commit` [#huskypre-commit] ```sh pnpm lint-staged pnpm lint pnpm typecheck ``` Use this as the default. Only make it heavier when the repo truly needs that pressure. ## `.husky/pre-push` [#huskypre-push] Optional heavier gate: ```sh pnpm lint || exit 1 pnpm typecheck || exit 1 pnpm test || exit 1 ``` ## 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] Default adapter is `drizzle-orm/neon-http`. The schema lives in `packages/db`; the client is a memoized singleton. ```ts // packages/db/src/client.ts import { drizzle } from "drizzle-orm/neon-http"; import { neon } from "@neondatabase/serverless"; import { getDatabaseUrl } from "@your-scope/env"; import * as schema from "./schema"; let cached: ReturnType> | null = null; export const getDb = () => { cached ??= drizzle(neon(getDatabaseUrl()), { schema }); return cached; }; export type Db = ReturnType; ``` Use `drizzle-orm/neon-serverless` (`Pool` over WebSockets) instead **only** for runtimes that need interactive/session transactions (long-running workers/CLIs); set `neonConfig.poolQueryViaFetch = true` on edge. ```ts // drizzle.config.ts import "dotenv/config"; // or load env via envy before invoking drizzle-kit import { defineConfig } from "drizzle-kit"; import { getDatabaseUrl } from "./packages/env/src"; export default defineConfig({ schema: "./packages/db/src/schema.ts", out: "./drizzle", dialect: "postgresql", dbCredentials: { url: getDatabaseUrl() }, strict: true, verbose: true, }); ``` Schema-first workflow — `push`, not migration files: ```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" } } ``` ## 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 this short. It should constrain agent behavior, not restate your whole engineering philosophy. # Default Dependencies (/docs/reference/default-dependencies) # Default Dependencies [#default-dependencies] This is the package baseline inferred from recent active repos. It is split by repo type so new projects do not have to re-decide the same dependencies repeatedly. The baseline is based on direct manifests only. Composer `vendor`, generated build output, and legacy PHP/Craft projects are ignored for new TypeScript project defaults. ## Every Repo [#every-repo] Install these by default: * `@howells/lint` * `@howells/typescript-config` * `turbo` * `typescript` * `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] These recur often enough that they should be treated as the default UI baseline: * `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 These are common enough to treat as default-adjacent: * `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` These are not mandatory in every package, but they recur enough that they should be the first options rather than random alternatives. `@base-ui/react` is the default primitive layer, following shadcn's July 2026 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` * `trpc` * `motion` * `auth` * repo-local `ai` packages above `@howells/ai` * `agents` * `mcp` These are common enough to treat as optional defaults rather than one-off inventions: * `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 defaults for richer app repos: * `@trpc/server` * `@trpc/client` * `@trpc/tanstack-react-query` * `@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. For full-stack TypeScript apps, `tRPC` should now be treated as a default recommendation, not just an occasional package. 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 * use `@howells/motif-mcp` when other agents should call image-generation and media tools through MCP Use Motif before writing one-off fal.ai clients. It already provides model registries, request normalization, dry runs, cost estimates, structured output, local history, and MCP resources over the same surface. 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` * `@howells/motif-mcp` 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` Default model access on `ai` (AI SDK v6) is the AI Gateway: pass a `"provider/model"` string and requests route through the Vercel AI Gateway with no per-provider client in app code. 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` This is the important distinction: * `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 turbo typescript husky lint-staged tsx vitest ``` ### New Next.js UI monorepo [#new-nextjs-ui-monorepo] ```bash pnpm add -D @howells/lint @howells/typescript-config turbo typescript husky 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 @trpc/server @trpc/client @trpc/tanstack-react-query drizzle-orm @neondatabase/serverless @howells/envy pnpm add -D drizzle-kit ``` ### 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 CLI or MCP use: ```bash pnpm add -D @howells/motif-cli @howells/motif-mcp ``` ### 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 ``` ## Packages That Are Recurring Enough To Standardize [#packages-that-are-recurring-enough-to-standardize] These are the strongest repeated dependencies from the scan of active repos: * `@trpc/server` * `@trpc/client` * `@trpc/tanstack-react-query` * `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` That does not mean every repo needs all of them. It means they should be your default shortlist, not re-litigated from zero each time. # Deployment Defaults (/docs/reference/deployment-defaults) # Deployment Defaults [#deployment-defaults] This is the default deployment logic inferred from your active repos. The purpose is to make deployment a deliberate architectural choice early, not an afterthought. ## Default Web Deployment [#default-web-deployment] For Next.js apps, docs sites, and Storybook-like web surfaces: * default to Vercel This is the clearest repeated pattern for web-facing surfaces in your portfolio. Use Vercel for: * product web apps * docs sites * Storybook deployments * preview and production web releases ### Current Vercel platform defaults [#current-vercel-platform-defaults] * Use `vercel.ts` via `@vercel/config` for project configuration. Prefer it over `vercel.json` — typed config catches mistakes at author time. * 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 This already aligns with the docs-style surfaces in the current portfolio. ## 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 typed app APIs close to the main app via `tRPC` 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 This is a platform decision, not just a package decision. 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 ## 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 * 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 ## Short Version [#short-version] * 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 That is the default unless the repo has a concrete reason to deviate. # Launch Checklist (/docs/reference/launch-checklist) # Launch Checklist [#launch-checklist] Use this when starting a new repo or standardizing an existing one. ## Baseline [#baseline] * `packageManager` pinned to the settled house pnpm line, currently `pnpm@11.10.0` * Node engine pinned to `>=24.18.0 <25` for apps and services * `.node-version` pins the current Node 24 LTS patch * `turbo` pinned to `2.10.4` * `typescript` pinned to `6.0.3` * `lint-staged` pinned to `17.0.8` * `@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 * `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 * `.husky/pre-commit` is installed * `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 * `tRPC` is the default typed app API boundary unless the API needs to be language-agnostic * `@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 ## 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 # Package Boundaries (/docs/reference/package-boundaries) # Package Boundaries [#package-boundaries] These are the package boundaries that keep recurring across your serious monorepos. The goal is not maximal modularity. The goal is to stop app code from swallowing shared infrastructure and then becoming impossible to reuse or reason about. For new scaffolds, derive these boundaries from TypeScript projects only. Legacy PHP/Craft repos do not set the default package model. ## Default Boundaries for a Full-Stack App [#default-boundaries-for-a-full-stack-app] For a real product app, this is the clean default: ```text apps/ web/ packages/ db/ trpc/ ui/ typescript-config/ tailwind-config/ env/ motion/ ``` Add more only when the product clearly needs them. ## `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 These Only When Needed [#add-these-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 wrapping or configuring `@howells/motif-mcp` before creating a fresh MCP server surface. ### `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. ## What Should Stay in `apps/web` [#what-should-stay-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. ## What Not to Extract [#what-not-to-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 If a package does not express a real dependency boundary, it is probably cargo-cult modularity. ## Good Signs a Boundary Is Real [#good-signs-a-boundary-is-real] * 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 ## Bad Signs a Boundary Is Fake [#bad-signs-a-boundary-is-fake] * 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 ## Strong Defaults [#strong-defaults] If you do nothing else right, get these boundaries right first: 1. `db` 2. `ui` 3. `typescript-config` 4. `tailwind-config` 5. `trpc` 6. `motion` 7. `env` 8. `ai` / `mastra` / `agents` / `mcp` when agent behavior is part of the product That is where the portfolio already shows a durable pattern. # Repo Archetypes (/docs/reference/repo-archetypes) # Repo Archetypes [#repo-archetypes] Most new repos should start from one of these shapes. The point is to reduce unnecessary architectural improvisation. These archetypes are for new TypeScript-first work. PHP/Craft maintenance projects are outside the default scaffold unless explicitly requested. ## 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 16.2 App Router * `tRPC` * React Query * Drizzle * Neon * Clerk by default * 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/ 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 ``` This is the most common serious-app archetype in your portfolio. ## 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/ ``` This should be the model for new shared UI work, not fresh one-off design system repos. ## 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.22.3` 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/ ``` This is closer to a service-heavy media platform model than the UI-system model. ## 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` * `howells/motif` packages when image generation, editing, media utilities, CLI automation, or MCP image tools 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 These repos usually need stronger script and data-pipeline conventions than typical UI apps. ## Choosing Between Archetypes [#choosing-between-archetypes] 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 If a repo looks like two archetypes at once, choose the dominant one and add the secondary capabilities carefully. Do not mash two entire architectures together by default. # Shared Package Candidates (/docs/reference/shared-package-candidates) # Shared Package Candidates [#shared-package-candidates] This section separates packages that are already real shared assets from patterns that look ready to become them. ## Already Shared and Real [#already-shared-and-real] These should be treated as canonical shared packages now: * `@howells/lint` * pinned Oxlint/Oxfmt, Biome, 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/motif-mcp` * MCP tools and resources for image generation, editing, utility media actions, model metadata, and history * `@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 ## Keep Standardizing: AI Provider Baseline [#keep-standardizing-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. ## Keep Standardizing: Image Generation Surface [#keep-standardizing-image-generation-surface] `howells/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, structured CLI output, and MCP image tools. Recommendation: * use `@howells/motif-sdk` for product or package code * use `@howells/motif-cli` for scriptable local and agent workflows * use `@howells/motif-mcp` when other agents need image-generation tools through MCP * 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 Package [#strong-candidate-motion-tokens-package] 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: Shared Drawer and Sidepanel Layer [#medium-candidate-shared-drawer-and-sidepanel-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 ## Not a Good Shared Package Candidate Yet [#not-a-good-shared-package-candidate-yet] 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 Recommendation vs Package Recommendation [#product-recommendation-vs-package-recommendation] There is an important distinction here: * 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 ## Practical Standardization Order [#practical-standardization-order] If you want to reduce duplicated package work across the portfolio, the best order is: 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 That order reduces duplication without locking in the wrong abstractions too early. # 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 Versions [#core-versions] Pin these unless there is a deliberate repo-specific reason not to: | Tool | Baseline | | ---------------------------- | ---------------------------------------------- | | `pnpm` | `11.10.0` | | `turbo` | `2.10.4` | | `typescript` | `6.0.3` | | `husky` | `9.1.7` | | `lint-staged` | `17.0.8` | | `@howells/lint` | `1.0.0` | | `@howells/typescript-config` | `0.1.6` | | `@howells/envy` | `0.3.7` | | Node | latest Node 24 LTS patch (currently `24.18.0`) | ## Package Manager [#package-manager] * Use `pnpm`. * Pin `packageManager` in the root `package.json`. * Prefer one lockfile at the repo root. * Treat `pnpm@11.10.0` as the settled baseline. * 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: `"node": ">=24.18.0 <25"` * CI: Node `24.x` * local version files: pin the latest Node 24 LTS patch * published packages: keep runtime support at `>=22.22.3` 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. The recent pattern across active Turborepos is clear: hidden stale-cache failures 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. ## Husky and Git Hooks [#husky-and-git-hooks] Use Husky for lightweight quality gates. Default approach: * `prepare`: `husky` * `pre-commit`: run `lint-staged`, then repo-wide `lint` and `typecheck` * `pre-push`: only when the repo needs a heavier gate such as `test` or stricter validation Across recent repos, the stable baseline is small hooks plus standard root scripts. ## UI Stack [#ui-stack] For new UI repos: * Next.js 16.2 App Router * React 19.2 * Tailwind CSS 4.3 * 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 defaults to Base UI as of July 2026, 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. This does not mean every product should look the same. It means structural decisions should be shared while brand and product expression stay local. ### Next 16 baseline [#next-16-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] }); }, }); } ``` This replaces `useEffect` + `useState` + `fetch` patterns. React Query handles loading states, error states, caching, and cache invalidation. ## Dependency Standard [#dependency-standard] The active repos are not just converging on config. They are also converging on a real dependency baseline. The strongest direct-dependency signals in the 2026-05 inventory are: * `typescript`: 90 projects * `react`: 80 projects * `tailwindcss`: 75 projects * `next`: 72 projects * `zod`: 54 projects * `@howells/lint`: 48 projects * `lucide-react`: 48 projects * `vitest`: 47 projects * `turbo`: 45 projects * `motion`: 44 projects * `tsx`: 43 projects * `@howells/typescript-config`: 40 projects * `drizzle-orm`: 33 projects * `@neondatabase/serverless`: 31 projects * `ai`: 22 projects * `@mastra/core`: 18 projects * `@modelcontextprotocol/sdk`: 18 projects 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` The repeated package names across your Turborepos are also clear enough to treat as default boundaries, not accidental patterns: * first tier: `db`, `ui`, `typescript-config`, `tailwind-config` * second tier: `utils`, `trpc`, `motion`, `auth`, `mastra`, `agents`, `mcp`, repo-local `ai` packages above `@howells/ai` * 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` for image generation, editing, utility media tools, and agent-facing creative automation * 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 There is also a recurring architecture baseline for full-stack apps: * `tRPC` for typed API boundaries * 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 are now common enough that they should have a standard starting shape. Default package choices: * `ai` (AI SDK v6) for the Vercel AI SDK surface * `@howells/ai` for shared provider defaults and house wrappers * `howells/motif` packages when image generation, image editing, media utilities, CLI automation, or MCP image tools 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] Default model access is the AI Gateway. On AI SDK v6 the global provider is the Vercel AI Gateway, so pass a `"provider/model"` string straight into AI SDK calls and requests route through the Gateway. This is the default surface — one credential, provider switching without new SDK wiring, and no per-provider client in app code. 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. # UI Projects (/docs/reference/ui-projects) # UI Projects [#ui-projects] For new UI-based projects, the default is a local UI package seeded from the scaffold's bundled UI baseline. That is the key decision in this scaffold: the shared shape lives here, not 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 ## What to Reuse vs What to Own [#what-to-reuse-vs-what-to-own] 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 ## What Not to Do [#what-not-to-do] * 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 16.2 App Router * React 19.2 * Tailwind CSS 4.3 * 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 as of July 2026 (`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. That does not mean every app needs a huge Storybook surface. It means shared UI should have a visible contract and a place for visual regression checks. ## Bundled Baseline In Flight [#bundled-baseline-in-flight] 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 This keeps new projects aligned without depending on a separate live upstream. ## Migration Rule for Existing Projects [#migration-rule-for-existing-projects] 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 Do not attempt a one-shot visual rewrite just to claim alignment. # Bundled UI Baseline (/docs/reference/ui-baseline) # Bundled UI Baseline [#bundled-ui-baseline] This directory is the scaffold's maintained UI baseline. It originated as UI-system files recovered from the old `~/Sites/patternmode` history at commit `1ffeb6ddb9d5d449a3618f3b73f79b93590cd463^`, but it is no longer a frozen snapshot. It tracks current scaffold guidance and is migrated as the baseline moves — the primitive layer is now Base UI (`@base-ui/react`), following the scaffold default, and lint scripts and shared dependencies are kept on current versions. Treat this as scaffold-owned reference material, not as an external upstream dependency. When creating a new UI repo, copy and adapt the relevant package shapes into that repo, then rename package scopes and imports to the target project. ## What Is Included [#what-is-included] * `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 the Tailwind CSS 4.3 shared stylesheet and token wiring. * `source/packages/ui/README.md` for the intended component package shape. * `source/docs/design-context.md` and `source/docs/arc/specs` for the visual and architectural rationale behind the baseline. * `source/apps/web/content/docs` for the old docs copy around installation, tokens, motion, and responsive behavior. ## How To Use It [#how-to-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 the relevant package files from `source/packages/*`. 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 original `@patternmode/*` package names are kept as workspace-local identifiers to rename per repo, but the code and dependencies are maintained against current scaffold guidance rather than frozen for provenance. Because this is vendored reference material, it is not built by this repo — build it inside a bootstrapped repo before trusting it. The scaffold guidance should point at this bundled baseline, not at a live Patternmode package.