Files
agentic-dev/docs/superpowers/plans/2026-05-13-conformance-milestone-vi-feature-migrations.md

10 KiB

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 + <name>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:

---
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: <cross-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:

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:

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:

import { assertFeatureConformance } from "@repo/core-shared/conformance";
import { blogManifest } from "../feature.manifest";

At the end of the bindProductionBlog(ctx) function body — after the existing // <gen:*> anchors, before the closing } — add:

  // 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:

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:

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:

export { mediaManifest, type MediaManifest } from "./feature.manifest";

Update packages/media/src/di/bind-production.ts — add imports and tail call:

import { assertFeatureConformance } from "@repo/core-shared/conformance";
import { mediaManifest } from "../feature.manifest";

End of function body:

  assertFeatureConformance(
    mediaContainer,
    mediaManifest,
    {
      getMedia: MEDIA_SYMBOLS.IGetMediaUseCase,
      listMedia: MEDIA_SYMBOLS.IListMediaUseCase,
      deleteMedia: MEDIA_SYMBOLS.IDeleteMediaUseCase,
    },
    ctx,
  );

Verify + commit:

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:

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:

export { navigationManifest, type NavigationManifest } from "./feature.manifest";

Update packages/navigation/src/di/bind-production.ts. Imports:

import { assertFeatureConformance } from "@repo/core-shared/conformance";
import { navigationManifest } from "../feature.manifest";

Tail of function:

  assertFeatureConformance(
    navigationContainer,
    navigationManifest,
    { getHeader: NAVIGATION_SYMBOLS.IGetHeaderUseCase },
    ctx,
  );

Verify + commit.

Task 5: Migrate marketing-pages

packages/marketing-pages/src/feature.manifest.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:

export { marketingPagesManifest, type MarketingPagesManifest } from "./feature.manifest";

Update packages/marketing-pages/src/di/bind-production.ts:

import { assertFeatureConformance } from "@repo/core-shared/conformance";
import { marketingPagesManifest } from "../feature.manifest";

Tail:

  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:

"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:

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-progressdone
  • All - [ ]- [x]

Update docs/work/conformance-system-v1/_epic.md. Find:

- [ ] 07 — Migrate auth feature reference (later plan)

Replace with:

- [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-progressstatus: done.

Commit: docs(work): close story 06 + conformance-system-v1 epic.