# v1.8.0 validation report

**Date:** 2026-05-03
**Corpus:** 105 unique domains drawn from `corpus-50-diverse.txt`,
`corpus-random-50-20260427.txt`, and `corpus-weak-areas.txt`.
**Command:** `python -m recon_tool.cli batch <corpus> --json --include-ecosystem --concurrency 8`

## Headline

- **0 errors** across 105 domains in both the original and post-fix
  runs. The v1.8 code paths
  (`infrastructure_clusters`, `fingerprint_metadata`, ecosystem
  hypergraph) do not introduce regressions.
- **The validation surfaced two pre-tag hardening items** described
  below. Both are fixed and re-validated; the second run confirms
  the fixes work on real data.
- **Cluster partition quality strong on the domains that have
  buildable graphs.** Mean modularity **0.563**, max **0.883** in
  the original run.
- **Fingerprint relationship metadata covers 91 / 105 domains** that
  detected at least one slug carrying `parent_vendor` /
  `product_family` / `bimi_org` metadata.

## What the validation surfaced (and how it's fixed)

### 1. CertSpotter `Z`-suffix dates rejected on Python 3.10

The original 105-domain run produced **0/105** populated
`cert_summary` blocks despite 104/105 domains successfully reaching
CertSpotter. Investigation traced this to `build_cert_summary` in
`recon_tool/sources/cert_providers.py`: it called
`datetime.fromisoformat()` on `not_before` / `not_after`, which
CertSpotter emits with the `Z` UTC marker (e.g.
`2025-04-03T00:00:00Z`). Python 3.10's `fromisoformat` rejects `Z`
— only 3.11+ accepts it natively. Every entry was silently skipped
and `cert_summary` returned None, which in turn left
`top_issuer` ecosystem hyperedges, `wildcard_sibling_clusters`, and
`deployment_bursts` all empty.

This was a pre-existing v1.7.0 bug, surfaced for the first time by
the v1.8 corpus run. **Fix:** new `_parse_iso_datetime()` helper
normalizes `Z` → `+00:00` before parsing; applied in both
`build_cert_summary` and `_detect_deployment_bursts`. Regression
test in
[`tests/test_cert_providers.py::TestBuildCertSummary::test_certspotter_z_suffix_dates_parse`](../tests/test_cert_providers.py).

After the fix, a fresh-cache re-run produced a populated
`cert_summary` (cert_count=100, top_issuers=Amazon Trust Services,
GlobalSign, DigiCert) on the one domain that beat CertSpotter's
weekly rate limit. Production usage spread across cache + crt.sh
fallback recovers cert_summary normally.

### 2. `shared_slugs` hyperedge noise floor

The original run's hypergraph was 195/200 trivial pairs — every
enterprise pair sharing the same handful of ubiquitous-SaaS slugs
would fire as a `shared_slugs` edge. The cap was hit by
ubiquitous-SaaS coincidence rather than by structural similarity.

**Fix:** two complementary changes in `recon_tool/ecosystem.py`:

1. `_MIN_SLUG_OVERLAP` raised from 2 → 3.
2. New `_baseline_slugs()` helper computes per-batch slug
   prevalence; slugs above `_BASELINE_FREQ_THRESHOLD = 0.5` are
   stripped from the intersection before the threshold check. The
   filter is adaptive — it scales with whatever corpus the operator
   runs — and engages only when the batch exceeds
   `_MIN_BATCH_FOR_BASELINE = 5` domains so synthetic test fixtures
   stay deterministic.

Post-fix re-run: the 7 ubiquitous slugs identified in this corpus
(productivity-suite, site-verification, ESPs, CDN, identity, and
collaboration platforms each appearing on >50% of domains) no longer
appear in any `shared_slugs` key. Remaining edges surface
discriminating signal — identity-platform overlaps in the high
hundreds, document-signing and on-prem-collaboration in the dozens,
endpoint-management and AI-platform overlaps in the low dozens —
each pair now reflects genuine enterprise tech-stack overlap rather
than baseline noise. Two new regression tests in
[`tests/test_ecosystem.py::TestSharedSlugs`](../tests/test_ecosystem.py).

