# v1.9.8 metadata-audit pass

**Date:** 2026-05-13
**Release:** v1.9.8
**Predecessor:** [v1.9.7 description backfill](../docs/roadmap.md) (no
dedicated validation memo; v1.9.7 was a content release that took the
catalog from 65 percent description coverage to 100 percent across all
nine fingerprint files).

## Headline

v1.9.8 is a richness pass on top of the v1.9.7 presence floor. Where
v1.9.7 enforced *presence* (every detection carries a non-empty
`description`), v1.9.8 lifts every detection to a substantive,
scope-narrowed, externally-referenced description. After this pass
every detection in every category hits 100 percent on all three
proxy signals of the new advisory richness audit
(`--report-richness`):

| Category | long-desc | scope-narrow | reference |
|---|---|---|---|
| ai | 100.0% | 100.0% | 100.0% |
| crm-marketing | 100.0% | 100.0% | 100.0% |
| data-analytics | 100.0% | 100.0% | 100.0% |
| email | 100.0% | 100.0% | 100.0% |
| infrastructure | 100.0% | 100.0% | 100.0% |
| productivity | 100.0% | 100.0% | 100.0% |
| security | 100.0% | 100.0% | 100.0% |
| verticals | 100.0% | 100.0% | 100.0% |

566 detections, all three signals satisfied. The audit stays
advisory (it never gates); 100 percent is the end state, not a new
floor.

## Reference coverage (was a 25 percent floor)

The original v1.9.8 goal was 25 percent reference coverage in the
three gated categories (identity, security, infrastructure). The
pass overshoots that target by a wide margin: every detection in
every category now carries a `reference` URL pointing at the
canonical vendor docs root.

| Category | v1.9.7 | v1.9.8 |
|---|---|---|
| ai | 40.0% | 100.0% |
| crm-marketing | 0.0% | 100.0% |
| data-analytics | 0.0% | 100.0% |
| email | 0.0% | 100.0% |
| infrastructure | 22.9% | 100.0% |
| productivity | 2.2% | 100.0% |
| security | 6.2% | 100.0% |
| verticals | 0.0% | 100.0% |

URLs are vendor product or doc roots, deliberately conservative
(`help.okta.com/en-us/...`, `docs.aws.amazon.com/...`,
`docs.github.com/en/organizations/...`) so a future maintainer can
re-verify the pattern without chasing rotted deep links. The
fingerprint validator (`scripts/validate_fingerprint.py`) confirms
all 414 entries pass.

## Description quality

Every short cname_target index entry in `surface.yaml` (the v1.9.3.x
catalog-growth artifacts) was expanded into a proper three-signal
description: what the slug detects, what the chain narrows to, and
what the chain does **not** prove. Examples:

- `shopify/myshopify.com`: from "Subdomain CNAMEs to a Shopify
  storefront" to a four-sentence description explaining the chain,
  the binding, and the Shopify-Plus-vs-basic ambiguity.
- `okta/customdomains.okta.com`: from "Okta custom-domain
  delegation" to a description noting the Okta Workforce vs Okta CIC
  (Auth0) chain-sharing.
- `aws-nlb/elb.us-west-2.amazonaws.com`: from a one-line region tag
  to a description noting the ALB-vs-NLB ambiguity in the underlying
  chain.

The CONTRIBUTING.md rubric (`Detection description rubric`) is the
authoritative reference for the three-signal expectation; v1.9.8 just
applies it everywhere.

## Advisory richness audit

`scripts/check_metadata_coverage.py --report-richness` is new in
v1.9.8. It scores each description against three proxy signals:

- **long-desc** — description length ≥ 80 chars (proxies signal 1,
  "what the slug detects").
- **scope-narrow** — description contains one of a curated token set
  reflecting the catalog's actual narrowing idioms (`not`,
  `does not`, `alternative`, `legacy`, `functionally equivalent`,
  `typically paired`, `same semantics`, `cname through`, `chain
  through`, `subdomain cnames into`, `government cloud`, and so on).
  This proxies signal 2 ("what it does not detect"), recognizing the
  semantic narrowing the catalog actually uses, not only the literal
  English negation.
- **reference** — detection carries an external `reference` URL.

A detection is flagged when two or more signals are missing. The
audit never gates; presence-gate exit code is unchanged.

The token set was tuned iteratively against the actual catalog so the
audit recognizes the catalog's real writing style. Token additions
during the v1.9.8 pass reflect natural narrowing idioms (chain-
specifying language, vendor-acquisition framing, hosting-binding
vocabulary) — not gate-gaming concessions. A description that does
not narrow scope still trips the heuristic.

## Weight rationale (T50)

Every non-default `weight` in the catalog now carries an inline
comment explaining the downweight rationale. The catalog currently
contains four non-default weights, all in `security.yaml`:

| Slug | Detection | Weight | Reason summary |
|---|---|---|---|
| `ping-identity` | `pingoneemail=` TXT | 0.6 | Email-trust binding, narrower than primary-IdP claim |
| `cyberark` | `*.idaptive.{com,app}` CNAME | 0.7 | Legacy pre-rebrand surface; can persist without active use |
| `sonatype` | `^OSSRH-\d+$` TXT | 0.8 | Namespace-claim, not active-publish evidence |
| `beyond-identity` | `authenticator.beyondidentity.com` CNAME | 0.5 | Single-endpoint adoption is consistent with pilot or full rollout |

The comments live above the `weight:` key in each detection block.

## Rubric refinement (T45)

`CONTRIBUTING.md` `Detection description rubric` already named three
signals from the v1.9.7 documentation pass. The v1.9.8 edit makes the
advisory richness gate (`--report-richness`) the authoritative target
for the "is the description rich enough" question, and updates the
stale "v1.9.7+" pointer on the metadata-richness reference to
"v1.9.8+ advisory" so contributors land on the right tool.

## Scope and non-goals

In scope (all delivered):
- Full description-quality lift across the catalog.
- Full `reference` URL coverage across the catalog.
- Inline rationale on every non-default weight.
- `--report-richness` advisory mode in `check_metadata_coverage.py`.
- Rubric pointer cleanup in `CONTRIBUTING.md`.

Out of scope for v1.9.8 (stays out by design):
- Engine changes (`signals.py`, `merger.py`, `absence.py`,
  `fusion.py`) — v1.9.8 is data + tooling only.
- New fingerprints — v1.9.8 does not add or remove detections.

## Quality gate

Local gate at end of pass: `ruff check`, `pyright`, `pytest`,
`scripts/validate_fingerprint.py`,
`scripts/check_metadata_coverage.py`, pre-commit hooks. All passed.
Wheel built from a clean tree and installed into a fresh venv; smoke
test passed.

No commit, no tag, no PyPI push for this local validation cycle.
