docs(architecture): surface core-audit + DPA across architecture docs

Touches the deeper architecture surfaces the Phase 6 sweep skipped:

- overview.md: split must-have (core-shared, core-cms, core-api) from
  optional (core-trpc, core-ui, core-realtime, core-events, core-audit);
  add core-audit to the Five tags optional list
- dependency-flow.md: extend the bindAll diagram with resolveAudit;
  add auditLog row to the BindContext table; rename the
  TRACER/LOGGER/METRICS heading to include AUDIT (ADR-018); note the
  R52-style boundary rule for @repo/core-audit (consume via protocol)
- vertical-feature-spec.md: target-state section now states 3 must-have
  + 5 optional cores; tag matrix includes the optional cores; bind-
  production signature destructure includes auditLog
- di-explainer.html: §08 instrumentation gains an IAuditLog block + the
  Wiring path tree shows resolveAudit + auditLog in ctx
- testing-strategy.md: RecordingAuditLog reference + reset() guidance
This commit is contained in:
2026-05-11 17:06:58 +02:00
parent 451a3cdbc3
commit 7c915cb447
5 changed files with 40 additions and 17 deletions

View File

@@ -1170,7 +1170,7 @@ footer .colophon {
<div class="section-num">§ 08</div>
<div>
<h2 class="section-title">Instrumentation <em>symbols</em>.</h2>
<p class="section-blurb">ADR-014 + ADR-017 added instrumentation symbols to the per-feature container — <code>ITracer</code>, <code>ILogger</code>, and <code>IMetrics</code> — bound by a separate Rule 0 in <code>bindAll()</code> that's <strong>orthogonal</strong> to the repo binding mode. The DSN env var decides OTel+Sentry vs Noop; <code>USE_DEV_SEED</code> / <code>NODE_ENV</code> decide real vs mock repos. <strong>Substrate: OpenTelemetry SDK.</strong> Sentry is the exporter via <code>@sentry/opentelemetry</code>.</p>
<p class="section-blurb">ADR-014 + ADR-017 added instrumentation symbols to the per-feature container — <code>ITracer</code>, <code>ILogger</code>, and <code>IMetrics</code> — bound by a separate Rule 0 in <code>bindAll()</code> that's <strong>orthogonal</strong> to the repo binding mode. The DSN env var decides OTel+Sentry vs Noop; <code>USE_DEV_SEED</code> / <code>NODE_ENV</code> decide real vs mock repos. <strong>Substrate: OpenTelemetry SDK.</strong> Sentry is the exporter via <code>@sentry/opentelemetry</code>. ADR-018 adds <code>IAuditLog</code> as a parallel channel with different durability and redaction contracts — see <a href="audit-and-compliance-explainer.html">audit-and-compliance-explainer.html</a>.</p>
</div>
</div>
@@ -1184,6 +1184,9 @@ footer .colophon {
<h3 class="trace-h3"><code>INSTRUMENTATION_SYMBOLS.IMetrics</code></h3>
<p class="trace-p">Bound to <code>NoopMetrics</code> or <code>OtelMetrics</code>. <code>OtelMetrics</code> uses the OTel metrics API (<code>counter</code>, <code>histogram</code>, <code>gauge</code>). Per-feature adoption is opportunistic — no feature call sites required.</p>
<h3 class="trace-h3"><code>IAuditLog</code> (ADR-018, optional)</h3>
<p class="trace-p">Not a symbol — passed through <code>ctx.auditLog</code>, not bound on the per-feature container. Present only when <code>@repo/core-audit</code> is scaffolded via <code>pnpm turbo gen core-package audit</code>. Pre-wrapped in <code>TraceIdEnrichingAuditLog</code> so callers don't supply <code>correlationId</code> — it's read from the active OTel span at sink time. Parallel channel: OTel is sampled/pruned best-effort observability; audit is append-only and retained for compliance. Same <code>traceId</code> lives in both, which is the debugging bridge.</p>
</div>
<div>
@@ -1194,12 +1197,15 @@ footer .colophon {
OR bindNoopInstrumentation() ← all Noop
└─ resolveEventsAndJobs*() ← ADR-015 (env-driven bus + queue)
└─ Payload-backed in prod, in-memory in dev-seed
└─ build ctx: BindProductionContext = { config, tracer, logger, metrics?, bus, queue, realtime, realtimeRegistry }
└─ required: tracer, logger, config | optional: metrics, bus, queue, realtime, realtimeRegistry
└─ resolveRealtime() ← ADR-016 (Socket.IO or in-memory)
└─ resolveAudit() ← ADR-018 (TraceIdEnriching( MultiSink([Stdout, Payload]) ))
└─ absent → ctx.auditLog is undefined; record() calls become no-ops via ?.
└─ build ctx: BindProductionContext = { config, tracer, logger, metrics?, bus, queue, realtime, realtimeRegistry, auditLog }
└─ required: tracer, logger, config | optional: metrics, bus, queue, realtime, realtimeRegistry, auditLog
└─ bindProductionX(ctx) ← single ctx object passed to each feature binder
└─ feature container also binds TRACER + LOGGER
└─ withSpan(withCapture(...)) at every use case + controller
└─ // &lt;gen:event-handlers&gt; / // &lt;gen:jobs&gt; injection sites</code></pre>
└─ // &lt;gen:event-handlers&gt; / // &lt;gen:jobs&gt; / // &lt;gen:realtime-handlers&gt; / // &lt;gen:audit-hooks&gt; injection sites</code></pre>
<p class="trace-p"><strong>Why per-feature containers also get the binding:</strong> repository classes resolve TRACER/LOGGER through the container; controllers and use cases receive instrumentation via the bind-time wrapper instead.</p>
</div>