All three apps' instrumentation.ts files now call initOtelServerNode directly
instead of initSentryServer/initSentryServerNode, closing the startup window
where @sentry/nextjs auto-instrumentation could send unscrubbed errors before
bindAll() fires. bindOtelInstrumentation no longer calls initOtelServerNode
(SDK init belongs at app boot, binding at request scope). Orphaned sentry/
init-server*.ts files deleted; their package.json subpath exports removed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Removes sentry-logger.ts and its test now that OtelLogger is the active
ILogger impl in bind-otel-instrumentation.ts. Updates the binder test to
assert OtelLogger (not SentryLogger) is bound. Fixes TypeScript errors:
SentryLogRecordForwarder registers directly as LogRecordProcessor (not
wrapped in BatchLogRecordProcessor which expects a LogRecordExporter);
severityNumber undefined guard added.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds SentryLogRecordForwarder (LogRecordProcessor impl) to sentry-bridge.ts
that forwards OTel log records to Sentry via captureException/captureMessage.
Wires it as a BatchLogRecordProcessor in init-server-node.ts. Replaces
SentryLogger with OtelLogger in bind-otel-instrumentation.ts. 7 new bridge
tests pass alongside the existing 25 tests.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds @opentelemetry/api-logs ^0.55.0 and @opentelemetry/sdk-logs ^0.55.0
to core-shared dependencies in preparation for OtelLogger (Phase 3).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
sentry/sentry-tracer.ts and its test removed. OtelTracer is now the active
ITracer implementation. Spans flow to Sentry via @sentry/opentelemetry's
SentrySpanProcessor wired in bindOtelInstrumentation → initOtelServerNode.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
git mv bind-sentry-instrumentation → bind-otel-instrumentation; function
renamed; new impl calls initOtelServerNode and binds OtelTracer (SentryLogger
kept for Phase 3). Barrel re-exports both new name and deprecated alias
bindSentryInstrumentation for one release. ESLint allowlist updated to the
new filename. init-server-node opts gains release? field.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements OtelTracer as ITracer replacement for SentryTracer. Uses
@opentelemetry/api's trace.getTracer + startActiveSpan, maps span.op to
a span attribute, catches exceptions via recordException + setStatus(ERROR),
and filters null attribute values. Five Vitest tests covering name/attributes,
nesting, exception recording, status mapping, and null filtering.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- AGENTS.md bind-production code block: shows the slim default state
(no @repo/core-events / @repo/core-realtime imports, BindProductionContext
with no generic args) with a comment pointing to the scaffold workflow.
The previous block showed a fully-wired post-scaffold state without
signaling that none of those packages exist in main.
- bind-protocols.test.ts: top-of-file comment clarifies what these tests
actually verify (protocol shapes have required methods) vs what the
spec text might suggest (full assignability of optional packages'
interfaces — that's verified by the e2e reconstruction tests, not here).
- core-package-generator.md: drops two stale "Until Phases 3-6 land"
parentheticals — the phases shipped.
- config.test.ts: extends the choices assertion to cover all 4 names
(realtime, events, trpc, ui).
- marketing-pages bind-* comments: reverse the inverted optional/required
language. queue (IJobQueue) is from core-shared and always present;
bus is the optional one (from @repo/core-events when scaffolded).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- di-explainer.html: update stale JS data strings from positional-arg
form (bindAllProduction(config), bindProductionBlog(config,tracer,...))
to ctx-arg form (bindAllProduction(deps), bindProductionBlog(ctx)) with
a note on ctx shape.
- auth/sign-up.use-case: change bus param from IEventBus to
EventBusProtocol|undefined; guard bus.publish with if(bus) so the use
case is safe when core-events is absent (Phase 3+).
- auth/bind-production + bind-dev-seed: drop as IEventBus cast and unused
IEventBus import; ctx.bus is now passed directly (typed as
EventBusProtocol|undefined).
- marketing-pages/bind-production + bind-dev-seed: drop as IJobQueue cast
and unused IJobQueue import; wrap event-handler DI block in if(queue)
guard so the handler is only bound when core-jobs is wired.
- core-shared/src/index.ts: add `export * from "./di"` as the plan
specified (subpath export alone is no longer the only access path).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
Plan 10 documented R44 (capture at originating-throw layer) but only the
R43 repo leg was wired. captureException had zero call sites in any
controller or use-case body. This commit closes the gap.
Mechanism:
- Extract __sentryReported flag helpers into core-shared/instrumentation/
reported-flag.ts. SentryLogger switches to importing them; RecordingLogger
carries an inlined copy (tooling → core boundary disallows the import).
- Add withCapture(logger, tags, fn) higher-order wrapper paralleling
withSpan. On throw: capture-with-tags, mark, re-throw. Bail if the flag
was already set — covers the bubbled-from-repo case so each error
surfaces in the logger exactly once with the inner-most layer's tags.
- Apply withSpan(withCapture(factory)) in every feature's bind-production
and bind-dev-seed: auth (3 use cases × 3 controllers), blog (3×3),
marketing-pages (2×2), navigation (1×1), media (3×3). Span is outermost
so the errored span timing reflects the capture-and-rethrow.
- RecordingLogger.captureException now also honours the flag — test
capture counts stay honest when both repo and outer layer wrap.
Tests:
- packages/core-shared/src/instrumentation/with-capture.test.ts —
4 cases covering success, capture-on-throw, mark-on-capture, no-double
via the flag.
- packages/blog/tests/r44-no-double-capture.test.ts — 3 cases: repo throw
→ 1 capture with repo tags; controller parse fail → 1 capture with
controller tags; success → 0 captures.
Verification: pnpm test 26/26, pnpm lint 15/15, pnpm typecheck 14/14.
Docs: ADR-014 and the refactor log gain a "Post-merge follow-up" section
recording the gap, the fix, and the underlying lesson (don't describe
intent as shipped state — grep first).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
Adds initSentryServerNode + initSentryClientReact to core-shared
(Vite/non-Next variants of the existing init helpers — same R31/R32/R33
posture, R34/R35/R37 replay defaults). Extends no-sentry.ts to mock
@sentry/node + @sentry/react. Wires the web-tanstack server/client
instrumentation entry hooks and adds the R38 PII test.
Spec deviation: web-tanstack has no vite.config.ts yet (placeholder app
per its package.json). The @sentry/vite-plugin dep is added but unused
until the TanStack Start build is wired in a later plan. A minimal
src/vite-env.d.ts shims ImportMetaEnv for the client entry until the
full Vite types land.
@sentry/node and @sentry/react are added to core-shared as optional
peerDependencies so feature packages don't transitively pull them in;
they're also devDependencies of core-shared for typecheck/test runs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds apps/web-next/instrumentation.ts (server) and instrumentation-client.ts
(browser) hooks, wraps next.config.mjs with withSentryConfig (R52), and adds
the R38 per-app PII scrubber smoke test.
Spec deviation: extend PII_KEY_SUBSTRINGS with "ipaddress" so keys like
ipAddress trigger key-level redaction (tighter posture than the spec's
substring list; existing scrub.test.ts still passes).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds DI binder functions for instrumentation. bindNoopInstrumentation binds
NoopTracer + NoopLogger; bindSentryInstrumentation calls initSentryServer then
binds SentryTracer + SentryLogger. Both are idempotent (unbind-first). Adds
inversify + reflect-metadata as devDependencies so container tests compile.
Re-exports from instrumentation barrel. 6 new tests (3+3).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>