refactor(marketing-pages)!: delete marketing-pages demo feature

Veect retrofit (ADR-027): fourth slice of the demo-content removal.
Deletes packages/marketing-pages whole and prunes every composition
edge in one commit: core-api router mount + dep, core-cms
collection/global composition + dep + regenerated Payload types,
web-next bindAll (prod + dev-seed) + tests + about page + Tailwind
source + transpilePackages + dep, cms/core-cms payload config test
assertions, marketing-page e2e spec, tsconfig paths, fallow ignore
entry, anchor-guard + generator e2e feature lists, compliance
data-map + retention-policy regeneration, lockfile prune, and
feature-list doc entries (CLAUDE.md, AGENTS.md, glossary, app/feature
AGENTS.md).

Event teardown: marketing-pages was the sole consumer of
auth.user.signed-up (welcome-email handler + job). The handler, its
Payload tasks, and the bus subscription all lived inside the package's
own binders, so they die with it — no other package wires the
subscription. Auth's manifest `publishes` stays untouched: a publisher
with zero consumers is legal (pnpm conformance only fails on orphan
consumers, verified green). The app-level sign-up-welcome-email test
asserted the marketing-pages mailer stays empty without a bus; it is
deleted with the feature, and the now-unused test-only bind-state
helpers in web-next bind-production.ts go with it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016j8z4VHjedXDTjEDNg7qHK
This commit is contained in:
2026-07-12 16:53:44 +02:00
parent 7dd3e7ceac
commit 9ee941863d
108 changed files with 35 additions and 3205 deletions

View File

@@ -5,12 +5,12 @@ describe("CMS app payload.config", () => {
it("registers all feature collections", async () => {
const resolved = await config;
const slugs = resolved.collections?.map((c) => c.slug) ?? [];
expect(slugs).toEqual(expect.arrayContaining(["users", "pages"]));
expect(slugs).toEqual(expect.arrayContaining(["users"]));
});
it("registers all feature globals", async () => {
const resolved = await config;
const slugs = resolved.globals?.map((g) => g.slug) ?? [];
expect(slugs).toEqual(expect.arrayContaining(["site-settings", "header"]));
expect(slugs).toEqual(expect.arrayContaining(["header"]));
});
});

File diff suppressed because one or more lines are too long

View File

@@ -26,7 +26,7 @@ pnpm dev --filter @repo/web-next # Next.js on port 3000
| ----------------------- | ---------------------------------------------------------------------------------------- |
| `src/app/layout.tsx` | Root layout — wraps app with `<Providers>` |
| `src/app/providers.tsx` | Client component wrapper (add tRPC/React Query here after scaffolding `@repo/core-trpc`) |
| `src/app/page.tsx` | Home page — navigation + marketing content |
| `src/app/page.tsx` | Home page |
| `e2e/` | Playwright end-to-end tests |
## tRPC Setup (optional)
@@ -104,7 +104,7 @@ Run: `pnpm test:e2e` starts the dev server and runs all `.spec.ts` files.
## Cross-References
- **Feature packages:** `packages/{auth,marketing-pages,navigation}/`
- **Feature packages:** `packages/{auth,navigation}/`
- **tRPC composition:** `packages/core-api/AGENTS.md`
- **tRPC client + provider (optional):** scaffold `@repo/core-trpc` first, then see `turbo/generators/templates/core-package/trpc/AGENTS.md.hbs`
- **UI components (optional):** scaffold with `pnpm turbo gen core-package ui`, then see `turbo/generators/templates/core-package/ui/AGENTS.md.hbs`

View File

