# v1.9.3 calibration report

**Date:** 2026-05-09
**Release:** v1.9.3 (Bayesian-network topology surgery — definitional split)
**Network topology version:** v1.9.3 (frozen 2026-05-09; 9 nodes)
**Predecessor report:** [v1.9.0](v1.9-validation-summary.md)

## Headline

The v1.9.3 surgery resolves the calibration weak spot identified in
v1.9.0. The single `email_security_strong` node — which agreed with
the deterministic pipeline on only **52.6%** of high-confidence
posteriors — has been split into two nodes that each match their
definition. Spot-check agreement on the new topology is **100%** on
the v1.9.3 spot corpus.

| Metric | v1.9.0 | v1.9.3 |
|---|---|---|
| Spot-check overall agreement (posterior ≥ 0.85, non-sparse) | 88.2% (276/313) | **100%** (8/8 on spot corpus) |
| `email_security_strong` agreement | 52.6% | **resolved (node removed)** |
| `email_security_modern_provider` (new) | — | 100% sparse → not in spot-check; pure structural propagation as designed |
| `email_security_policy_enforcing` (new) | — | **100%** (2/2) |
| `federated_identity` agreement | 100% (M365 path only) | **100%** + new GWS path active |
| Network nodes | 8 | 9 |

The headline number on the *full* v1.9.0 corpus is not repeated here:
this is a topology-surgery patch, not a calibration recalibration. The
spot corpus (2 fictional-acceptable real domains, gitignored under
`validation/corpus-private/v1.9.3-spot.txt`) exists to confirm the
surgery does not regress the eight nodes that were already 100% in
v1.9.0 and to validate that the two replacement nodes fire as
designed. The full v1.9-validation-summary corpus run will be
re-executed at the v1.9.4 hardened-adversarial milestone.

## What changed

See [docs/correlation.md §4.8.9](../docs/correlation.md) for the full
rationale. In summary:

1. **`email_security_strong` removed.** The v1.9.0 node parameterized
   its CPT on modern-mail-provider presence (M365 / GWS / gateway) but
   its evidence bindings tested policy enforcement signals (DMARC /
   DKIM / SPF / MTA-STS). These are different claims. The 52.6%
   disagreement was definitional, not numerical.
2. **`email_security_modern_provider` added.** Provider-presence claim.
   Parents: `[m365_tenant, google_workspace_tenant, email_gateway_present]`.
   No evidence bindings — pure CPT propagation; provider presence is
   fully observable through the parent slugs.
3. **`email_security_policy_enforcing` added.** Policy-enforcement
   claim. Root node, prior 0.25. Evidence bindings: `dmarc_reject`,
   `dmarc_quarantine`, `mta_sts_enforce`, `dkim_present`, `spf_strict`.
   Provider-independent — a Zoho or Fastmail tenant with a strict
   DMARC reject fires this node identically to an M365 one.
4. **`federated_identity` parents expanded.** From `[m365_tenant]`
   to `[m365_tenant, google_workspace_tenant]`. The v1.9.0 single-
   parent network systematically under-attributed federation when
   the path didn't go through M365 (Okta + GWS, Auth0 + custom IdP,
   standalone SAML). The v1.9.3 four-entry CPT preserves the M365
   path numbers (0.45 / 0.08) and seeds the GWS path values
   (0.50 both, 0.35 GWS-only); the v1.9.4 hardened-adversarial
   corpus run will tighten the GWS-path numbers.

## Spot-check results

Spot corpus: 2 domains, gitignored. Anonymized aggregates only.

| Node | high-conf count | agreement | sparse rate | Notes |
|---|---|---|---|---|
| `m365_tenant` | 2 | **100%** | 0% | Both spot domains exercise M365 evidence. |
| `google_workspace_tenant` | 1 | **100%** | 50% | One domain GWS-tenant, one M365-only. |
| `federated_identity` | 1 | **100%** | 50% | New GWS-parent path active; further validation in v1.9.4. |
| `okta_idp` | 0 | n/a | 100% | Neither spot domain is Okta-fronted. |
| `email_gateway_present` | 0 | n/a | 100% | Neither spot domain shows a third-party gateway. |
| `email_security_modern_provider` | 0 (in spot-check) | n/a | 100% | **By design**: no evidence bindings; pure CPT propagation. The spot-check only counts non-sparse posteriors. |
| `email_security_policy_enforcing` | 2 | **100%** | 0% | Both spot domains publish enforcing DMARC. The v1.9.0 node would have disagreed on this claim (52.6% rate); the v1.9.3 split agrees. |
| `cdn_fronting` | 2 | **100%** | 0% | Both spot domains CDN-fronted. |
| `aws_hosting` | 0 | n/a | 100% | Neither spot domain is AWS-evident. |

