Files
agentic-dev-template/docs/architecture/vertical-feature-spec.md
Danijel Martinek 2c2375920f docs: reflect tooling-package rename + Turbo boundaries enforcement
- Rename eslint-config → core-eslint, typescript-config → core-typescript
  in all docs (package map, AGENTS.md, overview.md, dependency-flow.md, etc.)
- Document the five-tag model (app, feature, core, core-composition,
  tooling) — refinement of ADR-006's three-tag mention
- Document core-trpc's core-composition tag (transitively reaches features
  through core-api's AppRouter type)
- Note Turborepo boundaries as a second enforcement layer alongside ESLint
- Add ADR-010 explaining the two-layer enforcement decision and
  five-tag refinement

Files updated:
- docs/architecture/overview.md: package map, enforcement layers, five-tag section
- docs/architecture/dependency-flow.md: boundary rules, enforcement strategy
- docs/architecture/vertical-feature-spec.md: package names, five-tag model
- AGENTS.md: package map, boundary rules, commands
- CLAUDE.md: tooling package names, quick-start command
- packages/core-eslint/AGENTS.md: tag clarification
- packages/core-typescript/AGENTS.md: tag clarification
- packages/core-api/AGENTS.md: core-composition tag
- packages/core-cms/AGENTS.md: core-composition tag
- packages/core-trpc/AGENTS.md: core-composition tag + rationale
- docs/decisions/adr-010-turbo-boundaries.md: new ADR

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

27 KiB

Vertical Feature Architecture Spec

Source of truth. Copied from docs/superpowers/specs/2026-04-21-vertical-monorepo-refactor-design.md for in-tree reference. Edits here should be backported to the design spec.


Vertical Feature Monorepo Refactor — Design Spec

Date: 2026-04-21 Status: Approved for implementation planning Supersedes (partially): 2026-04-06-clean-architecture-monorepo-template-design.md Source spec: monorepo-architecture-spec-detailed-v5.md (v1 + addenda v3/v4/v5)


1. Goal

Refactor the template from a horizontal Clean Architecture monorepo (single packages/core, single packages/api, etc.) into a vertical feature-package monorepo where business capabilities (auth, blog, media, marketing-pages, navigation) are the top-level organizing principle, supported by core-* foundation packages for non-business concerns.

The refactor preserves Clean Architecture layering inside each feature (the existing rigor) while reorganizing between packages by business capability.


2. Current state (summary)

  • packages/core — all domains (auth, content) share one Clean Architecture layout with InversifyJS DI container
  • packages/api — single tRPC aggregator + per-domain routers
  • packages/api-client — React Query hooks + ApiProvider + useTRPC
  • packages/cms-core — Payload config + all collections (Users, Articles, Media, SiteSettings global)
  • packages/cms-client — dual-mode Payload client wrapper; defined but unused
  • packages/ui — atomic-design component library
  • apps/web-next (empty shell), apps/web-tanstack (empty shell), apps/cms (just stabilized), apps/storybook
  • Mock repositories only — no Payload-backed infrastructure
  • 9 Vitest unit tests under packages/core/tests/unit/
  • Extensive per-directory AGENTS.md; 5 ADRs; 2 guides; 6 dated superpower plans

3. Target state (summary)

  • Five core-* packages: core-shared, core-cms, core-api, core-trpc, core-ui
  • Five feature packages: auth, blog, media, marketing-pages, navigation
  • Two tooling packages renamed: eslint-configcore-eslint, typescript-configcore-typescript
  • Three apps unchanged in name: apps/web-next, apps/web-tanstack, apps/cms
  • Packages deleted: packages/core, packages/api, packages/api-client, packages/cms-core, packages/cms-client, packages/ui
  • Per-feature InversifyJS containers (no shared container)
  • Clean Architecture controllers retained inside each feature (interface-adapters/controllers/)
  • Spec's adapters/ renamed to integrations/ to avoid collision with interface-adapters/
  • Boundary enforcement via eslint-plugin-boundaries + package.json deps + exports maps + Turborepo tags
  • Playwright e2e set up from day one in web-next and web-tanstack

4. Decision log

All decisions captured from the brainstorming conversation:

