dsfb-debug 0.1.0

DSFB-Debug — Structural Semiotics Engine for Software Debugging. A deterministic, read-only, observer-only augmentation layer for execution-trace residual interpretation. Does NOT replace existing observability tools — augments them with typed structural interpretation.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# DSFB-Debug \u2014 Reviewer Reproducibility Notebook\n",
    "\n",
    "**Deterministic detector-field semiotics with routed forensic witness-field fusion**\n",
    "\n",
    "This notebook runs the full DSFB-Debug crate from scratch on a fresh Colab runtime, against twelve real-bytes vendored fixtures spanning nine distinct upstream public datasets, and produces:\n",
    "\n",
    "- 126 figures (10 per fixture \u00d7 12 fixtures + 3 architecture/infrastructure + 3 cross-fixture summary)\n",
    "- 12 verbatim JSON metric blocks (one per fixture)\n",
    "- A single multi-page PDF report (`report.pdf`) assembled via `matplotlib.PdfPages`\n",
    "- A zipped artefact bundle for one-click download\n",
    "\n",
    "Output folder is timestamped: `output-dsfb-debug/dsfb-debug-{ISO-datetime}/`. Reruns never overwrite each other.\n",
    "\n",
    "---\n",
    "\n",
    "## Prior Art Notice (35 U.S.C. \u00a7 102)\n",
    "\n",
    "This notebook constitutes prior art deposited via Zenodo + GitHub at the run-id timestamp encoded in the parent folder name. All rights reserved to prior-art date of public deposit.\n",
    "\n",
    "## IP Notice\n",
    "\n",
    "The theoretical framework, formal constructions, and supervisory methods described herein constitute proprietary Background IP of **Invariant Forge LLC** (Delaware LLC No. 10529072), with prior art established by this publication and earlier Zenodo DOI publications by the same author. Commercial deployment requires a separate written license. Reference implementations are released under Apache 2.0.\n",
    "\n",
    "**Licensing:** `licensing@invariantforge.net`\n",
    "\n",
    "## Authoring discipline\n",
    "\n",
    "- All numerical values shown are verbatim test stdout from the engine. **No synthetic data, ever.**\n",
    "- Every detector is a deterministic statistical or structural function with a literature citation. **No neural network, no learned model, no training data.**\n",
    "- Every motif is a hand-curated rule anchored to **IEEE 24765 / Avizienis-Laprie-Randell** vocabulary.\n",
    "- The reference Rust implementation is `#![no_std]` + zero-allocation + `#![forbid(unsafe_code)]` with zero runtime Cargo dependencies in the core; it compiles for ARM Cortex-M / RISC-V embedded targets.\n",
    "- Theorem 9 (deterministic replay) holds across every fixture; this notebook re-fires `verify_deterministic_replay` per evaluation.\n",
    "\n",
    "---\n",
    "\n",
    "## Math summary \u2014 the engine in one page\n",
    "\n",
    "**Residual signature** at window $k$, signal $s$:\n",
    "\n",
    "$$\\sigma(k) = (\\|r(k)\\|,\\ \\dot{r}(k),\\ \\ddot{r}(k))$$\n",
    "\n",
    "where $r(k) = x(k) - \\hat{x}(k)$ is the residual, $\\dot{r}$ is finite-difference drift over a fixed window $W$, $\\ddot{r}$ is slew (first difference of drift).\n",
    "\n",
    "**Admissibility envelope:** $\\mathcal{E}(k) = \\{r : \\|r\\| \\leq \\rho(k)\\}$ where $\\rho$ is operator-defined (SLO/SLA target or healthy-window baseline).\n",
    "\n",
    "**Grammar state** $G(k,s) \\in \\{\\text{Admissible},\\ \\text{Boundary},\\ \\text{Violation}\\}$ is a deterministic function of $\\sigma(k,s)$ and $\\rho$, with hysteresis confirmation (n_confirm windows).\n",
    "\n",
    "**Detector ensemble:** 205 deterministic detectors organised across 27 mathematical axes (Tiers A\u2013U + EXTRA + V/X/Y/Z/AA). Each detector is a pure function of the residual matrix; outputs the per-(window, signal) firing pattern.\n",
    "\n",
    "**Routed Evidence Principle:** for each motif $m$ in the bank, an affinity bitmask $\\alpha(m)$ over the 27 tiers selects which detectors' evidence enters the motif's score. The motif-conditional consensus at cell $(w,s)$ is\n",
    "\n",
    "$$c_m(w,s) = \\mathrm{popcount}\\bigl(\\alpha(m) \\wedge \\phi(w,s)\\bigr)$$\n",
    "\n",
    "where $\\phi(w,s)$ is the per-cell tier-fired bitmask.\n",
    "\n",
    "**Episode aggregation** collapses contiguous non-Admissible windows into typed structural episodes (`DebugEpisode`), each with a `StructuralSignature` (peak slew, duration, contributing signals, dominant drift direction).\n",
    "\n",
    "**9-axis bank-aware fusion:** provenance gate, margin gate, tier-affinity scoring, zero-tier-firing filter, adaptive margin gate, confuser-boundary adjudication, structural disambiguator boost, tier-level primary witness gate, per-detector named witness gate. Each axis is a single `FusionConfig` flag.\n",
    "\n",
    "**Operator evidence packet** per typed episode: top motif, runner-up, declared confuser, three margins, tier consensus factor, disambiguator boost, witness-gate flags, root-cause attribution.\n",
    "\n",
    "---\n",
    "\n",
    "## Code tour \u2014 public API\n",
    "\n",
    "```rust\n",
    "// Engine creation under paper-lock parameters\n",
    "let engine = DsfbDebugEngine::<32, 64>::paper_lock()?;\n",
    "\n",
    "// Real-data evaluation against a vendored fixture\n",
    "let eval = evaluate_real_dataset(&engine, &MANIFEST_TADBENCH_F11, F11_BYTES)?;\n",
    "\n",
    "// Multi-detector fusion (9-axis bank-aware)\n",
    "let metrics = run_fusion_evaluation(\n",
    "    &engine, &data, num_signals, num_windows,\n",
    "    healthy_window_end, &fault_labels,\n",
    "    &FusionConfig::ALL_DEFAULT, fixture_name,\n",
    ")?;\n",
    "\n",
    "// Per-episode evidence packet\n",
    "let bank = HeuristicsBank::<64>::with_canonical_motifs();\n",
    "let confidence = bank.match_episode_with_consensus(&episode, /* ... */);\n",
    "```\n",
    "\n",
    "All entry points accept `&[T]` only; the type system enforces non-intrusion at compile time."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 1 \u2014 Setup\n",
    "\n",
    "Install rustup (1.85+ for the `demo` feature), clone the crate, and build the demo binary. The core crate remains MSRV-pinned; this notebook explicitly selects the newer toolchain for the optional demo figure stack."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Install rustup (~30 seconds on Colab)\n",
    "!curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain 1.85.1 -q\n",
    "import os\n",
    "os.environ['PATH'] = os.path.expanduser('~/.cargo/bin') + ':' + os.environ['PATH']\n",
    "os.environ['RUSTUP_TOOLCHAIN'] = '1.85.1'\n",
    "!rustc --version\n",
    "!cargo --version"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Clone the dsfb repository (or use a local path if running outside Colab)\n",
    "import os\n",
    "if not os.path.isdir('dsfb'):\n",
    "    !git clone https://github.com/infinityabundance/dsfb.git\n",
    "%cd dsfb/crates/dsfb-debug\n",
    "!ls data/fixtures/ | wc -l    # expect 12 fixtures"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Build the optional demo binary with the Colab-selected toolchain.\n",
    "# The crate's rust-toolchain.toml pins the core MSRV; RUSTUP_TOOLCHAIN keeps demo-only plotting deps on rustc 1.85.1.\n",
    "import os, subprocess\n",
    "env = dict(os.environ)\n",
    "env['RUSTUP_TOOLCHAIN'] = '1.85.1'\n",
    "build = subprocess.run(['cargo', 'build', '--release', '--features', 'demo', '--bin', 'dsfb-debug-demo'],\n",
    "                       capture_output=True, text=True, env=env)\n",
    "print(build.stdout[-4000:])\n",
    "if build.returncode != 0:\n",
    "    print('STDERR:', build.stderr[-4000:])\n",
    "    raise RuntimeError(f'dsfb-debug demo build failed with exit code {build.returncode}')\n",
    "\n",
    "# Install Python figure-quality packages (publication style + perceptually-uniform colormaps)\n",
    "!pip install --quiet --break-system-packages scienceplots cmcrameri 2>&1 | tail -3"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 2 \u2014 Run the demo binary\n",
    "\n",
    "Produces 126 PNG figures + 12 JSON metric blocks + Markdown report + zip."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import glob, os, subprocess\n",
    "\n",
    "env = dict(os.environ)\n",
    "env['RUSTUP_TOOLCHAIN'] = '1.85.1'\n",
    "print('rustc:', subprocess.check_output(['rustc', '--version'], env=env, text=True).strip())\n",
    "\n",
    "result = subprocess.run(['cargo', 'run', '--release', '--features', 'demo', '--bin', 'dsfb-debug-demo'],\n",
    "                       capture_output=True, text=True, env=env)\n",
    "print(result.stdout[-2000:])\n",
    "if result.returncode != 0:\n",
    "    print('STDERR:', result.stderr[-4000:])\n",
    "    raise RuntimeError(f'dsfb-debug demo binary failed with exit code {result.returncode}')\n",
    "\n",
    "# Locate the run folder + zip path\n",
    "run_dirs = [d for d in sorted(glob.glob('output-dsfb-debug/dsfb-debug-*')) if not d.endswith('.zip')]\n",
    "if not run_dirs:\n",
    "    raise FileNotFoundError('Demo completed without creating output-dsfb-debug/dsfb-debug-*')\n",
    "run_dir = run_dirs[-1]\n",
    "zip_path = run_dir + '.zip'\n",
    "print('Run folder:', run_dir)\n",
    "print('Zip:', zip_path)\n",
    "\n",
    "# Run the Python publication-quality figure pipeline\n",
    "print('\\n[python] Rendering publication-quality figures via matplotlib\u2026')\n",
    "result2 = subprocess.run(['python3', '-m', 'tools.figures.render', run_dir],\n",
    "                         capture_output=True, text=True)\n",
    "print(result2.stdout[-1500:])\n",
    "if result2.returncode != 0:\n",
    "    print('STDERR:', result2.stderr[-1000:])\n",
    "figs_py_dir = os.path.join(run_dir, 'figures-py')\n",
    "print('Python-rendered figures:', figs_py_dir)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 3 \u2014 Display architecture + cross-fixture figures inline"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from IPython.display import Image, display, Markdown\n",
    "import os\n",
    "\n",
    "# Display architecture / infrastructure figures (publication-quality, matplotlib-rendered)\n",
    "for label, fname in [\n",
    "    ('Figure A1 \u2014 How DSFB-Debug differs from learned anomaly detection', '00_architecture/01_ml_vs_dsfb.png'),\n",
    "    ('Figure A2 \u2014 Detector ensemble (205 detectors / 27 axes / 8 mathematical families)', '00_architecture/02_tier_breakdown.png'),\n",
    "    ('Figure A3 \u2014 32-motif \u00d7 27-axis affinity matrix (Routed Evidence Principle)', '00_architecture/03_motif_affinity.png'),\n",
    "    ('Figure C1 \u2014 F-11 fusion sweep: clean-window FP rate vs min_consensus N', 'cross_fixture/01_fusion_sweep.png'),\n",
    "    ('Figure C2 \u2014 Cross-fixture RSCR forest plot (12 fixtures)', 'cross_fixture/02_rscr_forest.png'),\n",
    "    ('Figure C3 \u2014 Cross-fixture per-tier firing pattern', 'cross_fixture/03_tier_firing.png'),\n",
    "    ('Figure O1 \u2014 F-11 forensic evidence packets (3-card panel)', 'operator/01_evidence_cards.png'),\n",
    "    ('Figure O2 \u2014 F-11 episode 0 confuser-pair adjudication (Phase 5.6 gate)', 'operator/02_confuser_adjudication.png'),\n",
    "    ('Figure O3 \u2014 Anti-Hallucination Ladder progression on F-11', 'operator/03_anti_hallucination_ladder.png'),\n",
    "    ('Figure S1 \u2014 Trace Event Collapse on F-11', 'special/01_trace_event_collapse.png'),\n",
    "    ('Figure S2 \u2014 Theorem 9 deterministic-replay verification', 'special/02_theorem9_verification.png'),\n",
    "]:\n",
    "    p = os.path.join(figs_py_dir, fname)\n",
    "    if os.path.isfile(p):\n",
    "        display(Markdown(f'### {label}'))\n",
    "        display(Image(p))\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 4 \u2014 Per-fixture figures + verbatim JSON metrics\n",
    "\n",
    "12 fixtures \u00d7 10 figures = 120 figures inline, plus the JSON metric block per fixture."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import json, glob\n",
    "fixture_dirs = sorted([d for d in glob.glob(os.path.join(figs_py_dir, '[0-9]*'))\n",
    "                        if os.path.isdir(d)])\n",
    "\n",
    "for d in fixture_dirs:\n",
    "    label = os.path.basename(d)\n",
    "    display(Markdown(f'## {label}'))\n",
    "    json_path = os.path.join(run_dir, 'results', f'{label}.json')\n",
    "    if os.path.isfile(json_path):\n",
    "        with open(json_path) as f:\n",
    "            data = json.load(f)\n",
    "        # Just show the headline metrics (full JSON is multi-MB)\n",
    "        head = {\n",
    "            'manifest_name': data.get('manifest_name'),\n",
    "            'episode_count': data.get('episode_count'),\n",
    "            'metrics': data.get('metrics'),\n",
    "            'fusion': data.get('fusion'),\n",
    "        }\n",
    "        display(Markdown('**Verbatim metric block:**'))\n",
    "        display(Markdown('```json\\n' + json.dumps(head, indent=2) + '\\n```'))\n",
    "    for fn in ['01_residual_smallmult.png', '02_summary_card.png', '03_episode_evidence.png']:\n",
    "        p = os.path.join(d, fn)\n",
    "        if os.path.isfile(p):\n",
    "            display(Image(p))\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 5 \u2014 Assemble PDF report (via matplotlib.PdfPages)\n",
    "\n",
    "Combines all 126 PNG figures into a single PDF with one figure per page, captioned."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from matplotlib.backends.backend_pdf import PdfPages\n",
    "import matplotlib.pyplot as plt\n",
    "import matplotlib.image as mpimg\n",
    "import glob, os\n",
    "\n",
    "pdf_path = os.path.join(run_dir, 'report.pdf')\n",
    "all_figs = []\n",
    "all_figs += sorted(glob.glob(os.path.join(figs_py_dir, '00_architecture', '*.png')))\n",
    "all_figs += sorted(glob.glob(os.path.join(figs_py_dir, 'cross_fixture', '*.png')))\n",
    "all_figs += sorted(glob.glob(os.path.join(figs_py_dir, 'operator', '*.png')))\n",
    "all_figs += sorted(glob.glob(os.path.join(figs_py_dir, 'special', '*.png')))\n",
    "for d in sorted([d for d in glob.glob(os.path.join(figs_py_dir, '[0-9]*'))\n",
    "                  if os.path.isdir(d)]):\n",
    "    all_figs += sorted(glob.glob(os.path.join(d, '*.png')))\n",
    "\n",
    "with PdfPages(pdf_path) as pdf:\n",
    "    # Cover page\n",
    "    fig = plt.figure(figsize=(8.27, 11.69))   # A4 portrait\n",
    "    fig.text(0.05, 0.92, 'DSFB-Debug \u2014 Demo Report', fontsize=22, weight='bold')\n",
    "    fig.text(0.05, 0.87, 'Deterministic detector-field semiotics with routed forensic witness-field fusion', fontsize=11)\n",
    "    fig.text(0.05, 0.83, 'Twelve real-bytes vendored fixtures across nine distinct upstream public datasets.', fontsize=10)\n",
    "    fig.text(0.05, 0.75, 'Prior Art Notice (35 U.S.C. \u00a7102):', fontsize=10, weight='bold')\n",
    "    fig.text(0.05, 0.72, 'Deposited via Zenodo + GitHub at the run-id timestamp.', fontsize=9)\n",
    "    fig.text(0.05, 0.66, 'IP Notice:', fontsize=10, weight='bold')\n",
    "    fig.text(0.05, 0.59, 'Background IP of Invariant Forge LLC. Reference implementation Apache-2.0;\\n'\n",
    "                          'commercial deployment requires written license.\\n'\n",
    "                          'Contact: licensing@invariantforge.net', fontsize=9)\n",
    "    fig.text(0.05, 0.50, 'Authoring discipline:', fontsize=10, weight='bold')\n",
    "    fig.text(0.05, 0.43, 'All numbers are verbatim test stdout from the engine. No synthetic data.\\n'\n",
    "                          'Theorem 9 (deterministic replay) holds across every fixture.\\n'\n",
    "                          'ML-free, no_std, zero runtime Cargo dependencies in the core, edge-deployable.', fontsize=9)\n",
    "    plt.axis('off')\n",
    "    pdf.savefig(fig, bbox_inches='tight'); plt.close(fig)\n",
    "\n",
    "    for fp in all_figs:\n",
    "        fig = plt.figure(figsize=(8.27, 11.69))   # A4 portrait\n",
    "        img = mpimg.imread(fp)\n",
    "        ax = fig.add_axes([0.05, 0.10, 0.90, 0.80])\n",
    "        ax.imshow(img)\n",
    "        ax.axis('off')\n",
    "        rel = os.path.relpath(fp, run_dir)\n",
    "        fig.text(0.05, 0.96, rel, fontsize=8, family='monospace')\n",
    "        pdf.savefig(fig, bbox_inches='tight'); plt.close(fig)\n",
    "\n",
    "print(f'OK \u2014 wrote {pdf_path}, {len(all_figs)+1} pages')\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 6 \u2014 Re-zip with PDF included, then download"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import zipfile, os\n",
    "rezip = os.path.join('output-dsfb-debug', os.path.basename(run_dir) + '-with-pdf.zip')\n",
    "with zipfile.ZipFile(rezip, 'w', zipfile.ZIP_DEFLATED) as z:\n",
    "    for root, dirs, files in os.walk(run_dir):\n",
    "        for f in files:\n",
    "            p = os.path.join(root, f)\n",
    "            arc = os.path.relpath(p, os.path.dirname(run_dir))\n",
    "            z.write(p, arc)\n",
    "print(f'Zip ready: {rezip}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 7 \u2014 Calibration & Sensitivity walk-through (Phase \u03b7.7, Session 18)\n",
    "\n",
    "The next two cells run the Session-18 sensitivity-sweep + per-axis-ablation\n",
    "audits over the F-11 fixture and render the response curves. These are\n",
    "the operator-side calibration tools: an on-call engineer pointing\n",
    "DSFB-Debug at their own trace data can re-run these cells with their\n",
    "own fixture path to see how each parameter and each fusion axis behaves\n",
    "on their data.\n",
    "\n",
    "Source artefacts (already populated by `cargo test --features \"std paper-lock\"`):\n",
    "- `docs/audit/sensitivity_sweep.md` \u2014 5 parameters \u00d7 5 values response table\n",
    "- `docs/audit/axis_ablation.md` \u2014 9-axis ladder ablation impact\n",
    "- `docs/audit/bootstrap_ci.md` \u2014 95% CI on cross-fixture aggregates\n",
    "- `docs/audit/kfold_cv.md` \u2014 K-fold CV (K=4) per-fold + cross-fold aggregate\n",
    "- `docs/audit/detector_subset_opt.md` \u2014 minimal-sufficient-subset trajectory\n",
    "- `docs/benchmarks.md` \u2014 wall-clock + ns/cell timing\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Phase \u03b7.7a \u2014 Render sensitivity-sweep response curves.\n",
    "#\n",
    "# Reads `docs/audit/sensitivity_sweep.md`, parses the 5 per-parameter\n",
    "# tables, plots typed-confirmed and FP rate vs each parameter on a\n",
    "# 2\u00d73 grid. Operator can correlate which parameters matter most\n",
    "# for their site.\n",
    "import re\n",
    "from pathlib import Path\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "audit_path = Path(\"crates/dsfb-debug/docs/audit/sensitivity_sweep.md\")\n",
    "if not audit_path.exists():\n",
    "    audit_path = Path(\"docs/audit/sensitivity_sweep.md\")\n",
    "\n",
    "if audit_path.exists():\n",
    "    text = audit_path.read_text()\n",
    "    # Parse each per-parameter table.\n",
    "    sections = re.split(r'\\n## ([A-Za-z0-9_ ()-]+)\\n', text)\n",
    "    fig, axes = plt.subplots(2, 3, figsize=(13, 7), constrained_layout=True)\n",
    "    axes = axes.flatten()\n",
    "    plotted = 0\n",
    "    for i in range(1, len(sections), 2):\n",
    "        param = sections[i].strip()\n",
    "        body = sections[i+1] if i+1 < len(sections) else \"\"\n",
    "        rows = re.findall(r'\\| ([0-9.]+) \\| ([0-9.]+) \\| ([0-9.]+) \\| ([0-9.]+) \\| ([0-9]+) \\|', body)\n",
    "        if not rows or plotted >= 6:\n",
    "            continue\n",
    "        xs = [float(r[0]) for r in rows]\n",
    "        rscr = [float(r[1]) for r in rows]\n",
    "        fp = [float(r[2]) for r in rows]\n",
    "        recall = [float(r[3]) for r in rows]\n",
    "        typed = [int(r[4]) for r in rows]\n",
    "        ax = axes[plotted]\n",
    "        ax2 = ax.twinx()\n",
    "        ax.plot(xs, fp, 'o-', label='FP rate', color='#9A031E')\n",
    "        ax.plot(xs, recall, 's-', label='Recall', color='#0F4C5C')\n",
    "        ax2.plot(xs, typed, 'd--', label='Typed', color='#E36414')\n",
    "        ax.set_xlabel(param)\n",
    "        ax.set_ylabel('FP rate / Recall')\n",
    "        ax2.set_ylabel('Typed-confirmed')\n",
    "        ax.set_title(param, fontsize=10)\n",
    "        ax.legend(loc='upper left', fontsize=8)\n",
    "        ax2.legend(loc='upper right', fontsize=8)\n",
    "        ax.grid(alpha=0.3)\n",
    "        plotted += 1\n",
    "    for k in range(plotted, 6):\n",
    "        axes[k].axis('off')\n",
    "    fig.suptitle('Phase \u03b7.3 \u2014 Sensitivity sweep response curves\\n(verbatim from cargo test, Session 18)', fontweight='bold')\n",
    "    plt.show()\n",
    "else:\n",
    "    print(f\"Sensitivity audit not found at {audit_path}; run `cargo test --features \\\"std paper-lock\\\" --test sensitivity_sweep -- --nocapture` first.\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Phase \u03b7.7b \u2014 Render per-axis ablation waterfall + bootstrap CI summary.\n",
    "#\n",
    "# Reads `docs/audit/axis_ablation.md` + `docs/audit/bootstrap_ci.md` and\n",
    "# plots: (a) per-axis \u0394typed waterfall, (b) bootstrap CI bars on the four\n",
    "# cross-fixture metrics. Operator sees which fusion axes carry empirical\n",
    "# load AND the honest uncertainty band on the cross-fixture aggregate.\n",
    "import re\n",
    "from pathlib import Path\n",
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "\n",
    "ablation_path = Path(\"crates/dsfb-debug/docs/audit/axis_ablation.md\")\n",
    "if not ablation_path.exists():\n",
    "    ablation_path = Path(\"docs/audit/axis_ablation.md\")\n",
    "boot_path = Path(\"crates/dsfb-debug/docs/audit/bootstrap_ci.md\")\n",
    "if not boot_path.exists():\n",
    "    boot_path = Path(\"docs/audit/bootstrap_ci.md\")\n",
    "\n",
    "fig, axes = plt.subplots(1, 2, figsize=(14, 5), constrained_layout=True)\n",
    "\n",
    "# (a) Per-axis \u0394typed waterfall.\n",
    "ax = axes[0]\n",
    "if ablation_path.exists():\n",
    "    text = ablation_path.read_text()\n",
    "    rows = re.findall(r'\\| (\\d+\\. [^|]+?) \\| `([^`]+)` \\| ([0-9.]+) \\| ([0-9.]+) \\| ([0-9.]+) \\| (\\d+) \\| ([+-]?\\d+) \\|', text)\n",
    "    if rows:\n",
    "        labels = [r[0].strip() for r in rows]\n",
    "        deltas = [int(r[6]) for r in rows]\n",
    "        colors = ['#2A9D8F' if d == 0 else ('#9A031E' if d > 0 else '#E36414') for d in deltas]\n",
    "        ax.barh(range(len(labels)), deltas, color=colors)\n",
    "        ax.set_yticks(range(len(labels)))\n",
    "        ax.set_yticklabels(labels, fontsize=9)\n",
    "        ax.axvline(0, color='black', linewidth=0.8)\n",
    "        ax.set_xlabel('\u0394 typed-confirmed (axis disabled vs baseline)')\n",
    "        ax.set_title('Phase \u03b7.4 \u2014 Per-axis ablation impact\\n(green = no impact / red = adds typed eps when off / orange = removes when off)', fontsize=10)\n",
    "        ax.grid(axis='x', alpha=0.3)\n",
    "    else:\n",
    "        ax.text(0.5, 0.5, 'Axis ablation: re-run `cargo test --test axis_ablation`', ha='center')\n",
    "else:\n",
    "    ax.text(0.5, 0.5, 'Axis ablation audit not found', ha='center')\n",
    "\n",
    "# (b) Bootstrap CI bars.\n",
    "ax = axes[1]\n",
    "if boot_path.exists():\n",
    "    text = boot_path.read_text()\n",
    "    rows = re.findall(r'\\| ([A-Za-z][^|]+?) \\| ([0-9.]+) \\| ([0-9.]+) \\| ([0-9.]+) \\| ([0-9.]+) \\|', text)\n",
    "    if rows:\n",
    "        labels = [r[0].strip() for r in rows]\n",
    "        points = [float(r[1]) for r in rows]\n",
    "        lowers = [float(r[2]) for r in rows]\n",
    "        uppers = [float(r[3]) for r in rows]\n",
    "        ys = np.arange(len(labels))\n",
    "        for i, (p, lo, hi) in enumerate(zip(points, lowers, uppers)):\n",
    "            # Plot CI bar normalized to point estimate (so bars share a scale).\n",
    "            if abs(p) > 1e-9:\n",
    "                ax.barh(i, hi - lo, left=lo, color='#7C7E80', alpha=0.4, height=0.6)\n",
    "                ax.scatter([p], [i], color='#9A031E', zorder=5, s=80, marker='|')\n",
    "        ax.set_yticks(ys)\n",
    "        ax.set_yticklabels(labels, fontsize=9)\n",
    "        ax.set_xlabel('Bootstrap value (point estimate marked with |)')\n",
    "        ax.set_title('Phase \u03b7.1 \u2014 Bootstrap 95% CI\\n(grey bar = CI, red mark = point estimate)', fontsize=10)\n",
    "        ax.grid(axis='x', alpha=0.3)\n",
    "    else:\n",
    "        ax.text(0.5, 0.5, 'Bootstrap CI: re-run LO-CV', ha='center')\n",
    "else:\n",
    "    ax.text(0.5, 0.5, 'Bootstrap CI audit not found', ha='center')\n",
    "\n",
    "plt.show()\n",
    "print(\"Operator-side calibration audits rendered. The audit ledgers\")\n",
    "print(\"under docs/audit/ are the source-of-truth for re-running on\")\n",
    "print(\"site-specific fixtures: edit data/fixtures/ + re-run cargo test.\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Download the zip (Colab only)\n",
    "try:\n",
    "    from google.colab import files\n",
    "    files.download(rezip)\n",
    "except ImportError:\n",
    "    print(f'Not in Colab. Zip is at: {os.path.abspath(rezip)}')"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python",
   "version": "3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}