# Conformance Milestone vi — Feature migrations **Goal:** Backfill manifests + self-asserting `bind-production` for the remaining four features (`blog`, `media`, `navigation`, `marketing-pages`). After this milestone, every feature is conformance-compliant; `feature-must-have-manifest` flips from WARN to ERROR. **Architecture:** For each feature: create `src/feature.manifest.ts` declaring its use cases (with `mutates: true` for state-changing ops like createArticle / deleteMedia; rest read-only; all with empty audits/publishes/consumes — none of the current use-case bodies have literal-string `bus.publish(...)` or `auditLog.record({type: "..."})` calls, so empty arrays are correct). Re-export from feature `index.ts`. Update each `bind-production.ts` to import `assertFeatureConformance` + `Manifest` and call the assertion at the tail with the appropriate symbol map. **Inventory:** - `blog` — useCases: `getArticles` (read), `createArticle` (mutate), `getArticleBySlug` (read) - `media` — useCases: `getMedia` (read), `listMedia` (read), `deleteMedia` (mutate) - `navigation` — useCases: `getHeader` (read) - `marketing-pages` — useCases: `getPageBySlug` (read), `getSiteSettings` (read) --- ## Tasks ### Task 1: Story 06 scaffold Create `docs/work/conformance-system-v1/06-feature-migrations/_story.md`: ```markdown --- id: 06-feature-migrations epic: conformance-system-v1 title: Migrate blog/media/navigation/marketing-pages to conformance pattern type: technical-story status: in-progress feature: depends-on: [05-generator-updates] blocks: [] --- ## Goal Every feature in the repo has a manifest + a self-asserting bindProduction. After this milestone, `feature-must-have-manifest` flips from WARN to ERROR. ## Why Three of the four enforcement layers already exist; the only thing keeping them from being fully effective is that 4 of 5 features still don't have manifests. This story closes that gap. ## In scope - `feature.manifest.ts` for blog / media / navigation / marketing-pages - Manifest re-export from each feature's `src/index.ts` - `bind-production.ts` update for each: imports + tail `assertFeatureConformance` call - Flip `feature-must-have-manifest` from `warn` to `error` in base.js ## Out of scope - Adding publishes/audits to existing use cases (declared empty for now — the existing `auth.signUp` `bus.publish(userSignedUpEvent, ...)` uses a non-literal event name so the rule doesn't fire; other features have no publish/record calls) - Cross-feature event wiring (blog→marketing welcome flows, etc.) - Migrating realtime channels or jobs into manifests ## Tasks - [ ] Story scaffold - [ ] blog manifest + binding + re-export - [ ] media manifest + binding + re-export - [ ] navigation manifest + binding + re-export - [ ] marketing-pages manifest + binding + re-export - [ ] Flip feature-must-have-manifest to error - [ ] Final verification + closeout ``` Commit: `docs(work): story 06 — feature migrations`. ### Task 2: Migrate blog Create `packages/blog/src/feature.manifest.ts`: ```ts import { defineFeature } from "@repo/core-shared/conformance"; /** * The blog feature's conformance manifest. */ export const blogManifest = defineFeature({ name: "blog", requiredCores: [], useCases: { getArticles: { mutates: false, audits: [], publishes: [], consumes: [], }, getArticleBySlug: { mutates: false, audits: [], publishes: [], consumes: [], }, createArticle: { mutates: true, audits: [], publishes: [], consumes: [], }, }, realtimeChannels: [], jobs: [], } as const); export type BlogManifest = typeof blogManifest; ``` Re-export from `packages/blog/src/index.ts` — append at the bottom: ```ts export { blogManifest, type BlogManifest } from "./feature.manifest"; ``` Update `packages/blog/src/di/bind-production.ts`. Read the file. Add these two imports near the existing imports: ```ts import { assertFeatureConformance } from "@repo/core-shared/conformance"; import { blogManifest } from "../feature.manifest"; ``` At the end of the `bindProductionBlog(ctx)` function body — after the existing `// ` anchors, before the closing `}` — add: ```ts // Boot-time conformance check. assertFeatureConformance( blogContainer, blogManifest, { getArticles: BLOG_SYMBOLS.IGetArticlesUseCase, getArticleBySlug: BLOG_SYMBOLS.IGetArticleBySlugUseCase, createArticle: BLOG_SYMBOLS.ICreateArticleUseCase, }, ctx, ); ``` (The container variable name might be `blogContainer` — verify by reading the existing file; if it's named differently, use the actual name.) Verify: ``` pnpm --filter @repo/blog typecheck pnpm --filter @repo/blog test ``` Commit: ```bash git add packages/blog/src/feature.manifest.ts packages/blog/src/index.ts packages/blog/src/di/bind-production.ts git commit -m "feat(blog): conformance manifest + self-asserting bind-production" ``` ### Task 3: Migrate media Same shape. Create `packages/media/src/feature.manifest.ts`: ```ts import { defineFeature } from "@repo/core-shared/conformance"; export const mediaManifest = defineFeature({ name: "media", requiredCores: [], useCases: { getMedia: { mutates: false, audits: [], publishes: [], consumes: [] }, listMedia: { mutates: false, audits: [], publishes: [], consumes: [] }, deleteMedia: { mutates: true, audits: [], publishes: [], consumes: [] }, }, realtimeChannels: [], jobs: [], } as const); export type MediaManifest = typeof mediaManifest; ``` Append to `packages/media/src/index.ts`: ```ts export { mediaManifest, type MediaManifest } from "./feature.manifest"; ``` Update `packages/media/src/di/bind-production.ts` — add imports and tail call: ```ts import { assertFeatureConformance } from "@repo/core-shared/conformance"; import { mediaManifest } from "../feature.manifest"; ``` End of function body: ```ts assertFeatureConformance( mediaContainer, mediaManifest, { getMedia: MEDIA_SYMBOLS.IGetMediaUseCase, listMedia: MEDIA_SYMBOLS.IListMediaUseCase, deleteMedia: MEDIA_SYMBOLS.IDeleteMediaUseCase, }, ctx, ); ``` Verify + commit: ```bash pnpm --filter @repo/media typecheck && pnpm --filter @repo/media test git add packages/media/src/feature.manifest.ts packages/media/src/index.ts packages/media/src/di/bind-production.ts git commit -m "feat(media): conformance manifest + self-asserting bind-production" ``` ### Task 4: Migrate navigation `packages/navigation/src/feature.manifest.ts`: ```ts import { defineFeature } from "@repo/core-shared/conformance"; export const navigationManifest = defineFeature({ name: "navigation", requiredCores: [], useCases: { getHeader: { mutates: false, audits: [], publishes: [], consumes: [] }, }, realtimeChannels: [], jobs: [], } as const); export type NavigationManifest = typeof navigationManifest; ``` Append to `packages/navigation/src/index.ts`: ```ts export { navigationManifest, type NavigationManifest } from "./feature.manifest"; ``` Update `packages/navigation/src/di/bind-production.ts`. Imports: ```ts import { assertFeatureConformance } from "@repo/core-shared/conformance"; import { navigationManifest } from "../feature.manifest"; ``` Tail of function: ```ts assertFeatureConformance( navigationContainer, navigationManifest, { getHeader: NAVIGATION_SYMBOLS.IGetHeaderUseCase }, ctx, ); ``` Verify + commit. ### Task 5: Migrate marketing-pages `packages/marketing-pages/src/feature.manifest.ts`: ```ts import { defineFeature } from "@repo/core-shared/conformance"; export const marketingPagesManifest = defineFeature({ name: "marketing-pages", requiredCores: [], useCases: { getPageBySlug: { mutates: false, audits: [], publishes: [], consumes: [] }, getSiteSettings: { mutates: false, audits: [], publishes: [], consumes: [] }, }, realtimeChannels: [], jobs: [], } as const); export type MarketingPagesManifest = typeof marketingPagesManifest; ``` Append to `packages/marketing-pages/src/index.ts`: ```ts export { marketingPagesManifest, type MarketingPagesManifest } from "./feature.manifest"; ``` Update `packages/marketing-pages/src/di/bind-production.ts`: ```ts import { assertFeatureConformance } from "@repo/core-shared/conformance"; import { marketingPagesManifest } from "../feature.manifest"; ``` Tail: ```ts assertFeatureConformance( marketingPagesContainer, marketingPagesManifest, { getPageBySlug: MARKETING_PAGES_SYMBOLS.IGetPageBySlugUseCase, getSiteSettings: MARKETING_PAGES_SYMBOLS.IGetSiteSettingsUseCase, }, ctx, ); ``` Note: `MARKETING_PAGES_SYMBOLS` might be a different name in this package — verify by reading the existing `symbols.ts` first and use the actual exported constant name. Verify + commit. ### Task 6: Flip `feature-must-have-manifest` from WARN to ERROR Now that all features have manifests, the rule should hard-fail. Modify `packages/core-eslint/base.js`. Find: ```js "conformance/feature-must-have-manifest": [ "warn", { repoRoot }, ], ``` Change `"warn"` to `"error"`. Other rule severities stay as they are. Run `pnpm lint` to verify still passes (now 0 warnings from feature-must-have-manifest because all features have manifests). Commit: ```bash git add packages/core-eslint/base.js git commit -m "feat(core-eslint): flip feature-must-have-manifest from warn to error" ``` ### Task 7: Final verification + closeout ``` pnpm typecheck pnpm test pnpm lint pnpm conformance pnpm turbo boundaries ``` All pass. Update `docs/work/conformance-system-v1/06-feature-migrations/_story.md`: - frontmatter `status: in-progress` → `done` - All `- [ ]` → `- [x]` Update `docs/work/conformance-system-v1/_epic.md`. Find: ```markdown - [ ] 07 — Migrate auth feature reference (later plan) ``` Replace with: ```markdown - [x] [06 — Migrate blog / media / navigation / marketing-pages](06-feature-migrations/_story.md) - [x] 07 — Migrate auth feature reference (completed inline in milestone i; signIn through ProductionUseCase slot) ``` Also: since this completes the conformance-system-v1 epic's planned stories, flip the epic frontmatter `status: in-progress` → `status: done`. Commit: `docs(work): close story 06 + conformance-system-v1 epic`.