# Decision Rationale
1 Big-bang migration (not incremental) Template has empty reference apps and no external consumers; incremental dual-maintenance is overhead without benefit
2 Feature scope: auth + blog + media + marketing-pages + navigation (all real, none as empty skeletons) Template value is in worked examples; reference app needs something to render; spec §15A.2 forbids empty folders
3 Keep InversifyJS User wants to preserve existing DI pattern rather than move to plain function injection
4 Per-feature DI containers (not a shared container) Each feature owns its own Container, symbols, getInjection(). Perfect vertical ownership; no composition package needed for DI; tests unbind/rebind their own container
5 media is a feature package, not core-media Application can live without media; it's a business capability per spec §3. Site-wide concerns (SiteSettings) fold into marketing-pages, not a separate site package
6 Keep all three apps (web-next, web-tanstack, cms) with existing names web-tanstack proves features are framework-agnostic; no rename avoids churn
7 Keep interface-adapters/controllers/ layer Transport-agnostic controllers enable CLI/cron reuse; template should demonstrate growth room for presenters/gateways
8 Rename spec's adapters/integrations/ Avoids collision with Clean Architecture's interface-adapters/; captures spec's "role not implementation" intent equally well; bounded deviation from spec
9 Delete existing tests, rewrite fresh; rewrite AGENTS.md, add ADRs, mark old ADRs superseded where relevant DI pattern change + file layout change make porting more work than rewriting; ADRs preserve architectural history
10 Include eslint-plugin-boundaries from day one Spec §13A.7 explicitly requires three-layer enforcement; package.json deps + exports + lint rules
11 Playwright set up immediately in web-next and web-tanstack Not deferred; demonstrates framework portability end-to-end
12 Keep articles collection/entity naming (not rename to posts) Simpler migration; collapses CMS-doc vs domain distinction which is fine for a template

Deviations from source spec (document explicitly as new ADRs):

  • Keep InversifyJS (spec examples use plain function injection)
  • Keep controller layer between tRPC and use-case (spec goes tRPC→use-case direct)
  • Rename spec's adapters/ to integrations/
  • Omit core-payload-client wrapper (aligned with spec §10)

5. Target package layout

repo/
  apps/
    web-next/                              # Next.js 15 App Router (port 3000)
      app/
        layout.tsx                         # <TrpcProvider> from @repo/core-trpc/next
        trpc/[trpc]/route.ts               # tRPC fetch adapter → @repo/core-api appRouter
        blog/[slug]/page.tsx
        about/page.tsx
        page.tsx                           # home — navigation + marketing-pages
      e2e/
        playwright.config.ts
        blog-post.spec.ts
        marketing-page.spec.ts
        home-nav.spec.ts
      package.json next.config.mjs tsconfig.json turbo.json

    web-tanstack/                          # TanStack Start (port 3002)
      ...                                  # parallel tRPC wiring; own providers from @repo/core-trpc/tanstack
      e2e/
        playwright.config.ts
        blog-post.spec.ts

    cms/                                   # Payload admin host (port 3001)
      app/(payload)/                       # unchanged from current stabilized state
      package.json next.config.mjs tsconfig.json turbo.json

    storybook/                             # unchanged; updates imports from @repo/ui → @repo/core-ui

  packages/
    # ─── CORE (foundation, tagged "core") ───
    core-shared/                           # generic primitives (no business knowledge)
    core-cms/                              # Payload composition only (aggregates feature cms exports)
    core-api/                              # tRPC composition only (aggregates feature api exports)
    core-trpc/                             # frontend tRPC platform (client, providers per framework)
    core-ui/                               # design-system primitives (atoms/molecules/templates)

    # ─── FEATURES (business capabilities, tagged "feature") ───
    auth/                                  # Users collection + sign-in/up/out
    blog/                                  # Articles collection + article use-cases
    media/                                 # Media collection + upload helpers
    marketing-pages/                       # pages collection + SiteSettings global
    navigation/                            # header global

    # ─── TOOLING (tagged "tooling") ───
    core-eslint/
    core-typescript/

  docs/
    architecture/
      overview.md                          # rewritten
      dependency-flow.md                   # rewritten
      vertical-feature-spec.md             # copy of source spec
    decisions/
      adr-001 … adr-009                    # five existing + four new (see §10)
    guides/
      adding-a-feature.md                  # rewritten
      testing-strategy.md                  # rewritten
    superpowers/specs/                     # this file + implementation plan

  CLAUDE.md AGENTS.md docker-compose.yml package.json pnpm-lock.yaml
  pnpm-workspace.yaml tsconfig.base.json turbo.json

Package count: 3 apps + 5 core + 5 feature + 2 tooling = 15 packages (was 12).