Multi-signal correlation depth on the spot corpus: 2/2 domains have
> 1 evidence binding firing across nodes (100%). Conflict count: 0
across both domains.

## Sensitivity (publicly reproducible)

`tests/test_bayesian_sensitivity.py` perturbs each CPT entry and each
root prior by ±0.10 across canonical evidence patterns. Re-run on the
v1.9.3 network:

| Statistic | v1.9.0 | v1.9.3 |
|---|---|---|
| Median posterior shift | 0.019 | **0.018** |
| 95th percentile | 0.109 | **0.100** |
| Maximum observed | 0.139 | **0.106** |

Sensitivity is slightly tighter than v1.9.0 — the split removed the
most-sensitive node (`email_security_strong` had the largest CPT
under perturbation due to its three parents and five evidence
bindings). The new `email_security_modern_provider` has three parents
but no evidence bindings; the new `email_security_policy_enforcing`
has zero parents and five evidence bindings. Each is a simpler factor
than the conflated v1.9.0 node.

## Synthetic calibration (publicly reproducible)

`validation/synthetic_calibration.py --samples 20000 --seed 1729` on
the v1.9.3 network:

| Metric | v1.9.0 | v1.9.3 |
|---|---|---|
| Mean Brier (marginal) | 0.134 | **0.129** |
| Sample-weighted ECE (marginal) | 0.247 | **0.232** |
| Mean Brier (conditional) | 0.164 | **0.150** |
| Sample-weighted ECE (conditional) | **0.160** | **0.163** |

Conditional ECE is essentially unchanged (0.160 → 0.163; within
sample-noise of the same value). Marginal ECE improved (0.247 →
0.232) because removing the v1.9.0 node's three-way CPT eliminates
one source of marginal-regime overconfidence. The conditional figure
is the calibration claim the model makes; the synthetic test
continues to surface `aws_hosting` as the residual weak spot for
v1.9.5 stability dispositions.

## Tests passing

```
2102 passed (1938 v1.9.0 + 164 v1.9.x cumulative additions)
ruff: clean on recon_tool/ + tests/
pyright: 0 errors on recon_tool/
coverage: 83.25% (≥ 80% gate)
```

Includes 11 new tests in
`tests/test_bayesian_v193_topology.py` pinning the surgery against
silent topology regression.

## What this run does NOT validate

1. **Full corpus regression.** The 91-domain v1.9.0 corpus has not
   been re-run on the v1.9.3 network. Re-running is scheduled for
   the v1.9.4 hardened-adversarial milestone; the spot corpus exists
   to confirm the surgery is sound, not to certify cross-vertical
   calibration.
2. **GWS-federated CPT numbers.** The new `federated_identity` CPT
   entries for the GWS-only and both-providers cases (0.35, 0.50)
   are seeded from defensible priors. They are tunable, but per the
   no-learned-weights invariant + v1.9.6 CPT-change discipline, they
   will only be revised based on a topology-questioning analysis of
   the v1.9.4 corpus run, not via empirical-Bayes fitting.
3. **`aws_hosting` calibration.** Still flagged by the synthetic ECE
   test as the network's residual weak spot. Disposition decided in
   v1.9.5 per the per-node stability criteria.

## Reproducing the public components

```bash
pytest tests/test_bayesian_v193_topology.py -v
pytest tests/test_bayesian_sensitivity.py -s
python validation/synthetic_calibration.py --samples 20000 --seed 1729
```

Spot-check on a private corpus (the spot file under
`validation/corpus-private/` is gitignored):

```bash
python -m recon_tool.cli batch validation/corpus-private/v1.9.3-spot.txt \
       --ndjson --fusion --no-ct --concurrency 2 \
       > validation/runs-private/v1.9.3-calibration/results.ndjson
python validation/analyze_v19_calibration.py \
       --input validation/runs-private/v1.9.3-calibration/results.ndjson
```

## Disposition for v2.0

The v1.9.3 surgery moves two new nodes into the v2.0 schema-lock
disposition table (docs/roadmap.md):

| Field | Disposition |
|---|---|
| `posterior_observations[].name == "email_security_modern_provider"` | Promote to stable in v2.0 if v1.9.4 + v1.9.5 stability criteria pass. |
| `posterior_observations[].name == "email_security_policy_enforcing"` | Promote to stable in v2.0 if v1.9.4 + v1.9.5 stability criteria pass. |
| `posterior_observations[].name == "email_security_strong"` | **Removed in v1.9.3**; consumers pinning the v1.9.0 name must update. The EXPERIMENTAL label on the whole `--fusion` surface (per v1.9.0 release notes) accommodates this; no separate deprecation patch needed because the node's calibration was the explicit weak spot v1.9.x was committed to fixing. |
