docs(html): data-flow-explainer §06 tracing + di-explainer §08 instrumentation symbols
Inserts a new §06 "Tracing & error capture" between the existing "Tradeoffs by part" (§05) and the verdict (which moves to §07). Includes the trace tree (HTTP → tRPC → controller → use-case → repo → Payload), capture-rules table (where captureException fires per layer + the no-capture rule for defineErrorMiddleware), the __sentryReported double-report guard explanation, and the R31–R38 PII rule list. Adds matching CSS (.trace-tree / .capture-rules / .pii-rules) tuned to the existing dark-paper palette and Fraunces heading font. Updates the contents nav from 6 → 7 columns. di-explainer.html gets a new §08 "Instrumentation symbols" with cards for TRACER and LOGGER, the bindAll() Rule-0 wiring path, and a note on why feature containers also bind the symbols. Two-column grid collapses to one on narrow viewports. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1401,6 +1401,53 @@ footer .colophon {
|
||||
@keyframes fadeIn { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: translateY(0); } }
|
||||
.fade-in { animation: fadeIn 0.32s ease; }
|
||||
|
||||
/* ─── §06 Tracing & error capture (Plan 10) ─────────────────────────── */
|
||||
|
||||
.trace-h3 {
|
||||
font-family: "Fraunces", serif;
|
||||
font-weight: 500;
|
||||
font-size: 1.1rem;
|
||||
margin: 32px 0 14px;
|
||||
color: var(--paper);
|
||||
}
|
||||
.trace-p {
|
||||
margin: 0 0 18px;
|
||||
max-width: 70ch;
|
||||
}
|
||||
.trace-tree {
|
||||
background: var(--ink-2, rgba(0,0,0,.32));
|
||||
padding: 1.25rem 1.5rem;
|
||||
border-radius: 4px;
|
||||
overflow-x: auto;
|
||||
font-family: "JetBrains Mono", monospace;
|
||||
font-size: 0.85rem;
|
||||
line-height: 1.6;
|
||||
margin: 0 0 18px;
|
||||
}
|
||||
.capture-rules {
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
margin: 0 0 28px;
|
||||
}
|
||||
.capture-rules th, .capture-rules td {
|
||||
padding: 0.6rem 0.85rem;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid var(--rule, rgba(255,255,255,.12));
|
||||
}
|
||||
.capture-rules th {
|
||||
font-family: "Fraunces", serif;
|
||||
font-weight: 500;
|
||||
}
|
||||
.pii-rules {
|
||||
display: grid;
|
||||
gap: 0.5rem;
|
||||
padding-left: 1.2rem;
|
||||
margin: 0 0 14px;
|
||||
}
|
||||
.pii-rules li {
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
/* ─── Responsive (basic) ─────────────────────────────────────────────── */
|
||||
|
||||
@media (max-width: 1100px) {
|
||||
@@ -1427,13 +1474,14 @@ footer .colophon {
|
||||
|
||||
<div class="contents-row">
|
||||
<div class="contents-label">Contents</div>
|
||||
<ol class="contents" style="grid-template-columns: repeat(6, 1fr);">
|
||||
<ol class="contents" style="grid-template-columns: repeat(7, 1fr);">
|
||||
<li><a href="#anatomy"><span class="num">01</span>Feature anatomy</a></li>
|
||||
<li><a href="#flow"><span class="num">02</span>Request flow</a></li>
|
||||
<li><a href="#di"><span class="num">03</span>Dependency injection</a></li>
|
||||
<li><a href="#contracts"><span class="num">04</span>Mocks, contracts & factories</a></li>
|
||||
<li><a href="#tradeoffs"><span class="num">05</span>Tradeoffs by part</a></li>
|
||||
<li><a href="#verdict"><span class="num">06</span>The verdict</a></li>
|
||||
<li><a href="#tracing"><span class="num">06</span>Tracing & error capture</a></li>
|
||||
<li><a href="#verdict"><span class="num">07</span>The verdict</a></li>
|
||||
</ol>
|
||||
</div>
|
||||
</header>
|
||||
@@ -2305,10 +2353,71 @@ footer .colophon {
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ─── 06 ─ VERDICT ─────────────────────────────────────────────────── -->
|
||||
<section id="verdict" style="border-bottom: 0;">
|
||||
<!-- ─── 06 ─ TRACING & ERROR CAPTURE ─────────────────────────────────── -->
|
||||
<section id="tracing">
|
||||
<div class="section-head">
|
||||
<div class="section-num">§ 06</div>
|
||||
<div>
|
||||
<h2 class="section-title">Tracing & <em>error capture</em>.</h2>
|
||||
<p class="section-blurb">Every request produces a nested span tree: tRPC procedure → controller → use case → repository → Payload op. Errors are captured at the throw site closest to the cause, never at the boundary that translates them.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3 class="trace-h3">The trace tree (one tRPC request)</h3>
|
||||
<pre class="trace-tree"><code>HTTP transaction (auto, @sentry/nextjs)
|
||||
└── tRPC procedure span (auto, sentry trpc integration)
|
||||
└── controller span (op="controller", DI-wrapped)
|
||||
└── use-case span (op="use-case", DI-wrapped)
|
||||
└── repository span (op="repository", explicit startSpan)
|
||||
└── Payload Local API call (auto, @sentry/node http)</code></pre>
|
||||
|
||||
<h3 class="trace-h3">Capture rules (where <code>captureException</code> fires)</h3>
|
||||
<table class="capture-rules">
|
||||
<thead>
|
||||
<tr><th>Layer</th><th>Captures</th><th>Doesn't capture</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Repository</td>
|
||||
<td>Infra / Payload errors that originate here</td>
|
||||
<td>Bubbled errors</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Use case</td>
|
||||
<td>Business-rule violations originated in this body</td>
|
||||
<td>Errors from repos (already captured)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Controller</td>
|
||||
<td><code>InputParseError</code> from safeParse failure</td>
|
||||
<td>Anything else</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>defineErrorMiddleware</code></td>
|
||||
<td><strong>Nothing</strong> — maps domain → TRPCError only</td>
|
||||
<td>—</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h3 class="trace-h3">Double-report guard</h3>
|
||||
<p class="trace-p">Every error captured by <code>SentryLogger.captureException</code> gets a non-enumerable <code>__sentryReported = true</code> property. A second capture call for the same error returns early. This means each error surfaces in Sentry exactly once, regardless of how many layers it passes through.</p>
|
||||
|
||||
<h3 class="trace-h3">PII rules (R31–R38, non-negotiable)</h3>
|
||||
<ul class="pii-rules">
|
||||
<li><code>sendDefaultPii: false</code> — every <code>Sentry.init()</code>. CI grep gate.</li>
|
||||
<li>Replay <strong>default-masks all text + inputs + media</strong>. Allowlist starts empty.</li>
|
||||
<li><code>beforeSend</code> scrubber strips email / password / token / cookie / authorization / ipaddress keys (substring match).</li>
|
||||
<li><code>beforeSendTransaction</code> scrubber strips PII query params from URLs.</li>
|
||||
<li><code>setUser</code> accepts only <code>{ id }</code>. Stripping wrapper warns in dev when other keys passed.</li>
|
||||
<li>IPv4/IPv6 in event payload string values redacted to <code>[redacted-ip]</code>.</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<!-- ─── 07 ─ VERDICT ─────────────────────────────────────────────────── -->
|
||||
<section id="verdict" style="border-bottom: 0;">
|
||||
<div class="section-head">
|
||||
<div class="section-num">§ 07</div>
|
||||
<div>
|
||||
<h2 class="section-title">Do we <em>need</em> them?</h2>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user