6. Feature package internal shape

Canonical mature shape (e.g., packages/blog/):

packages/blog/
  src/
    entities/
      article.ts                           # Zod schema + Article type
      article.test.ts
      errors.ts

    application/
      repositories/
        articles-repository.interface.ts   # IArticlesRepository
      use-cases/
        get-article.use-case.ts
        get-article.use-case.test.ts
        create-article.use-case.ts

    infrastructure/
      repositories/
        payload-articles.repository.ts     # @injectable, calls getPayload({ config }) from core-cms
        mock-articles.repository.ts        # @injectable, for tests
        payload-articles.repository.test.ts

    interface-adapters/                    # Clean Arch grouping (controllers now; presenters/gateways later)
      controllers/
        articles.controller.ts             # Zod safeParse → InputParseError → use case
        articles.controller.test.ts

    di/                                    # feature-local InversifyJS container
      symbols.ts                           # BLOG_SYMBOLS
      module.ts                            # ContainerModule
      container.ts                         # blogContainer + getInjection<T>()
      container.test.ts

    integrations/                          # renamed from spec's adapters/
      cms/
        collections/
          articles.ts                      # Payload CollectionConfig
        hooks/
          after-post-change.ts             # Payload lifecycle adapter → calls effects
        index.ts                           # exports: articles (for core-cms composition)
      api/
        router.ts                          # tRPC procedures → controllers
        router.test.ts

    effects/                               # only when needed
      revalidate-post.ts
      sync-post-search.ts

    jobs/                                  # only when needed
      publish-scheduled-posts.ts

    events/                                # only when needed
      post-updated.ts

    ui/
      query.ts                             # trpc.blog.articleBySlug.queryOptions(...)
      query.test.ts
      article-client.tsx
      article-client.test.tsx
      page.tsx

    index.ts                               # re-exports ui components + public types

  tests/
    article-by-slug.feature.test.ts        # cross-layer feature test

  package.json                             # exports: ".", "./cms", "./api"
  tsconfig.json
  turbo.json                               # tags: ["feature"]

Small-feature variant (e.g., packages/navigation/) omits folders without meaningful code per spec §15 / addendum v5 ("create folders only when needed"):

packages/navigation/
  src/
    entities/ nav.ts
    infrastructure/repositories/ payload-navigation.repository.ts
    di/ symbols.ts module.ts container.ts
    interface-adapters/controllers/ navigation.controller.ts
    integrations/
      cms/ globals/ header.ts + index.ts
      api/ router.ts
    ui/ query.ts
    index.ts

No application/use-cases/, effects/, jobs/, events/ unless the feature grows them.

Request flow:

useQuery(articleQuery(slug))              ui/query.ts (typed tRPC client)
  ↓
tRPC router.articleBySlug                 integrations/api/router.ts
  ↓  .input(zod).query(...)
articlesController.getBySlug(input)       interface-adapters/controllers/
  ↓  safeParse → delegate
getArticleUseCase(slug)                   application/use-cases/
  ↓  getInjection(BLOG_SYMBOLS.IArticlesRepository)
PayloadArticlesRepository.getBySlug       infrastructure/repositories/ (@injectable)
  ↓  getPayload({ config }) from @repo/core-cms
Payload Local API → PostgreSQL

DI placement rationale: di/ sits at feature root (not under infrastructure/) because the container wires application/ interfaces to infrastructure/ implementations — it has knowledge of both layers and is a sibling to them, not a sub-layer.


7. Core package responsibilities

core-shared/

Generic reusable primitives. Zero business knowledge.

src/
  lib/
    env.ts date.ts
  payload/
    access/    is-admin.ts
    fields/    slug-field.ts seo-fields.ts
    blocks/    cta.ts
    hooks/     set-published-at.ts slugify-if-missing.ts
    index.ts
  trpc/
    init.ts    # initTRPC.create + router/publicProcedure
    context.ts # createTrpcContext + TrpcContext type
  index.ts

Exports: ., ./payload, ./trpc/init, ./trpc/context.

Forbidden: importing any feature package.

core-cms/

Payload composition only.

src/
  payload.config.ts    # imports feature /cms exports, composes buildConfig
  generated-types.ts   # Payload type generator output
  index.ts             # re-exports config as default

Exports: ., ./generated-types.

Allowed exception: may import @repo/<feature>/cms subpath exports only.

core-api/

tRPC composition only.

