From 7d38ff3bb9b943922ce25f72f1ec6663d4cc65d0 Mon Sep 17 00:00:00 2001 From: Danijel Martinek Date: Wed, 13 May 2026 00:06:48 +0200 Subject: [PATCH] =?UTF-8?q?plan(conformance):=20milestone=20vi=20=E2=80=94?= =?UTF-8?q?=20feature=20migrations?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...ormance-milestone-vi-feature-migrations.md | 349 ++++++++++++++++++ 1 file changed, 349 insertions(+) create mode 100644 docs/superpowers/plans/2026-05-13-conformance-milestone-vi-feature-migrations.md diff --git a/docs/superpowers/plans/2026-05-13-conformance-milestone-vi-feature-migrations.md b/docs/superpowers/plans/2026-05-13-conformance-milestone-vi-feature-migrations.md new file mode 100644 index 0000000..c8dc8b0 --- /dev/null +++ b/docs/superpowers/plans/2026-05-13-conformance-milestone-vi-feature-migrations.md @@ -0,0 +1,349 @@ +# 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`.