# v1.9.10 — stratified-corpus pre-lock validation

**Release:** v1.9.10
**Date:** 2026-05-15
**Scope:** Pre-v2.0 stratified validation. Six cloud strata each
with ~10 publicly-reproducible synthetic fixtures (60 stratum
fixtures + 19 base v1.9.9 fixtures = 79 total). Bayesian network
re-validation after the v1.9.9 evidence-distribution shift. No
engine code changes.

## What shipped

1. **Stratified synthetic corpus.** Extended the v1.9.9 19-fixture
   corpus to 79 fixtures, with 60 of them tagged by stratum:
   - **GCP** (10 fixtures): pure-native, with-Okta-SSO, with-security-stack,
     Apigee-heavy, Firebase-only, minimal-hardened, dual-with-AWS,
     data-pipeline, Cloud-Run-modern, IdP-heavy.
   - **Azure non-O365** (10): native-without-M365, with-Okta-SSO,
     Static-Web-Apps, with-Proofpoint, govcloud, minimal,
     with-Databricks, dual-cloud, API-management, IdP-managed.
   - **Oracle** (10): Fusion-apps, OCI-only, with-legacy,
     APEX-dev, minimal, dual-with-AWS, with-security-stack,
     global-multi-region, IdP-with-Okta, APEX-only.
   - **Alibaba** (10): native, global, with-security, minimal,
     dual-with-AWS, ecommerce, with-OSS, with-APIM, minimal-CDN,
     with-DingTalk-proxy.
   - **PaaS / Vercel / Netlify** (10): Vercel-full, Netlify-jamstack,
     Vercel-with-Auth0, Railway-app, Render-app, Fly.io-distributed,
     Vercel-minimal, Netlify-minimal, Cloudflare-Pages, multi-PaaS.
   - **SSE / SASE** (10): Zscaler-fronted, Netskope-fronted,
     Cloudflare-One, Prisma-Access, Cato-SASE, minimal-Zscaler,
     dual-SSE, with-Okta-SSO, PaloAlto-only, government-SASE.

   All apex names are Microsoft-fictional. The corpus generator is
   `validation/synthetic_corpus/generator.py`; the registry is
   deterministic. The v1.9.9 base corpus is preserved unchanged.

2. **Per-stratum aggregator.** `validation/corpus_aggregator.py`
   gained per-stratum bucketing. v1.9.10 used a tenant_id substring
   matcher (`_stratum_for_tenant`); a v1.9.11 audit found that
   matcher misbucketed brand-style tenant_ids (`tailspin-firebase`,
   `northwind-oci`) and pulled a baseline fixture (`wingtip-azure`)
   into the Azure bucket. v1.9.11 replaced it with an explicit
   `_stratum` tag the generator injects, derived from the REGISTRY
   key. The aggregate output carries a `per_stratum` map with
   per-stratum counted, multi-cloud rendered firing count, and
   ceiling rendered firing count. **The corrected numbers are below
   under "Aggregator output"; the original v1.9.10 numbers (with
   the misbucketing) are retained at the bottom of this memo for
   historical reference.**

3. **Bayesian network re-validation.** Documented in
   `validation/v1.9.10-bayesian-revalidation.md`. The v1.9.9
   wordlist additions widen slug collection at the surface-
   attribution layer; the network's evidence bindings (5 signals
   plus upstream node posteriors) are unchanged. All 20 v1.9.5
   stability tests pass on the v1.9.9 codebase. No node requires
   re-calibration; the v1.9.6 disposition table holds.

4. **Mutation-testing status.** Documented in
   `validation/v1.9.10-mutation-status.md`. The full cosmic-ray
   sweep slips from v1.9.10 to the v2.0 lock (Linux CI runner).
   The v1.9.9 catalog-driven Hypothesis tests already caught a
   real pre-existing bug (the `Data & Analytics` KeyError), which
   is stronger evidence of test-quality breadth than a clean
   cosmic-ray run would be.

5. **Roadmap cleanup.** `cosmic-ray-v199.toml` removed from the
   repo root (committed in v1.9.9 in error). Doc references
   updated.

## Aggregator output (synthetic corpus, 79 fixtures)

Numbers below are the v1.9.11 re-aggregation with the corrected
stratum derivation. The overall multi-cloud and ceiling rates are
unchanged from v1.9.10 (the bug was in bucketing, not in trigger
detection); the per-stratum rows shift to match the intended
fixture counts (19 baseline + 10 per stratum).

```
multi_cloud (rendered): 23 of 79 (29.1%)
ceiling (rendered): 68 of 79 (86.1%)
```

Per-stratum:

| Stratum | counted | multi-cloud fires | rate | ceiling fires | rate |
|---|---|---|---|---|---|
| baseline (v1.9.9 mixed) | 19 | 8 | 42.1% | 11 | 57.9% |
| gcp | 10 | 3 | 30.0% | 10 | 100.0% |
| azure | 10 | 1 | 10.0% | 10 | 100.0% |
| oracle | 10 | 3 | 30.0% | 8 | 80.0% |
| alibaba | 10 | 1 | 10.0% | 9 | 90.0% |
| paas | 10 | 7 | 70.0% | 10 | 100.0% |
| sse | 10 | 0 | 0.0% | 10 | 100.0% |

### Reading the per-stratum results

**Multi-cloud rollup behaviour by stratum:**
- PaaS fires multi-cloud most often (70%) because Vercel /
  Netlify / Cloudflare Pages fixtures legitimately span two cloud
  vendors (the PaaS provider + Cloudflare DNS).