src/
  root.ts              # aggregates feature routers → appRouter
  index.ts             # re-exports appRouter + AppRouter type

Allowed exception: may import @repo/<feature>/api subpath exports only.

core-trpc/

Frontend tRPC platform with framework-specific provider shims.

src/
  client.ts                     # createTRPCReact<AppRouter>()
  query-client.ts               # makeQueryClient()
  providers/
    next-provider.tsx           # 'use client' — Next.js App Router pattern
    tanstack-provider.tsx       # TanStack Start pattern
  index.ts

Exports: ., ./next, ./tanstack.

Forbidden: importing any feature package.

core-ui/

Design-system primitives only.

src/
  atoms/       button/ input/ label/
  molecules/   form-field/
  organisms/   (generic only — e.g., modal, tabs, navigation-menu)
  templates/   auth-layout/ dashboard-layout/
  lib/         utils.ts
  index.ts

Generic organisms (Modal, Tabs, NavigationMenu, Command) live here. Feature-specific organisms (e.g., ArticleCard, PricingSection, HeaderNavMenu) live in the owning feature's ui/. Spec §6.5 boundary.

Forbidden: importing any feature package.


8. Payload collection & global ownership

Current → New location Slug / type Notes
cms-core/src/collections/users/ packages/auth/src/integrations/cms/collections/users.ts users Authenticated collection
cms-core/src/collections/articles/ packages/blog/src/integrations/cms/collections/articles.ts articles Name preserved per decision #12
cms-core/src/collections/media/ packages/media/src/integrations/cms/collections/media.ts media Upload collection
cms-core/src/globals/site-settings/ packages/marketing-pages/src/integrations/cms/globals/site-settings.ts site-settings Site-wide metadata
(new) packages/marketing-pages/src/integrations/cms/collections/pages.ts pages
(new) packages/navigation/src/integrations/cms/globals/header.ts header

Composition in core-cms/src/payload.config.ts:

import { buildConfig } from 'payload'
import { users } from '@repo/auth/cms'
import { articles } from '@repo/blog/cms'
import { pages, siteSettings } from '@repo/marketing-pages/cms'
import { header } from '@repo/navigation/cms'
import { media } from '@repo/media/cms'

export default buildConfig({
  collections: [users, articles, pages, media],
  globals: [header, siteSettings],
  typescript: {
    outputFile: new URL('./generated-types.ts', import.meta.url).pathname,
    declare: false,
  },
  // db, admin config unchanged from current cms-core
})

Hook routing policy:

  • Generic hooks (e.g., slugify-if-missing, set-published-at) live in core-shared/src/payload/hooks/ and are imported by any collection that needs them.
  • Business-specific hooks (e.g., "revalidate blog post page when published") live in the feature's integrations/cms/hooks/, which call the feature's effects/ for reusable side effects.

9. Boundaries + enforcement

9.1 Five tags