## Infrastructure clusters by path (original run)

| algorithm | domains |
|---|---|
| `louvain` (graph fits in 500-node cap) | 10 |
| `connected_components` (fallback above cap) | 1 |
| `skipped` (no graph buildable from CT data) | 94 |

The 94 `skipped` results align with the "hardened-target" framing in
[`docs/correlation.md`](../docs/correlation.md): wildcard-dominated CT
histories, single-SAN certs, and small footprints all collapse below
the 2-edge minimum. Only ~10 % of the corpus has rich enough
multi-SAN issuance to support community detection — exactly why v1.8
keeps the cluster envelope hedged and the modularity score visible
rather than burying the partition behind a verdict.

Graph size on the buildable subset: **mean 234 nodes, max 714** — the
714-node case triggered the connected-components fallback,
validating the 500-node cap.

## Defensive interpretation

The numbers reinforce the v1.7-v1.9 thesis: hardening narrows what
single-cert fingerprinting can recover, and structural correlation
recovers usable signal from the residual. Where the graph fires
(modularity 0.5-0.9), the partition is meaningful. Where it doesn't
(94 `skipped` cases), the report says so — no false confidence on
sparse evidence.

After the noise-floor fix, the hypergraph cap order — top_issuer →
bimi_org → parent_vendor → shared_slugs — pre-fills high-signal edges
and lets discriminating slug overlaps fill the rest, instead of
ubiquitous-SaaS coincidence.

## What this run does NOT validate

- **Live-network resilience at scale.** CertSpotter rate-limited
  most cache-cleared queries (1/105 got fresh cert metadata in the
  re-run). Production usage spread across the day, with crt.sh as
  primary and CertSpotter as fallback, will populate
  `cert_summary` on more targets — but a 105-domain burst against
  CertSpotter alone will hit weekly quota.
- **Chain-motif coverage.** Zero `chain_motifs` observations across
  both runs. Likely the v1.7 motif catalogue is too narrow for the
  CDN/origin shapes this corpus uses; a follow-up motif-library
  expansion is the right next step.
- **Vertical-baseline anomaly behavior.** Anomalies surface inline
  with `analyze_posture --profile`, not in batch mode. Validating
  the new `expected_categories` / `expected_motifs` rules requires a
  second pass through `recon analyze posture --profile fintech` over
  representative domains; tracked for the v1.8 retrospective.

## Reproducing

```bash
cat validation/corpus-50-diverse.txt \
    validation/corpus-random-50-20260427.txt \
    validation/corpus-weak-areas.txt \
  | grep -v '^#' | grep -v '^$' | sort -u > /tmp/v1_8_validation.txt
python -m recon_tool.cli batch /tmp/v1_8_validation.txt \
  --json --include-ecosystem --concurrency 8 \
  > validation/v1.8-validation-results.json
```

Raw result sets:

- [`validation/v1.8-validation-results.json`](v1.8-validation-results.json)
  — original run (pre-fix).
- [`validation/v1.8-validation-results-v2.json`](v1.8-validation-results-v2.json)
  — post-fix run (cache cleared).

## v1.8.1 follow-up (2026-05-03)

After shipping v1.8.0, a 10-domain diverse deep-dive — one apex from
each of: hyperscale tech, global payments, productivity SaaS,
heavy-industrial manufacturing, higher-ed research, EHR, non-profit
messaging, federal agency, warehouse retail, and national news media
— surfaced four issues that batch-statistics validation missed because
per-target output quality matters more than corpus medians:

