docs(agents): bring agent-facing docs in line with the R44 fix
Six docs lagged after the post-merge R44 fix added withCapture + reported-flag.ts. They mentioned withSpan only and described the capture story as if it were inline in use-case / controller bodies. This commit aligns them with what shipped. CLAUDE.md (Key Conventions): - "Spans applied at DI bind time" → "Spans + capture composed at DI bind time" with the withSpan(withCapture(factory)) sandwich and the outermost-span ordering note. - "Capture at throw sites only" expanded to mention the __sentryReported flag, the three flag-checking sites (withCapture, SentryLogger, RecordingLogger), and where the helper lives. AGENTS.md (Instrumentation conventions): - Use case + controller wrapping example shows the full sandwich. - Capture-rules table now explicitly says "via withCapture" for use cases and controllers, and "flag set, withCapture bails" for the bubbled cases. packages/core-shared/AGENTS.md: - "with-span.ts" entry split into a paired with-span + with-capture block, including the actual sandwich code. - New entry for reported-flag.ts explaining the helper and why RecordingLogger inlines the check (boundary rule). - Barrel re-export list updated. docs/architecture/vertical-feature-spec.md (§16): - The bind-production line now describes the withSpan(withCapture(...)) sandwich, the outermost-span rationale, and the bubbled-error bail. docs/architecture/dependency-flow.md (TRACER/LOGGER subsection): - bindAll diagram updated: real repo line annotates inline calls; use case + controller lines show withSpan(withCapture(...)). docs/guides/tdd-workflow.md (Asserting spans and captures): - Direct-injection example shows the binder sandwich. - Capture-assertion example explains the flag-bail behaviour and links to the new tests/r44-no-double-capture.test.ts e2e example. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -657,11 +657,11 @@ E2E tests live in `apps/web-next/e2e/`. The `webServer` block in `apps/web-next/
|
||||
|
||||
## Asserting spans and captures (Plan 10)
|
||||
|
||||
Use cases, controllers, and repositories emit OpenTelemetry-style spans through the `ITracer` interface. Tests that need to assert span shape inject a `RecordingTracer`:
|
||||
Use cases, controllers, and repositories emit OpenTelemetry-style spans through the `ITracer` interface. Repositories also call `logger.captureException` inline; use cases and controllers get capture composed in via `withCapture` at DI bind time. Tests that need to assert either inject `RecordingTracer` + `RecordingLogger`:
|
||||
|
||||
```ts
|
||||
import { RecordingTracer, RecordingLogger } from "@repo/core-testing/instrumentation";
|
||||
import { withSpan } from "@repo/core-shared/instrumentation";
|
||||
import { withSpan, withCapture } from "@repo/core-shared/instrumentation";
|
||||
import { MockArticlesRepository } from "@/infrastructure/repositories/articles.repository.mock";
|
||||
import { getArticlesUseCase } from "@/application/use-cases/get-articles.use-case";
|
||||
|
||||
@@ -671,11 +671,15 @@ describe("blog.getArticles use case", () => {
|
||||
const logger = new RecordingLogger();
|
||||
const repo = new MockArticlesRepository(tracer, logger);
|
||||
// Use cases are wrapped at DI bind time. For direct-injection tests,
|
||||
// wrap inline:
|
||||
// mirror the binder's sandwich: withSpan(withCapture(factory(deps))).
|
||||
const wrapped = withSpan(
|
||||
tracer,
|
||||
{ name: "blog.getArticles", op: "use-case" },
|
||||
getArticlesUseCase(repo),
|
||||
withCapture(
|
||||
logger,
|
||||
{ feature: "blog", layer: "use-case", name: "blog.getArticles" },
|
||||
getArticlesUseCase(repo),
|
||||
),
|
||||
);
|
||||
await wrapped({ limit: 10 });
|
||||
expect(tracer.findSpan("blog.getArticles")?.op).toBe("use-case");
|
||||
@@ -684,17 +688,19 @@ describe("blog.getArticles use case", () => {
|
||||
});
|
||||
```
|
||||
|
||||
**Capture assertions** use `RecordingLogger`:
|
||||
**Capture assertions** use `RecordingLogger`. Note: `RecordingLogger.captureException` honours the `__sentryReported` flag — so when a repo's catch block captures and the outer `withCapture` sees the bubbled error, only the inner-most call records:
|
||||
|
||||
```ts
|
||||
const logger = new RecordingLogger();
|
||||
const repo = new MockArticlesRepository(tracer, logger);
|
||||
// Force an infra error in your test setup, then:
|
||||
expect(logger.captures).toHaveLength(1);
|
||||
expect(logger.captures).toHaveLength(1); // exactly one — flag prevents double
|
||||
expect(logger.captures[0]).toMatchObject({
|
||||
kind: "exception",
|
||||
ctx: { tags: { feature: "blog", repo: "articles", method: "getArticles" } },
|
||||
});
|
||||
```
|
||||
|
||||
For an end-to-end example (controller → use case → repo, all wrapped, asserting no double-capture across layers), see `packages/blog/tests/r44-no-double-capture.test.ts`.
|
||||
|
||||
**Default mocks** (when you don't need assertions): construct `new MockArticlesRepository()` with no args — the constructor defaults bind `NoopTracer` + `NoopLogger`.
|
||||
|
||||
Reference in New Issue
Block a user