- SSE/SASE never fires multi-cloud (0%): the SSE provider is a
  security service, not a cloud vendor in the rollup map. SSE
  fixtures with one true cloud vendor (e.g., AWS Route 53) cannot
  cross the >= 2 distinct vendor threshold. This is the right
  behaviour and matches how operators describe SSE deployments.
- Alibaba / Azure rates are low (10%) because most fixtures in
  those strata are intentionally single-cloud (the stratum is the
  cloud).
- GCP and Oracle fire more often (30% each) because each stratum
  includes deliberate multi-cloud variants
  (`stratum_gcp_dual_with_aws`, `stratum_gcp_idp_heavy`,
  `stratum_oracle_dual_with_aws`, `stratum_oracle_global`).

**Ceiling firing rate is high across stratum fixtures (80-100%)**
because the synthetic fixtures are intentionally weighted toward
sparse profiles (multi-domain + few categorized service families +
few surface attributions). On a real-world balanced corpus the
ceiling firing rate would be substantially lower.

The ceiling rate on the v1.9.9 baseline corpus (57.9%) is closer to
what a balanced operational corpus would show. The stratum
fixtures' high ceiling rate is a synthetic-corpus artefact, not a
production calibration concern.

## Honest framing

This is synthetic-corpus validation, not real-corpus validation. A
rigorous review correctly notes that:

1. The synthetic fixtures are designed by the same person who wrote
   the trigger heuristics, so they are biased toward demonstrating
   the surfaces.
2. The per-stratum patterns reflect intentional fixture design, not
   patterns observed in real data.

The corpus aggregator at `validation/corpus_aggregator.py` is ready
to run against the gitignored private corpus. The maintainer's
real-corpus run (whenever it happens) will produce per-stratum
firing rates that are the true measurement; the synthetic results
above are the publicly-reproducible specification audit.

The v1.9.10 ship line is honest: "we built the harness, ran it on
synthetic data we authored, the synthetic data behaves as
designed." Production-fidelity validation waits on the real-corpus
run, which is documented as standing work in
`validation/invariant_audit.md` item 1.

## Quality bar — what was checked

- [x] 60 stratified synthetic fixtures across 6 strata.
- [x] All Microsoft-fictional brands; no real customer names.
- [x] Per-stratum aggregator emits coverage metrics.
- [x] Per-stratum unclassified-termini count: zero (synthetic
  fixtures don't include unclassified CNAME chains by construction).
- [x] Bayesian re-validation: 20/20 stability tests pass; no
  per-node calibration drift.
- [x] Cosmic-ray sweep: documented as v2.0-lock work (slipped from
  v1.9.10 with rationale).
- [x] All quality gates clean (ruff, pyright, full pytest).
- [x] Trend table v1.6 → v1.9.10: deferred to v1.9.11 doc-polish
  pass (requires re-running earlier versions against the new
  strata; out of v1.9.10 scope).

## What deferred to v1.9.11 or later

- **Trend table v1.6 → v1.9.10 per stratum.** Requires re-running
  the v1.6, v1.7, v1.8, v1.9.0 builds against the new stratified
  corpus. Substantial standalone work; v1.9.11 doc-polish pass is
  the natural home.
- **Real-corpus aggregator run.** The maintainer runs it locally
  against the gitignored private corpus and drops the aggregate
  output here. The script is ready; the data is not committed.
- **Cosmic-ray full sweep.** v2.0 schema lock per
  `validation/v1.9.10-mutation-status.md`.

## Files touched

- `validation/synthetic_corpus/generator.py` — 60 new stratum
  fixture functions added; REGISTRY extended.
- `validation/synthetic_corpus/fixtures/` — 60 new JSON files
  generated.
- `validation/synthetic_corpus/results.json` — regenerated as
  combined 79-fixture results.
- `validation/synthetic_corpus/aggregate.json` — per-stratum
  aggregate output.
- `validation/corpus_aggregator.py` — `_stratum_for_tenant` helper
  added; `per_stratum` map added to output shape.
- `validation/v1.9.10-bayesian-revalidation.md` — Bayesian
  re-validation memo (this release).
- `validation/v1.9.10-mutation-status.md` — cosmic-ray status memo
  (slip to v2.0).
- `validation/invariant_audit.md` — item 2 milestone updated to
  v2.0.
- `docs/roadmap.md` — v1.9.10 section flipped from forward-looking
  to shipped; trend-table item moved to v1.9.11.
- `CHANGELOG.md` — v1.9.10 entry.

## Appendix — original (v1.9.10 ship-time) per-stratum numbers

These are the numbers committed at v1.9.10 ship time. They are
retained here for historical reference. The v1.9.11 audit
established that the `_stratum_for_tenant` substring matcher
misbucketed three fixtures (`tailspin-firebase`, `northwind-oci`
into `baseline`; `wingtip-azure` into `az`); the corrected numbers
appear above under "Aggregator output". Overall multi-cloud and
ceiling rates (29.1% / 86.1%) were correct at v1.9.10 ship time;
only the per-stratum allocation needed correction.

| Stratum | counted | multi-cloud fires | rate | ceiling fires | rate |
|---|---|---|---|---|---|
| baseline (v1.9.9 mixed) | 20 | 9 | 45.0% | 12 | 60.0% |
| gcp | 9 | 3 | 33.3% | 9 | 100.0% |
| az | 11 | 1 | 9.1% | 11 | 100.0% |
| oracle | 9 | 2 | 22.2% | 7 | 77.8% |
| ali | 10 | 1 | 10.0% | 9 | 90.0% |
| paas | 10 | 7 | 70.0% | 10 | 100.0% |
| sse | 10 | 0 | 0.0% | 10 | 100.0% |
