tenseleyflow/sway / 9e97f1b

Browse files

core/result: ProbeResult.ci_95 + safe_finalize passthrough (null-safe)

Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
9e97f1bd59a7c30bd4702a68b33ff3c8900d2e69
Parents
fd975b7
Tree
ce53d6c

1 changed file

StatusFile+-
M src/dlm_sway/core/result.py 17 0
src/dlm_sway/core/result.pymodified
@@ -62,6 +62,15 @@ class ProbeResult:
6262
         One-line diagnostic. Surfaces in the terminal report.
6363
     duration_s:
6464
         Wall time to execute.
65
+    ci_95:
66
+        95% percentile-bootstrap confidence interval on :attr:`raw`.
67
+        Populated by aggregating probes (``delta_kl`` over N prompts,
68
+        ``calibration_drift`` over pack items, etc.) via
69
+        :func:`dlm_sway.core.stats.bootstrap_ci`. ``None`` when the
70
+        probe doesn't aggregate (``adapter_ablation``,
71
+        ``style_fingerprint``), when the sample count is too low to
72
+        bootstrap meaningfully, or when the raw value isn't finite.
73
+        Report surfaces it inline as ``raw [lo–hi]``.
6574
     """
6675
 
6776
     name: str
@@ -75,6 +84,7 @@ class ProbeResult:
7584
     evidence: dict[str, Any] = field(default_factory=dict)
7685
     message: str = ""
7786
     duration_s: float = 0.0
87
+    ci_95: tuple[float, float] | None = None
7888
 
7989
 
8090
 @dataclass(frozen=True, slots=True)
@@ -181,6 +191,7 @@ def safe_finalize(
181191
     evidence: dict[str, Any] | None = None,
182192
     message: str = "",
183193
     duration_s: float = 0.0,
194
+    ci_95: tuple[float, float] | None = None,
184195
     critical_fields: tuple[str, ...] = ("raw",),
185196
 ) -> ProbeResult:
186197
     """Build a :class:`ProbeResult` with defense against non-finite metrics.
@@ -252,6 +263,11 @@ def safe_finalize(
252263
         for fname in non_finite:
253264
             numeric_kwargs[fname] = None
254265
 
266
+    # ``ci_95`` is only attached when ``raw`` survived the
267
+    # defensive-null sweep — a CI bracketing a nulled-out point
268
+    # estimate would mislead more than it informs.
269
+    final_ci_95 = ci_95 if numeric_kwargs["raw"] is not None else None
270
+
255271
     return ProbeResult(
256272
         name=name,
257273
         kind=kind,
@@ -264,4 +280,5 @@ def safe_finalize(
264280
         evidence=ev,
265281
         message=message,
266282
         duration_s=duration_s,
283
+        ci_95=final_ci_95,
267284
     )