docs: instrumentation testing patterns + dependency-flow + core-shared AGENTS update
Adds "Asserting spans and captures" section to tdd-workflow.md with RecordingTracer/Logger usage and inline withSpan wrapping pattern for direct-injection tests. Adds R49/R50 section to testing-strategy.md covering the no-sentry guard, contract suite span assertions, and RecordingTracer/Logger field reference. Adds "TRACER / LOGGER (Plan 10)" subsection to dependency-flow.md showing the bindAll → feature-container wiring path. Adds an "src/instrumentation/" section to core-shared/AGENTS.md documenting the two interfaces, three impl pairs, withSpan helper, scrubbers, both Next.js + Vite/React init helpers, and the subpath exports. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -93,3 +93,33 @@ Three layers work in tandem:
|
||||
- **Turborepo `boundaries`** runs at build time, validating the entire workspace graph including transitive dependencies
|
||||
|
||||
The two enforcement layers are independent but complementary. ESLint is stricter on per-import context (e.g., file-specific exemptions via `// @boundaries-ignore`), while Turborepo catches transitive issues that lint-time checking misses. Run `pnpm lint` and `pnpm turbo boundaries` in CI to catch all violations.
|
||||
|
||||
## TRACER / LOGGER (Plan 10)
|
||||
|
||||
The instrumentation layer is **per-feature container** but **app-wide instance**: each feature container binds `INSTRUMENTATION_SYMBOLS.TRACER` and `INSTRUMENTATION_SYMBOLS.LOGGER` to the SAME instance, constructed once by the app's `bindAll()` dispatcher (Rule 0).
|
||||
|
||||
```
|
||||
apps/web-next/src/server/bind-production.ts (bindAll)
|
||||
│
|
||||
├─ Rule 0: WEB_NEXT_SENTRY_DSN set?
|
||||
│ yes → bindSentryInstrumentation(sharedContainer, { dsn, app: "web-next" })
|
||||
│ no → bindNoopInstrumentation(sharedContainer)
|
||||
│ ↓
|
||||
│ tracer + logger instances
|
||||
│ ↓
|
||||
├─ bindProductionBlog(config, tracer, logger)
|
||||
│ │
|
||||
│ ├─ blogContainer.bind(TRACER).toConstantValue(tracer)
|
||||
│ ├─ blogContainer.bind(LOGGER).toConstantValue(logger)
|
||||
│ ├─ ArticlesRepository(config, tracer, logger) → bound to IArticlesRepository
|
||||
│ ├─ withSpan(tracer, ...) wraps every use case → bound to UseCase symbol
|
||||
│ └─ withSpan(tracer, ...) wraps every controller → bound to Controller symbol
|
||||
│
|
||||
└─ (same for auth, marketing-pages, navigation, media)
|
||||
```
|
||||
|
||||
**Why per-feature containers also get the binding:** lets internal DI-resolved code in a feature pull TRACER/LOGGER without going through the app dispatcher. In practice, only repository classes and feature-internal services would use this — controllers and use cases receive instrumentation via the bind-time wrapper.
|
||||
|
||||
**Why the shared container exists at all:** isolates Rule 0 resolution from feature containers. Feature containers don't need to know if Sentry is on or off — they just receive an `ITracer` instance.
|
||||
|
||||
**Boundary rule:** feature packages MUST NOT import `@sentry/*` directly (R40, ESLint-enforced). The only paths that may import the SDK are `core-shared/instrumentation/sentry/**`, the `bind-sentry-instrumentation` files, the `no-sentry` test guards, and per-app `instrumentation*.{ts,mjs}` / `next.config.{mjs}` / `vite.config.{ts}` entries.
|
||||
|
||||
Reference in New Issue
Block a user