214 lines
7.2 KiB
Markdown
214 lines
7.2 KiB
Markdown
# Conformance Milestone v — Generator updates
|
|
|
|
**Goal:** `pnpm turbo gen feature <name>` now emits a `feature.manifest.ts` declaring the scaffolded use case, and the scaffolded `bind-production.ts` calls `assertFeatureConformance` at its tail. New features are conformance-compliant from creation.
|
|
|
|
**Architecture:** Two template additions/changes in `turbo/generators/templates/feature/`:
|
|
1. NEW: `src/feature.manifest.ts.hbs` — emits `defineFeature({...})` with the scaffolded use case declared
|
|
2. UPDATE: `src/di/bind-production.ts.hbs` — adds imports for `assertFeatureConformance` + `<name>Manifest` and calls the assertion at the tail
|
|
|
|
Plus one `config.ts` action to emit the manifest template, and snapshot regeneration if the existing tests snapshot the full file tree.
|
|
|
|
---
|
|
|
|
## Tasks
|
|
|
|
### Task 1: Story 05 scaffold
|
|
|
|
Create `docs/work/conformance-system-v1/05-generator-updates/_story.md`:
|
|
|
|
```markdown
|
|
---
|
|
id: 05-generator-updates
|
|
epic: conformance-system-v1
|
|
title: Generator updates — emit feature.manifest.ts + self-asserting bind-production
|
|
type: technical-story
|
|
status: in-progress
|
|
feature: turbo-generators
|
|
depends-on: [04-ci-drift-gate]
|
|
blocks: [06-feature-migrations]
|
|
---
|
|
|
|
## Goal
|
|
`pnpm turbo gen feature <name>` produces a feature that's conformance-
|
|
compliant out of the box: ships a `feature.manifest.ts` declaring the
|
|
scaffolded use case, and the scaffolded `bind-production.ts` calls
|
|
`assertFeatureConformance` at its tail.
|
|
|
|
## Why
|
|
Today, generating a new feature does NOT emit a manifest, so
|
|
`feature-must-have-manifest` would warn the moment the developer saves.
|
|
And `bindProductionX` doesn't self-assert. New features should be
|
|
conformance-ready by default.
|
|
|
|
## Done when
|
|
- `feature.manifest.ts.hbs` template exists, declaring the scaffolded
|
|
`getX` use case with mutates: false / empty arrays
|
|
- `bind-production.ts.hbs` imports `assertFeatureConformance` +
|
|
`<name>Manifest` and calls the assertion at the tail
|
|
- `config.ts` action emits the manifest file
|
|
- Running `pnpm turbo gen feature demo` into a tmp dir produces a
|
|
feature directory containing a valid `feature.manifest.ts`
|
|
- Existing snapshot tests pass (regenerate snapshots if needed)
|
|
|
|
## In scope
|
|
- Manifest template
|
|
- bind-production template update
|
|
- config.ts action wiring
|
|
- Snapshot regen
|
|
|
|
## Out of scope
|
|
- Multi-use-case feature scaffolding
|
|
- Generator changes to other generators (event/job/realtime)
|
|
- Backfilling manifests for existing features (milestone vi)
|
|
|
|
## Tasks
|
|
- [ ] Story scaffold
|
|
- [ ] feature.manifest.ts.hbs template
|
|
- [ ] bind-production.ts.hbs update
|
|
- [ ] config.ts action wiring
|
|
- [ ] Run generator into tmp; verify shape
|
|
- [ ] Snapshot regen (if needed) + verify tests pass
|
|
- [ ] Final verification + closeout
|
|
```
|
|
|
|
```bash
|
|
git add docs/work/conformance-system-v1/05-generator-updates/_story.md
|
|
git commit -m "docs(work): story 05 — generator updates"
|
|
```
|
|
|
|
### Task 2: `feature.manifest.ts.hbs` template
|
|
|
|
Create `turbo/generators/templates/feature/src/feature.manifest.ts.hbs`:
|
|
|
|
```hbs
|
|
import { defineFeature } from "@repo/core-shared/conformance";
|
|
|
|
/**
|
|
* The {{camelCase name}} feature's conformance manifest. Drives binding-slot
|
|
* types in `di/bind-production.ts` and is read by ESLint, the boot
|
|
* assertion, and the CI drift gate.
|
|
*
|
|
* Conventions:
|
|
* - `mutates: true` for any use case that creates, updates, or deletes state
|
|
* - `audits` lists every audit event the use case emits (must match calls
|
|
* to `auditLog.record(...)` in the factory body — ESLint enforces this)
|
|
* - `publishes` / `consumes` cover cross-feature events through `IEventBus`
|
|
*/
|
|
export const {{camelCase name}}Manifest = defineFeature({
|
|
name: "{{kebabCase name}}",
|
|
requiredCores: [],
|
|
useCases: {
|
|
get{{pascalCase entity}}: {
|
|
mutates: false,
|
|
audits: [],
|
|
publishes: [],
|
|
consumes: [],
|
|
},
|
|
},
|
|
realtimeChannels: [],
|
|
jobs: [],
|
|
} as const);
|
|
|
|
export type {{pascalCase name}}Manifest = typeof {{camelCase name}}Manifest;
|
|
```
|
|
|
|
(No test file for the template itself; the generator's existing snapshot tests will cover that the file is emitted with expected content.)
|
|
|
|
### Task 3: `bind-production.ts.hbs` update — call assertFeatureConformance at tail
|
|
|
|
Modify `turbo/generators/templates/feature/src/di/bind-production.ts.hbs`. Add three new imports at the top (after the existing imports):
|
|
|
|
```hbs
|
|
import { assertFeatureConformance } from "@repo/core-shared/conformance";
|
|
import { {{camelCase name}}Manifest } from "../feature.manifest";
|
|
```
|
|
|
|
At the very end of the `bindProduction{{pascalCase name}}` function body — AFTER the `// <gen:event-handlers>`, `// <gen:jobs>`, `// <gen:realtime-handlers>` anchor comments and BEFORE the closing `}` — add:
|
|
|
|
```hbs
|
|
// Boot-time conformance check: refuses to start if any use-case binding
|
|
// is missing a required brand (withSpan / withCapture / withAudit).
|
|
assertFeatureConformance(
|
|
{{camelCase name}}Container,
|
|
{{camelCase name}}Manifest,
|
|
{
|
|
get{{pascalCase entity}}: {{constantCase name}}_SYMBOLS.IGet{{pascalCase entity}}UseCase,
|
|
},
|
|
ctx,
|
|
);
|
|
```
|
|
|
|
### Task 4: `config.ts` action wiring — emit the manifest
|
|
|
|
Open `turbo/generators/config.ts`. Find the `actions` array for the `feature` generator. After the action that emits `src/index.ts` (or in the appropriate position with other `src/` files), add an action to emit `src/feature.manifest.ts` from the new template:
|
|
|
|
```ts
|
|
{
|
|
type: "add",
|
|
path: "{{ turbo_paths.root }}/packages/{{kebabCase name}}/src/feature.manifest.ts",
|
|
templateFile: "templates/feature/src/feature.manifest.ts.hbs",
|
|
},
|
|
```
|
|
|
|
If the existing actions use a different path-resolution pattern (e.g., `path.join(...)`), match that pattern. Read the surrounding actions before adding.
|
|
|
|
### Task 5: Run the generator into tmp + verify
|
|
|
|
Run a generator invocation against a tmp dir to confirm the output:
|
|
|
|
```bash
|
|
cd /tmp && rm -rf gen-test && mkdir gen-test && cd gen-test
|
|
# Initialize an empty turbo repo-like structure:
|
|
mkdir -p packages
|
|
ln -s /Users/danijel/Documents/Projects/template-vertical/.claude/worktrees/conformance-milestone-v/turbo .
|
|
```
|
|
|
|
Actually, the simpler path: run the existing snapshot tests, which already exercise the generator:
|
|
|
|
```
|
|
pnpm --filter @repo/turbo-generators test
|
|
```
|
|
|
|
If they pass (likely with snapshot mismatches because we added a new file), regenerate snapshots:
|
|
|
|
```
|
|
pnpm --filter @repo/turbo-generators test -- -u
|
|
```
|
|
|
|
Verify the regenerated snapshots include `src/feature.manifest.ts` and the updated bind-production with the assertion at the tail.
|
|
|
|
Commit the regenerated snapshots:
|
|
```bash
|
|
git add turbo/generators/__snapshots__/ turbo/generators/templates/ turbo/generators/config.ts
|
|
git commit -m "feat(generators): emit feature.manifest.ts + self-asserting bind-production"
|
|
```
|
|
|
|
### Task 6: Final verification + closeout
|
|
|
|
Run:
|
|
```
|
|
pnpm typecheck
|
|
pnpm test
|
|
pnpm lint
|
|
pnpm conformance
|
|
pnpm turbo boundaries
|
|
```
|
|
|
|
All five pass.
|
|
|
|
Update `docs/work/conformance-system-v1/05-generator-updates/_story.md`:
|
|
- frontmatter `status: in-progress` → `done`
|
|
- All `- [ ]` → `- [x]`
|
|
|
|
Update `docs/work/conformance-system-v1/_epic.md`. Find:
|
|
```markdown
|
|
- [ ] 05 — Generator emits manifest + contracts + test stubs (later plan)
|
|
```
|
|
|
|
Replace with:
|
|
```markdown
|
|
- [x] [05 — Generator updates](05-generator-updates/_story.md)
|
|
```
|
|
|
|
Commit: `docs(work): close story 05 — generator updates`.
|