Files
agentic-dev/packages/core-shared/AGENTS.md
Danijel Martinek 85513ccbf9 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>
2026-05-07 20:35:28 +02:00

4.7 KiB

AGENTS.md — core-shared

Generic, reusable primitives with zero business knowledge. This package is the foundation for all other packages and exports utilities, Payload field/hook definitions, and tRPC initialization.

Responsibilities

  • Generic primitives — environment helpers, date utilities, type guards
  • Payload utilities — field definitions (slug, SEO), blocks (CTA), access controls (is-admin), hooks (slugify, publish timestamp)
  • tRPC platforminitTRPC.create(), shared context factory, procedure builders
  • No business domain knowledge — no awareness of articles, users, media, or any feature

Must NOT import

  • Any feature package (@repo/auth, @repo/blog, etc.)
  • Any app package
  • Framework-specific code (Next.js, TanStack React Query)

Public exports

From package.json:

  • . — all utilities, Payload exports, tRPC init
  • ./payload — Payload field/hook/block utilities only
  • ./trpc/init — tRPC initTRPC instance + builders. Plan 9: also exports t (the raw initTRPC.create({...}) instance) so feature packages can build their own procedures via t.procedure.use(...)
  • ./trpc/context — tRPC context factory only
  • ./trpc/define-error-middlewarePlan 9: factory that builds a tRPC middleware translating domain errors to TRPCError. Takes ReadonlyArray<readonly [ErrorCtor, TRPC_ERROR_CODE_KEY]> tuples; uses instanceof discrimination; preserves the original error as .cause. Owned by features: each feature passes its own constructors in via integrations/api/procedures.ts. core-shared never enumerates feature-specific error classes — this stays boundary-clean

Test conventions

  • Tests colocated: src/lib/slug-field.tssrc/lib/slug-field.test.ts
  • Vitest environment: node
  • Alias: @/ resolves to src/
  • Run: pnpm test --filter @repo/core-shared

Covered areas:

  • Slug field generation + validation
  • Payload hooks (publish-at timestamp, slugify-if-missing)
  • Access control helpers

src/instrumentation/

Two interfaces: ITracer (in tracer.interface.ts) and ILogger (in logger.interface.ts).

Three implementation pairs:

  • NoopTracer / NoopLogger — pass-through. Default everywhere.
  • SentryTracer / SentryLogger — adapters over @sentry/nextjs. Live in sentry/ subfolder. The sentry/ subfolder is the only path in packages/ permitted to import @sentry/* (R40), with the additional exception of instrumentation/di/bind-sentry-instrumentation.{ts,test.ts}.
  • RecordingTracer / RecordingLogger — in @repo/core-testing/instrumentation, not here.

with-span.ts: higher-order helper used at DI binding time to wrap use case + controller factory results in a span. Pattern:

const wrapped = withSpan(
  tracer,
  { name: "blog.getArticles", op: "use-case" },
  factory(deps),
);

Symbols: INSTRUMENTATION_SYMBOLS.TRACER, INSTRUMENTATION_SYMBOLS.LOGGER (both Symbol.for(...) so cross-realm equality holds).

sentry/scrub.ts: PII scrubbers used by every Sentry.init() call across the monorepo. Substring-based key matching catches derived names (userEmail, accessToken, apiKey, ipAddress). IPv4/IPv6 are also redacted from string values via the [redacted-ip] token.

sentry/init-server.ts + init-client.ts: centralized init helpers (Next.js flavor) that hard-code R31 (sendDefaultPii: false), R32/R33 (scrubbers), R34/R35 (replay mask flags), R37 (sample-rate defaults). Apps call these from instrumentation.ts / instrumentation-client.ts.

sentry/init-server-node.ts + init-client-react.ts: Vite/non-Next variants used by apps/web-tanstack. Same R31/R32/R33/R34/R35/R37 posture; uses @sentry/node + @sentry/react instead of @sentry/nextjs.

di/bind-noop-instrumentation.ts + bind-sentry-instrumentation.ts: bind TRACER + LOGGER symbols to a Container. Returns the resolved instances so callers can use them without container lookup.

Subpath exports (package.json#exports):

  • ./instrumentation — barrel (interfaces + Noops + withSpan + symbols + binders + node/react init helpers)
  • ./instrumentation/sentry/init-server — Next.js server init helper
  • ./instrumentation/sentry/init-client — Next.js client init helper
  • ./instrumentation/sentry/init-server-node@sentry/node server init (TanStack Start)
  • ./instrumentation/sentry/init-client-react@sentry/react client init (TanStack Start)
  • ./instrumentation/sentry/scrubbeforeSend / beforeSendTransaction (used by per-app PII test)

Boundaries:

  • core-shared/instrumentation/sentry/** MAY import from @sentry/*.
  • Everything else in packages/core-shared/src/ MUST NOT.
  • The eslint rule in core-eslint/base.js enforces the broader monorepo boundary (R40).