Files
agentic-dev/docs/guides/scaffolding-a-feature.md
Danijel Martinek 03d4e0cbc8 docs: strip residual Phase/Plan refs from guides
Five spots across three guides referenced the original implementation
phasing (Plan-N / Phase-N nomenclature) from the template's setup era.
Now that the template-reset epic completed and the setup history was
archived, these refs are dead vocabulary for fresh consumers.

guides/scaffolding-a-feature.md:
  - "real repo body is a Phase-1 stub" -> "real repo body is a stub"
  - "## Phase-1 scope (intentionally limited)" -> "## Scope (intentionally limited)"
  - "manually authored as part of Phase-2 wiring" -> "manually authored
    as part of the post-scaffold wiring"

guides/adding-a-feature.md:
  - "the generator's Phase-1 scope doesn't fit" -> "the generator's
    default scope doesn't fit"

guides/tdd-workflow.md:
  - "**File naming convention (post-Plan-8):**" -> "**File naming
    convention:**"

The Plan-9 refs in docs/architecture/data-flow-explainer.html will be
handled separately along with that file's other staleness in the
next commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 17:01:17 +02:00

131 lines
6.6 KiB
Markdown

# Scaffolding a feature
`turbo gen feature` produces a feature package under
`packages/<name>/` matching the shape of the reference `navigation` feature.
## Invoking the generator
Interactive (recommended for first runs — Plop will prompt for each value):
```bash
pnpm turbo gen feature
```
Non-interactive (positional bypass — order matches the prompts in
`turbo/generators/config.ts`):
```bash
pnpm turbo gen feature --args <name> <Entity> <entities-plural>
```
| Position | Prompt | Example | Conventions |
| ------------------- | -------------------- | --------- | ----------------------------------------------------------- |
| `<name>` | Feature package name | `widgets` | `kebab-case`, becomes `@repo/<name>` and `packages/<name>/` |
| `<Entity>` | Entity name | `Widget` | `PascalCase` singular, drives class/symbol/use-case names |
| `<entities-plural>` | Entity plural slug | `widgets` | `kebab-case`, used for the future Payload collection slug |
Example end-to-end:
```bash
pnpm turbo gen feature --args widgets Widget widgets
pnpm install # link the new workspace package
pnpm --filter @repo/widgets lint typecheck test
```
## Conformance-ready by default
Since milestone v of the conformance system, `pnpm turbo gen feature <name>` emits two conformance artefacts:
- **`src/feature.manifest.ts`** declaring the scaffolded `getX` use case
- **`src/di/bind-production.ts`** with `assertFeatureConformance(...)` called at the tail
Run `pnpm conformance` after generating a feature — it should pass cleanly. If you add `bus.publish("X")` calls in a factory body, you'll also need to add `"X"` to the manifest's `publishes[]` array for that use case, or the `no-undeclared-event-publish` ESLint rule will warn.
See `docs/guides/conformance-quickref.md` for the manifest field reference.
## What it generates
- Package files: `package.json`, `tsconfig.json`, `vitest.config.ts`,
`eslint.config.js`, `turbo.json`, `AGENTS.md`
- One entity (`src/entities/models/<entity>.ts`) with a Zod schema +
unit test
- One use case (`src/application/use-cases/get-<entity>.use-case.ts`) with
exported input/output schemas, factory function, and tests
- One controller (`src/interface-adapters/controllers/get-<entity>.controller.ts`)
with the canonical safeParse → presenter shape
- Mock + real repository (`src/infrastructure/repositories/<entity>.repository{,.mock}.ts`).
Both wrap calls in `tracer.startSpan`; the real repo also calls
`logger.captureException` on errors. The real repo body is a
stub that returns `null` until you wire a Payload collection.
- DI: `symbols.ts`, `module.ts`, `container.ts`, plus
`bind-production.ts` and `bind-dev-seed.ts` that compose
`withSpan(tracer, opts, withCapture(logger, tags, factory(deps)))` at
bind time (span + capture sandwich pattern)
- tRPC integration: `procedures.ts` (feature-scoped error middleware) and
`router.ts` exposing `get<Entity>` with full router tests including
`BAD_REQUEST` / `NOT_FOUND` mapping
- Contract suite (`__contracts__/`), dev seed (`__seeds__/dev.ts`), and
empty stubs for `__factories__/` and `ui/`
## Scope (intentionally limited)
The generator does NOT yet emit:
- Payload CMS collection / global templates (`integrations/cms/**`)
- React Query option builders (`ui/query.ts`)
- Faker-driven `defineFactory<Entity>` factories (only stubs)
- Multi-entity / multi-use-case (one `get<Entity>` only)
- Aggregator wiring across the monorepo
Add these by hand once the entity stabilises. The generator's stub files
clearly mark each `TODO`.
## Manual aggregator wiring (printed on success)
After running the generator, hand-edit these files to mount the new
feature on the app's runtime composition graph:
1. **`apps/web-next/src/server/bind-production.ts`** — import
`bindProduction<Name>` and `bindDevSeed<Name>` and call them from the
`bindAll()` dispatcher (production branch + dev-seed branch).
2. **`packages/core-api/src/root.ts`** — import `<name>Router` from
`@repo/<name>/api` and mount it on the app router.
3. **`packages/core-api/package.json`** — add `"@repo/<name>": "workspace:*"`
to dependencies.
4. **`apps/web-next/package.json`** — add `"@repo/<name>": "workspace:*"`
to dependencies.
5. **(Later) Payload CMS** — add a collection at
`packages/<name>/src/integrations/cms/collections/<entities-plural>.ts`
and register it in `packages/core-cms/...`.
6. **Verify**: `pnpm --filter @repo/<name> lint typecheck test`
The same checklist is printed by the generator when it finishes.
## Adding events and jobs to a feature
Once a feature exists, augment it with cross-feature events or background jobs using the dedicated generators. See [`docs/guides/events-and-jobs.md`](./events-and-jobs.md) for full walkthroughs.
```bash
pnpm turbo gen event publish # publisher contract
pnpm turbo gen event consume # consumer handler + Payload event-task
pnpm turbo gen job # background job + TaskConfig
pnpm turbo gen realtime channel # realtime channel descriptor (ADR-016)
pnpm turbo gen realtime handler # inbound realtime handler (ADR-016)
```
The event/job generators insert at six fixed `// <gen:*>` anchor comments. Generated features include four of them automatically (the `// <gen:job-tasks>` location is in `integrations/cms/index.ts`, which is manually authored as part of the post-scaffold wiring); pre-existing features were retrofitted in ADR-015.
The realtime generators insert at three additional fixed `// <gen:realtime-*>` anchor comments (`// <gen:realtime-channels>` in `src/index.ts`, `// <gen:realtime-handler-symbols>` in `src/di/symbols.ts`, `// <gen:realtime-handlers>` in both `bind-*.ts` files). Generated features include all three automatically; pre-existing features were retrofitted in ADR-016.
## Cross-links
- `CLAUDE.md` — Key Conventions (factory-style use cases, `.toDynamicValue()`,
schemas-in-use-case, three binding modes per feature, span + capture sandwich)
- `packages/navigation/AGENTS.md` — canonical reference shape the templates mirror
- `docs/architecture/vertical-feature-spec.md` — design rationale for the layout
- `docs/decisions/adr-012-feature-conventions.md` — file naming + factory pattern
- `docs/decisions/adr-013-input-output-unification.md` — schemas-in-use-case + presenter
- `docs/decisions/adr-014-instrumentation-sentry.md` — span + capture wiring
- `docs/decisions/adr-015-events-and-jobs.md` — cross-feature events + background jobs
- `docs/decisions/adr-016-realtime-layer.md` — Socket.IO realtime channels + handlers