refactor: strip Phase/Plan/R-number references from source comments

This commit is contained in:
2026-05-13 09:51:45 +02:00
parent 075b729266
commit 17ae157365
66 changed files with 980 additions and 647 deletions

View File

@@ -22,66 +22,65 @@ export const CONTRACT_HEADER_SEED: Header = {
* Header is a singleton (Payload Global). The interface exposes only
* getHeader(). The contract verifies the shape, count, and order of items.
*/
export const headerRepositoryContract =
defineContractSuite<IHeaderRepository>(
"IHeaderRepository",
({ buildSubject, getTracer }) => {
let repo: IHeaderRepository;
export const headerRepositoryContract = defineContractSuite<IHeaderRepository>(
"IHeaderRepository",
({ buildSubject, getTracer }) => {
let repo: IHeaderRepository;
beforeEach(async () => {
repo = await buildSubject();
beforeEach(async () => {
repo = await buildSubject();
});
// --- getHeader ---
it("getHeader returns an object with an items array of the seeded length", async () => {
const header = await repo.getHeader();
expect(header).toBeDefined();
expect(header.items).toBeInstanceOf(Array);
expect(header.items).toHaveLength(3);
});
it("getHeader items appear in the seeded order with correct shape", async () => {
const header = await repo.getHeader();
expect(header.items[0]?.label).toBe("Home");
expect(header.items[0]?.href).toBe("/");
expect(header.items[0]?.external).toBe(false);
expect(header.items[1]?.label).toBe("Blog");
expect(header.items[1]?.href).toBe("/blog");
expect(header.items[1]?.external).toBe(false);
expect(header.items[2]?.label).toBe("Docs");
expect(header.items[2]?.href).toBe("/docs");
expect(header.items[2]?.external).toBe(true);
});
it("getHeader items have label, href, and external fields", async () => {
const header = await repo.getHeader();
for (const item of header.items) {
expect(typeof item.label).toBe("string");
expect(item.label.length).toBeGreaterThan(0);
expect(typeof item.href).toBe("string");
expect(item.href.length).toBeGreaterThan(0);
expect(typeof item.external).toBe("boolean");
}
});
it("getHeader logoId is string or undefined", async () => {
const header = await repo.getHeader();
expect(
header.logoId === undefined || typeof header.logoId === "string",
).toBe(true);
});
describe("span emission", () => {
it("getHeader emits header.getHeader span with op=repository", async () => {
if (!getTracer) return;
const tracer = getTracer();
tracer.reset();
await repo.getHeader();
const span = tracer.findSpan("header.getHeader");
expect(span).toBeDefined();
expect(span!.op).toBe("repository");
});
// --- getHeader ---
it("getHeader returns an object with an items array of the seeded length", async () => {
const header = await repo.getHeader();
expect(header).toBeDefined();
expect(header.items).toBeInstanceOf(Array);
expect(header.items).toHaveLength(3);
});
it("getHeader items appear in the seeded order with correct shape", async () => {
const header = await repo.getHeader();
expect(header.items[0]?.label).toBe("Home");
expect(header.items[0]?.href).toBe("/");
expect(header.items[0]?.external).toBe(false);
expect(header.items[1]?.label).toBe("Blog");
expect(header.items[1]?.href).toBe("/blog");
expect(header.items[1]?.external).toBe(false);
expect(header.items[2]?.label).toBe("Docs");
expect(header.items[2]?.href).toBe("/docs");
expect(header.items[2]?.external).toBe(true);
});
it("getHeader items have label, href, and external fields", async () => {
const header = await repo.getHeader();
for (const item of header.items) {
expect(typeof item.label).toBe("string");
expect(item.label.length).toBeGreaterThan(0);
expect(typeof item.href).toBe("string");
expect(item.href.length).toBeGreaterThan(0);
expect(typeof item.external).toBe("boolean");
}
});
it("getHeader logoId is string or undefined", async () => {
const header = await repo.getHeader();
expect(
header.logoId === undefined || typeof header.logoId === "string",
).toBe(true);
});
describe("span emission (R50)", () => {
it("getHeader emits header.getHeader span with op=repository", async () => {
if (!getTracer) return;
const tracer = getTracer();
tracer.reset();
await repo.getHeader();
const span = tracer.findSpan("header.getHeader");
expect(span).toBeDefined();
expect(span!.op).toBe("repository");
});
});
},
);
});
},
);

View File

@@ -12,7 +12,7 @@ describe("getHeaderUseCase", () => {
expect(result.items[0]?.label).toBe("Home");
});
it("(R25) throws ZodError when repository returns malformed header", async () => {
it("throws ZodError when repository returns malformed header", async () => {
const malformedRepo = {
getHeader: async () =>
({ items: [{ label: "", href: "/", external: false }] }) as never,

View File

@@ -37,8 +37,12 @@ export async function bindDevSeedNavigation(ctx: BindContext): Promise<void> {
if (navigationContainer.isBound(INSTRUMENTATION_SYMBOLS.LOGGER)) {
navigationContainer.unbind(INSTRUMENTATION_SYMBOLS.LOGGER);
}
navigationContainer.bind<ITracer>(INSTRUMENTATION_SYMBOLS.TRACER).toConstantValue(tracer);
navigationContainer.bind<ILogger>(INSTRUMENTATION_SYMBOLS.LOGGER).toConstantValue(logger);
navigationContainer
.bind<ITracer>(INSTRUMENTATION_SYMBOLS.TRACER)
.toConstantValue(tracer);
navigationContainer
.bind<ILogger>(INSTRUMENTATION_SYMBOLS.LOGGER)
.toConstantValue(logger);
if (navigationContainer.isBound(NAVIGATION_SYMBOLS.IHeaderRepository)) {
navigationContainer.unbind(NAVIGATION_SYMBOLS.IHeaderRepository);
@@ -54,7 +58,11 @@ export async function bindDevSeedNavigation(ctx: BindContext): Promise<void> {
{ name: "navigation.getHeader", op: "use-case" },
withCapture(
logger,
{ feature: "navigation", layer: "use-case", name: "navigation.getHeader" },
{
feature: "navigation",
layer: "use-case",
name: "navigation.getHeader",
},
getHeaderUseCase(repo),
),
);
@@ -77,13 +85,16 @@ export async function bindDevSeedNavigation(ctx: BindContext): Promise<void> {
{ name: "navigation.getHeader", op: "controller" },
withCapture(
logger,
{ feature: "navigation", layer: "controller", name: "navigation.getHeader" },
{
feature: "navigation",
layer: "controller",
name: "navigation.getHeader",
},
getHeaderController(wrappedGetHeader),
),
),
);
// bus + queue are accept-and-forward in Phase 6; consumed by Phase 7 generator
// output at the <gen:event-handlers> / <gen:jobs> anchors below.
// bus + queue are passed through; generated handlers consume them at the anchors below.
void bus;
void queue;
void realtime;

View File

@@ -15,7 +15,8 @@ import { getHeaderUseCase } from "../application/use-cases/get-header.use-case";
import { getHeaderController } from "../interface-adapters/controllers/get-header.controller";
export function bindProductionNavigation(ctx: BindProductionContext): void {
const { config, tracer, logger, bus, queue, realtime, realtimeRegistry } = ctx;
const { config, tracer, logger, bus, queue, realtime, realtimeRegistry } =
ctx;
// Bind shared instrumentation into feature container
if (navigationContainer.isBound(INSTRUMENTATION_SYMBOLS.TRACER)) {
@@ -24,8 +25,12 @@ export function bindProductionNavigation(ctx: BindProductionContext): void {
if (navigationContainer.isBound(INSTRUMENTATION_SYMBOLS.LOGGER)) {
navigationContainer.unbind(INSTRUMENTATION_SYMBOLS.LOGGER);
}
navigationContainer.bind<ITracer>(INSTRUMENTATION_SYMBOLS.TRACER).toConstantValue(tracer);
navigationContainer.bind<ILogger>(INSTRUMENTATION_SYMBOLS.LOGGER).toConstantValue(logger);
navigationContainer
.bind<ITracer>(INSTRUMENTATION_SYMBOLS.TRACER)
.toConstantValue(tracer);
navigationContainer
.bind<ILogger>(INSTRUMENTATION_SYMBOLS.LOGGER)
.toConstantValue(logger);
// Real repository
if (navigationContainer.isBound(NAVIGATION_SYMBOLS.IHeaderRepository)) {
@@ -42,7 +47,11 @@ export function bindProductionNavigation(ctx: BindProductionContext): void {
{ name: "navigation.getHeader", op: "use-case" },
withCapture(
logger,
{ feature: "navigation", layer: "use-case", name: "navigation.getHeader" },
{
feature: "navigation",
layer: "use-case",
name: "navigation.getHeader",
},
getHeaderUseCase(repo),
),
);
@@ -66,13 +75,16 @@ export function bindProductionNavigation(ctx: BindProductionContext): void {
{ name: "navigation.getHeader", op: "controller" },
withCapture(
logger,
{ feature: "navigation", layer: "controller", name: "navigation.getHeader" },
{
feature: "navigation",
layer: "controller",
name: "navigation.getHeader",
},
getHeaderController(wrappedGetHeader),
),
),
);
// bus + queue are accept-and-forward in Phase 6; consumed by Phase 7 generator
// output at the <gen:event-handlers> / <gen:jobs> anchors below.
// bus + queue are passed through; generated handlers consume them at the anchors below.
void bus;
void queue;
void realtime;

View File

@@ -3,7 +3,7 @@ export type { NavigationRouter } from "./integrations/api/router";
export { HeaderNotFoundError } from "./entities/errors/header";
export { InputParseError } from "./entities/errors/common";
// Use case schemas + types (Plan 9 R18)
// Use case schemas + types
export {
getHeaderInputSchema,
getHeaderOutputSchema,
@@ -17,4 +17,7 @@ export type { IGetHeaderController } from "./interface-adapters/controllers/get-
// <gen:events>
// <gen:realtime-channels>
export { navigationManifest, type NavigationManifest } from "./feature.manifest";
export {
navigationManifest,
type NavigationManifest,
} from "./feature.manifest";

View File

@@ -1,9 +1,12 @@
import { describe, it, expect } from "vitest";
import { RecordingTracer, RecordingLogger } from "@repo/core-testing/instrumentation";
import {
RecordingTracer,
RecordingLogger,
} from "@repo/core-testing/instrumentation";
import { MockHeaderRepository } from "@/infrastructure/repositories/header.repository.mock";
// Mock repo also wraps in spans (R42).
describe("MockHeaderRepository emits spans (R42)", () => {
// Mock repo also wraps in spans.
describe("MockHeaderRepository emits spans", () => {
it("getHeader emits one span with op='repository'", async () => {
const tracer = new RecordingTracer();
const logger = new RecordingLogger();

View File

@@ -30,7 +30,7 @@ describe("navigationRouter", () => {
});
});
describe("navigationRouter (R26 error mapping)", () => {
describe("navigationRouter error mapping", () => {
beforeEach(() => {
navigationContainer.unbindAll();
navigationContainer.load(NavigationModule);
@@ -43,7 +43,10 @@ describe("navigationRouter (R26 error mapping)", () => {
it("translates InputParseError → BAD_REQUEST when extra fields are passed", async () => {
const caller = navigationRouter.createCaller({});
try {
await caller.header({ unexpected: "field" } as unknown as Record<string, never>);
await caller.header({ unexpected: "field" } as unknown as Record<
string,
never
>);
throw new Error("expected throw");
} catch (e) {
expect(e).toBeInstanceOf(TRPCError);
@@ -66,12 +69,16 @@ describe("navigationRouter (R26 error mapping)", () => {
navigationContainer
.bind(NAVIGATION_SYMBOLS.IGetHeaderUseCase)
.toDynamicValue((ctx) =>
getHeaderUseCase(ctx.container.get(NAVIGATION_SYMBOLS.IHeaderRepository)),
getHeaderUseCase(
ctx.container.get(NAVIGATION_SYMBOLS.IHeaderRepository),
),
);
navigationContainer
.bind(NAVIGATION_SYMBOLS.IGetHeaderController)
.toDynamicValue((ctx) =>
getHeaderController(ctx.container.get(NAVIGATION_SYMBOLS.IGetHeaderUseCase)),
getHeaderController(
ctx.container.get(NAVIGATION_SYMBOLS.IGetHeaderUseCase),
),
);
const caller = navigationRouter.createCaller({});