Package-level turbo.json tags (refined from earlier ADR-006's three-tag model):

Tag Packages
app apps/web-next, apps/web-tanstack, apps/cms, apps/storybook
core-composition packages/core-api, core-cms, core-trpc
core packages/core-shared, core-ui
feature packages/auth, blog, media, marketing-pages, navigation
tooling packages/core-eslint, core-typescript

Note: core-trpc is core-composition (not plain core) because it transitively reaches features through core-api's AppRouter type.

9.2 Allowed dependency directions

Tag May depend on
app app, core, core-composition, feature, tooling
core-composition core, core-composition, feature, tooling
core core, core-composition, tooling
feature core, tooling
tooling tooling

9.3 Composition exceptions

  • core-cms may import @repo/<feature>/cms subpath exports only.
  • core-api may import @repo/<feature>/api subpath exports only.
  • No other package may deviate from the five-tag rules.

9.4 Four enforcement layers

  1. package.json dependencies — only allowed deps declared.
  2. exports maps — feature packages expose ., ./cms, ./api only (no deep source paths).
  3. ESLint eslint-plugin-boundaries (lint-time) — configured in packages/core-eslint/ flat config:
    • Enforces the five-tag rules (same rules as Turbo boundaries)
    • File-specific exemptions via // @boundaries-ignore comments
  4. Turborepo boundaries (build-graph time) — configured in root turbo.json:
    • Validates the entire workspace dependency graph, including transitive dependencies
    • Catches issues that lint-time checking misses (e.g., transitive feature reaches)
    • Run pnpm turbo boundaries to validate

9.5 Root turbo.json (unchanged concept)

{
  "tasks": {
    "build":     { "dependsOn": ["^build"], "outputs": [".next/**", "dist/**"] },
    "lint":      { "dependsOn": ["^lint"] },
    "typecheck": { "dependsOn": ["^typecheck"] },
    "test":      { "dependsOn": ["^build"] },
    "test:e2e":  { "dependsOn": ["^build"], "cache": false }
  }
}

Tags govern architectural boundaries; dependsOn: ["^build"] governs task execution order — separate concerns per spec §13A.6.


10. Test placement + tooling

10.1 Placement

Scope Location Suffix
Entity / value-object colocated *.test.ts
Use-case (with fake repo) colocated *.test.ts
Controller (Zod validation) colocated *.test.ts
Infrastructure repository colocated *.test.ts
DI container bindings colocated *.test.ts
React component colocated *.test.tsx
Query helper colocated *.test.ts
Core-shared primitive colocated in core-shared *.test.ts
Feature-level cross-layer packages/<feature>/tests/ *.feature.test.ts
Browser e2e apps/<app>/e2e/ *.spec.ts

10.2 Vitest

  • Each package has its own vitest.config.ts.
  • Shared base in packages/core-typescript/vitest.base.ts; packages extend it and pick environment: 'jsdom' | 'node' per need.
  • Turbo test task runs vitest run per package.

10.3 DI in tests (per-feature container)

Each feature's tests import the feature's own container and rebind per test:

import { blogContainer, BLOG_SYMBOLS } from '../../di/container'
import { MockArticlesRepository } from '../../infrastructure/repositories/mock-articles.repository'

beforeEach(() => {
  blogContainer.unbindAll()
  blogContainer.bind(BLOG_SYMBOLS.IArticlesRepository).to(MockArticlesRepository)
})

No shared initializeContainer() / destroyContainer().

10.4 Starter test coverage (end of refactor)

  • core-shared: 3 tests (slug-field, set-published-at, is-admin)
  • auth: 6 tests (3 controllers + 3 use-cases) — replaces current auth unit tests
  • blog: 3 tests (2 use-cases + 1 feature test for articleBySlug)
  • marketing-pages, navigation, media: minimum one feature test each

Total ~15 unit/integration tests + 4 e2e = replaces current 9 tests with full new layout.

10.5 Playwright (included from day one)

  • apps/web-next/e2e/:
    • playwright.config.ts — starts dev server on port 3000 via webServer, chromium only initially
    • blog-post.spec.ts, marketing-page.spec.ts, home-nav.spec.ts
  • apps/web-tanstack/e2e/:
    • Parallel config (port 3002)
    • blog-post.spec.ts (validates framework-agnostic feature claim)
  • apps/cms/ — no e2e (Payload has its own admin tests)
  • Root script: pnpm test:e2e via Turbo
  • ESLint config adds eslint-plugin-playwright for e2e folders
  • Playwright's globalSetup verifies Postgres is running; fails fast with a helpful message otherwise

11. Docs + ADR strategy

11.1 Existing ADRs

File Action Notes
adr-001-monorepo-tool.md Keep unchanged Turborepo + pnpm still accurate
adr-002-di-framework.md Keep; append note InversifyJS kept, but now per-feature containers
adr-003-cms-separation.md Mark v1 superseded, write v2 New architecture splits cms-core into core-cms + feature-owned collections
adr-004-dual-mode-client.md Mark superseded cms-client deleted per spec §10
adr-005-atomic-design.md Keep; append scope note Applies to core-ui/ only

11.2 New ADRs

  • adr-006-vertical-feature-packages.md — the main architectural pivot; references source spec
  • adr-007-drop-cms-client-wrapper.md — rationale for removing packages/cms-client
  • adr-008-per-feature-di-containers.md — why each feature owns its InversifyJS container
  • adr-009-integrations-folder-naming.md — why spec's adapters/ is renamed integrations/

11.3 Rewritten docs

  • docs/architecture/overview.md — new diagram, new flow, vertical package organization
  • docs/architecture/dependency-flow.md — new graph, three-tag boundary model
  • docs/architecture/vertical-feature-spec.md — copy of source spec for offline reference
  • docs/guides/adding-a-feature.md — new recipe (small feature + mature feature, per addendum v5)
  • docs/guides/testing-strategy.md — new placement table

11.4 Deleted

  • docs/superpowers/plans/* — 6 stale plan files from previous architecture

11.5 AGENTS.md

All rewritten:

  • Root AGENTS.md — new package map, new data flow, new rules, new boundary model
  • Per-core-package: responsibilities + forbidden imports (~60 lines each)
  • Per-feature-package: layer rules, addendum v5 folder-creation checklist, test placement
  • Per-layer inside features: local import rules, test patterns (short)
  • Per-app: purpose, imports, port, dev commands, e2e commands

Root CLAUDE.md — updated "Read First" pointers, unchanged port table, added boundary-enforcement note.


12. Migration sequencing

Big-bang refactor executed as 11 internal phases; each phase ends with a verification gate (pnpm typecheck && pnpm test) before proceeding. Commit per phase (or per feature within Phase 5) so git bisect works.

# Phase Key artifact Gate
1 Scaffold core packages (empty shells) packages/core-shared/, core-cms/, core-api/, core-trpc/, core-ui/ with stubs pnpm install + pnpm typecheck green
2 Populate core-shared Fields, blocks, access helpers, hooks, tRPC init/context pnpm test --filter @repo/core-shared passes
3 Populate core-cms stub and repoint apps/cms payload.config.ts lifted from cms-core to core-cms; collections: [], globals: [] initially; apps/cms updates its import from @repo/cms-core to @repo/core-cms pnpm dev --filter @repo/cms boots admin UI; pnpm generate:types succeeds
4 Migrate blog feature end-to-end (first vertical — proves pattern) Full canonical shape; Articles collection; per-feature DI container; tRPC router; UI pnpm typecheck && pnpm test --filter @repo/blog green
5 Migrate remaining features: auth, marketing-pages, navigation, media Each follows the blog template Per-feature typecheck + tests + core-cms regenerates
6 Populate core-trpc + wire apps Client, per-framework providers; route handlers in web-next + web-tanstack; example pages pnpm dev serves pages; tRPC returns Payload data
7 Migrate core-ui Move packages/ui/ contents; relocate feature-shaped organisms into features; update Storybook imports Storybook builds; pnpm test green
8 Delete old packages core/, api/, api-client/, cms-core/, cms-client/, ui/ pnpm install && pnpm typecheck && pnpm test green
9 Boundary enforcement Install eslint-plugin-boundaries; add package-level turbo.json tags; write lint rules pnpm lint zero violations
10 Playwright setup Configs, initial specs in both frontends; root test:e2e script pnpm test:e2e green
11 Docs rewrite Copy spec; rewrite overview, dependency-flow, guides; new ADRs; all AGENTS.md; delete stale plans Human review

Commit strategy: one commit per phase. Phase 5 may be multiple commits (one per feature). Every commit builds + tests green.


13. Out of scope (deferred)

  • Real Payload integration tests with a test database (stub with mock repos; write real integration tests later)
  • Coverage reporting aggregation across packages (initial vitest setup per-package; aggregation is a follow-up)
  • Multi-browser Playwright matrix (chromium only initially; add firefox/webkit later)
  • Queue workers / CLI scripts (no core-events package yet; spec addendum v4's optional core-events stays deferred until a feature actually needs an event bus)
  • Payload subscriptions / realtime (spec addendum v4); no feature requires it yet
  • CMS app Next.js 15.5 + Payload 3.81 stabilization concerns (recently patched; monitor; no specific action in this refactor)

14. Success criteria

  • pnpm install && pnpm typecheck && pnpm lint && pnpm test && pnpm build all green
  • pnpm test:e2e green against running dev servers
  • pnpm dev --filter @repo/web-next serves a home page with navigation + marketing content + a blog index, and /blog/[slug] shows an article — all fed by tRPC → feature controllers → use-cases → Payload Local API
  • pnpm dev --filter @repo/web-tanstack renders the same blog post using the same feature packages
  • Storybook builds showing core-ui primitives
  • Any deep import (e.g., import x from '@repo/blog/src/...') fails pnpm lint
  • Any cross-feature import fails pnpm lint
  • Root AGENTS.md and one-per-package AGENTS.md reflect the new architecture
  • 9 new ADRs (5 existing maintained/appended/superseded + 4 new)
  • Zero references to deleted packages anywhere in the codebase

15. Post-approval next step

Invoke the superpowers:writing-plans skill to produce a detailed, executable implementation plan based on the 11-phase sequencing in §12. Each phase becomes a plan section with concrete file-by-file steps, verification commands, and commit-message templates.