- Rename docs/decisions/adr-012-lazar-conformance.md → adr-012-feature-conventions.md - Strip "Lazar", "Plan 8/9/10/11", "refactor-logs" refs from all ADRs, architecture docs, HTML explainers, and feature/core AGENTS.md files - Update all incoming links in docs/, packages/*/AGENTS.md, HTML explainers Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6.2 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 platform —
initTRPC.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— tRPCinitTRPCinstance + builders; also exportst(the rawinitTRPC.create({...})instance) so feature packages can build their own procedures viat.procedure.use(...)./trpc/context— tRPC context factory only./trpc/define-error-middleware— factory that builds a tRPC middleware translating domain errors toTRPCError. TakesReadonlyArray<readonly [ErrorCtor, TRPC_ERROR_CODE_KEY]>tuples; usesinstanceofdiscrimination; preserves the original error as.cause. Owned by features: each feature passes its own constructors in viaintegrations/api/procedures.ts. core-shared never enumerates feature-specific error classes — this stays boundary-clean
Test conventions
- Tests colocated:
src/lib/slug-field.ts→src/lib/slug-field.test.ts - Vitest environment:
node - Alias:
@/resolves tosrc/ - 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/
Substrate: OpenTelemetry SDK (ADR-017). Sentry is the exporter via @sentry/opentelemetry. Feature packages depend only on the interfaces below — no Sentry or OTel SDK imports.
Three interfaces: ITracer (tracer.interface.ts), ILogger (logger.interface.ts), IMetrics (metrics.interface.ts).
Three implementation pairs:
NoopTracer/NoopLogger/NoopMetrics— pass-through. Default everywhere.OtelTracer/OtelLogger/OtelMetrics— emit via OTel API (@opentelemetry/api,@opentelemetry/api-logs). Live inotel/subfolder. Theotel/subfolder is the only path inpackages/permitted to import@opentelemetry/sdk-*or@sentry/opentelemetry(R52).RecordingTracer/RecordingLogger/RecordingMetrics— in@repo/core-testing/instrumentation, not here.
with-span.ts + with-capture.ts: two higher-order helpers used at DI binding time to wrap use case + controller factory results. The binders apply them as a sandwich — withSpan outermost, withCapture between span and factory:
const wrapped = withSpan(
tracer,
{ name: "blog.getArticles", op: "use-case" },
withCapture(
logger,
{ feature: "blog", layer: "use-case", name: "blog.getArticles" },
factory(deps),
),
);
withSpan is pure delegation to tracer.startSpan. withCapture catches thrown errors, calls logger.captureException(err, { tags }), marks the __sentryReported flag, and re-throws — but bails if the flag is already set so the inner-most layer wins.
reported-flag.ts: small module exporting markReported(err) and isReported(err). Used by withCapture and OtelLogger. RecordingLogger carries an inlined copy (tooling → core import is disallowed by the boundary rule).
Symbols: INSTRUMENTATION_SYMBOLS.ITracer, INSTRUMENTATION_SYMBOLS.ILogger, INSTRUMENTATION_SYMBOLS.IMetrics (all Symbol.for(...) so cross-realm equality holds).
otel/pii-fields.ts: vendor-neutral PII substring list (PII_KEY_SUBSTRINGS, PII_QUERY_PARAM_SUBSTRINGS). Used by otel/pii-scrub-processor.ts (server) and imported by sentry/init-client*.ts (browser).
otel/pii-scrub-processor.ts: PiiScrubSpanProcessor + PiiScrubLogRecordProcessor. Registered FIRST in the OTel processor chain so all downstream exporters (Sentry) see scrubbed data. Replaces Sentry's beforeSend/beforeSendTransaction hooks on the server side (ADR-017 §7).
sentry/init-server.ts + sentry/init-client.ts: centralized init helpers (Next.js flavor). init-server.ts calls Sentry.init with sendDefaultPii: false (R31) — no beforeSend hook (PII scrubbed at OTel layer). init-client.ts retains beforeSend/beforeSendTransaction because browser does not use the OTel pipeline.
sentry/init-server-node.ts + sentry/init-client-react.ts: Vite/non-Next variants used by apps/web-tanstack. Same posture as their Next.js counterparts.
di/bind-noop-instrumentation.ts + bind-otel-instrumentation.ts: bind ITracer + ILogger + IMetrics symbols to a Container. Returns the resolved instances so callers can use them without container lookup. bindSentryInstrumentation kept as a deprecated alias for one release.
Subpath exports (package.json#exports):
./instrumentation— barrel (interfaces + Noops + OtelTracer/OtelLogger + withSpan + withCapture + reported-flag + symbols + binders)./instrumentation/otel— OTel init helper + resource builder barrel./instrumentation/otel/init-server-node—initOtelServerNode(app bootstrap, server-side OTel SDK)./instrumentation/sentry/init-server— Next.js server Sentry.init helper./instrumentation/sentry/init-client— Next.js browser Sentry.init helper./instrumentation/sentry/init-server-node—@sentry/nodeserver init (TanStack Start)./instrumentation/sentry/init-client-react—@sentry/reactbrowser init (TanStack Start)
Boundaries:
core-shared/instrumentation/otel/**MAY import from@opentelemetry/sdk-*and@sentry/opentelemetry.core-shared/instrumentation/sentry/**MAY import from@sentry/*(browser init files).- Everything else in
packages/core-shared/src/MUST NOT. - ESLint rules R40 + R52 in
core-eslint/base.jsenforce the broader monorepo boundary.