@@ -1,11 +1,9 @@
import { test, expect } from "@playwright/test";
test("home page renders site name + nav + article list", async ({ page }) => {
test("home page renders heading + nav", async ({ page }) => {
await page.goto("/");
// Page renders and shows site name
// Page renders and shows the heading
await expect(page.locator("h1").first()).toBeVisible();
// Site name from siteSettings (mock seed: "My App")
await expect(page.locator("body")).toContainText(/My App/i);
// Nav element is present on the page
const nav = page.locator("nav");
await expect(nav).toHaveCount(1);

View File

@@ -1,10 +0,0 @@
import { test, expect } from "@playwright/test";
test("/about renders the about marketing page", async ({ page }) => {
await page.goto("/about");
// Either renders the seeded page (h1 = "About us") or "not yet published" message
// — both are HTTP 200, so the test only checks it doesn't 500.
const status = (await page.context().request.get("/about")).status();
expect(status).toBe(200);
await expect(page.locator("body")).toBeVisible();
});

View File

@@ -12,7 +12,6 @@ const nextConfig = {
"@repo/core-dsr",
"@repo/core-shared",
"@repo/core-ui",
"@repo/marketing-pages",
"@repo/navigation",
"@repo/core-trpc",
],

View File

@@ -19,7 +19,6 @@
"@repo/core-cms": "workspace:*",
"@repo/core-shared": "workspace:*",
"@repo/core-trpc": "workspace:^",
"@repo/marketing-pages": "workspace:*",
"@repo/navigation": "workspace:*",
"@sentry/nextjs": "^10.51.0",
"@tailwindcss/postcss": "^4.3.0",

View File

@@ -1,50 +0,0 @@
// Cross-feature event bus proof-of-life (DISABLED — @repo/core-events removed).
//
// @repo/core-events is now optional. When absent, ctx.bus is undefined, and
// bus?.subscribe(...) / bus?.publish(...) calls are no-ops. Cross-feature event
// fanout does not occur until core-events is scaffolded via:
//
// pnpm turbo gen core-package events
//
// After scaffolding, restore this test and re-wire the bus in bind-production.ts
// (see the comment in bindAll()). Until then, signing up does NOT trigger a
// welcome email — the mailer queue stays empty.
//
// Replaced with a reduced test that asserts the no-bus behavior.
import "reflect-metadata";
import { describe, it, expect, beforeEach } from "vitest";
import { bindAllDevSeed, __resetBindStateForTests } from "@/server/bind-production";
import { authContainer } from "@repo/auth/di/container";
import { AUTH_SYMBOLS } from "@repo/auth/di/symbols";
import type { ISignUpController } from "@repo/auth";
import { marketingPagesContainer } from "@repo/marketing-pages/di/container";
import { MARKETING_PAGES_SYMBOLS } from "@repo/marketing-pages/di/symbols";
import { RecordingMailerService } from "@repo/marketing-pages/services/recording-mailer";
describe("e2e: sign-up with no event bus (core-events not scaffolded)", () => {
beforeEach(() => {
__resetBindStateForTests();
});
it("sign-up succeeds and mailer stays empty (no cross-feature fanout without bus)", async () => {
await bindAllDevSeed();
const mailer = marketingPagesContainer.get<RecordingMailerService>(
MARKETING_PAGES_SYMBOLS.IMailerService,
);
const signUp = authContainer.get<ISignUpController>(AUTH_SYMBOLS.ISignUpController);
await signUp({
username: "testuser",
password: "secret_password",
confirmPassword: "secret_password",
});
// Without a bus, bus?.subscribe() is a no-op so the event handler never
// fires and the mailer receives nothing.
await new Promise((r) => setImmediate(r));
expect(mailer.sent).toEqual([]);
});
});

View File

@@ -1,12 +0,0 @@
import { PageContent } from "@repo/marketing-pages/ui";
import { bindAll } from "../../server/bind-production";
export default async function AboutPage() {
await bindAll();
return (
<main className="px-6 py-8">
<PageContent slug="about" />
</main>
);
}

View File

@@ -7,16 +7,10 @@ vi.mock("payload", () => ({
vi.mock("@repo/auth/di/bind-production", () => ({
bindProductionAuth: vi.fn(),
}));
vi.mock("@repo/marketing-pages/di/bind-production", () => ({
bindProductionMarketingPages: vi.fn(),
}));
vi.mock("@repo/navigation/di/bind-production", () => ({
bindProductionNavigation: vi.fn(),
}));
vi.mock("@repo/auth/di/bind-dev-seed", () => ({ bindDevSeedAuth: vi.fn() }));
vi.mock("@repo/marketing-pages/di/bind-dev-seed", () => ({
bindDevSeedMarketingPages: vi.fn(),
}));
vi.mock("@repo/navigation/di/bind-dev-seed", () => ({
bindDevSeedNavigation: vi.fn(),
}));
@@ -39,19 +33,16 @@ describe("bindAllProduction", () => {
vi.clearAllMocks();
});
it("binds all three feature production repos", async () => {
it("binds both feature production repos", async () => {
const { bindAllProduction } = await import("./bind-production");
const { bindProductionAuth } =
await import("@repo/auth/di/bind-production");
const { bindProductionMarketingPages } =
await import("@repo/marketing-pages/di/bind-production");
const { bindProductionNavigation } =
await import("@repo/navigation/di/bind-production");
await bindAllProduction();
expect(bindProductionAuth).toHaveBeenCalledOnce();
expect(bindProductionMarketingPages).toHaveBeenCalledOnce();
expect(bindProductionNavigation).toHaveBeenCalledOnce();
});

View File

@@ -18,10 +18,8 @@ import {
} from "@repo/core-shared/jobs";
import { NoopRateLimit } from "@repo/core-shared/rate-limit";
import { bindProductionAuth } from "@repo/auth/di/bind-production";
import { bindProductionMarketingPages } from "@repo/marketing-pages/di/bind-production";
import { bindProductionNavigation } from "@repo/navigation/di/bind-production";
import { bindDevSeedAuth } from "@repo/auth/di/bind-dev-seed";
import { bindDevSeedMarketingPages } from "@repo/marketing-pages/di/bind-dev-seed";
import { bindDevSeedNavigation } from "@repo/navigation/di/bind-dev-seed";
let bindPromise: Promise<void> | null = null;
@@ -96,7 +94,6 @@ export async function bindAllProduction(): Promise<void> {
};
bindProductionAuth(ctx);
bindProductionMarketingPages(ctx);
bindProductionNavigation(ctx);
}
@@ -117,7 +114,6 @@ export async function bindAllDevSeed(): Promise<void> {
};
await bindDevSeedAuth(ctx);
await bindDevSeedMarketingPages(ctx);
await bindDevSeedNavigation(ctx);
}
@@ -153,19 +149,3 @@ export function bindAll(): Promise<void> {
return bindPromise;
}
/** Test-only resets — not exported via package. Used by bind-production.test.ts. */
export function __resetBindStateForTests(): void {
bindPromise = null;
resolvedTracer = null;
resolvedLogger = null;
resolvedQueue = null;
}
/** Test-only accessor for resolved instrumentation. */
export function __getInstrumentationForTests(): {
tracer: ITracer | null;
logger: ILogger | null;
} {
return { tracer: resolvedTracer, logger: resolvedLogger };
}

View File

@@ -1,7 +1,6 @@
@import "tailwindcss";
@source "../../../../packages/core-ui/src";
@source "../../../../packages/navigation/src";
@source "../../../../packages/marketing-pages/src";
@source "../../../../packages/auth/src";
@source "../";

File diff suppressed because one or more lines are too long