1. `chain_motifs: 0/10` and `0/105` across both prior runs. Root cause:
   the v1.7 motif catalog covered only third-party-CDN-to-third-party-cloud
   patterns. Real-world chains terminate inside a single vendor's
   network (e.g. the hyperscale-tech apex resolved
   `trafficmanager.net -> azurefd.net -> t-msedge.net`) which no
   catalog motif matched. Plus a separate cache-fidelity bug (see
   item 4) that silently dropped motifs on cache write.
2. `cert_summary: None` on 7/10 domains. Root cause: the CT pipeline
   treated CertSpotter rate-limited responses (HTTP 200 with empty
   issuance list) as a "successful empty answer" and early-returned,
   blocking the CT-cache fallback even when a populated cache entry
   existed.
3. `fingerprint_metadata` coverage gap: only 6 slugs seeded with
   relationship metadata in v1.8.0 fired across the 10 domains, even
   though many high-frequency unseeded slugs across collaboration,
   CDN, document-signing, ESP, identity-platform, and AI-platform
   categories appeared on most targets.
4. `chain_motifs` and the v1.7 `cert_summary` extensions
   (`wildcard_sibling_clusters`, `deployment_bursts`) were silently
   dropped on cache write — cached lookups served zero motifs even
   when the original resolve produced matches. Surfaced as part of
   diagnosing item 1.

All four were fixed in v1.8.1:

- New `_parse_iso_datetime` already shipped in v1.8.0 covered the
  Z-suffix issue. Item 2 needed a separate fix in
  `_detect_cert_intel`: empty-but-not-error responses now treated as
  soft failure and the CT-cache fallback is reached.
- Motif catalog grew from 10 to 18 — added intra-vendor chains for a
  major hyperscale provider (`tm_to_azurefd`, `azurefd_to_msedge`,
  `akamai_to_msedge`, `tm_to_powerapps`), a CRM-platform edge
  (`salesforce_chain` / Force.com), an Adobe Experience Cloud chain,
  an Akamai-to-Adobe chain, and an Oracle Cloud chain.
- Fingerprint metadata seeded on 26 more slugs (now 34 total).
  Coverage spans AI platforms, CRM, marketing automation, ESPs,
  CDN/edge, identity platforms, document signing, MDM, payments,
  and site-verification — each now carries `product_family` /
  `parent_vendor`.
- `tenant_info_to_dict` and `tenant_info_from_dict` extended to
  round-trip `chain_motifs`, `wildcard_sibling_clusters`, and
  `deployment_bursts`.

### Post-fix dive (cache cleared)

Same 10 anonymized targets, fresh resolve:

- `chain_motifs`: **0 -> 19 firings** across 4 of 10 targets. The
  hyperscale-tech apex contributed the most: `azurefd_to_msedge`
  10x and `tm_to_azurefd` 4x and `tm_to_powerapps` 2x. The federal
  agency and warehouse-retail apexes contributed 3x of
  `salesforce_chain` (both run Salesforce). The EHR vendor
  contributed 2x of `azurefd_to_msedge` (Azure Front Door front).
- `fingerprint_metadata` distinct slugs surfaced: **6 -> 29**.
  Per-domain coverage went from 0-4 slugs to 4-16.
- Cluster quality preserved on the targets where CT data came
  through fresh: the hyperscale-tech apex hit modularity 0.82 on
  247 nodes / 20 clusters; the global-payments apex hit 0.20 on
  73 nodes (the lower modularity vs the prior corpus's 0.29
  reflects a smaller fresh CertSpotter pull this run, not a
  regression).

The `7/10 -> still 7/10` skipped-graph rate is unchanged in this
specific re-run because it depends on CertSpotter having pre-cached
data; cache-cleared runs don't exercise the v1.8.1 cache-fallback
fix. Production usage where the cache accumulates over time will see
the benefit (proven by unit tests in
[`tests/test_fallback_chain.py`](../tests/test_fallback_chain.py),
v1.8.1 additions).

The third dive's raw output stays private (per-domain real-world
JSON); only this summary ships in the public repo.
