v1.1 narrows ambiguity at the two boundaries that CI consumers depend on: what an input parser actually supports, and why a policy decision was emitted. It does not add another scientific-computing mini-project or move the tool out of this monorepo.
| Contract | Current v1.1 status |
|---|---|
| Real input support matrix | Implemented in parser-boundaries.md from the registered parsers |
| Policy schema identifier | Implemented as sbom-diff-risk.policy.v1; legacy policy files remain readable |
| Report schema identifier and compatibility tests | Implemented as sbom-diff-risk.report.v1 across checked-in full-report fixtures |
| Per-decision rule, evidence, reason, and confidence | Implemented additively in report v1 policy finding objects |
| Component identity canonicalization | Implemented as a typed value object with stable duplicate/conflict diagnostics |
| Ecosystem-specific canonicalization matrix | Implemented in component-identity-canonicalization.md; package-name normalization is explicit per ecosystem rather than universal |
The canonical identity record will expose these dimensions separately:
ecosystem: trimmed and normalized to a registered ecosystem identifier.package_name: normalized with ecosystem-aware rules. PyPI names use PEP 503 normalization; npm names use thepackageurl-pythonnpm purl name form; ecosystems without an explicit project rule preserve case rather than inheriting a universal lowercase rule.version: trimmed but otherwise preserved as observed. The tool will not infer semantic equivalence between unrelated version schemes.purl: parsed withpackageurl-pythonand normalized when present, while retaining the observed purl in component evidence for auditability. An explicit component version does not get invented inside a versionless purl.component_key: versionless package identity used to align before and after inputs. A version change remains a change, not an add plus remove.
Identity authority remains purl, then bom_ref, then the normalized
(ecosystem, package_name) coordinate. A parseable purl is authoritative for
its ecosystem and package coordinate. Explicit metadata that disagrees with
that coordinate is a conflict, not an alternative identity.
Canonical identity also drives change comparison: lexical PyPI variants that
normalize to the same identity do not create a metadata change, and a version
change carried only by the purl is still classified as version_changed.
Within one input:
- two records with the same key and identical normalized metadata fail closed
as
duplicate_component; - two records with the same key but different normalized metadata fail closed
as
conflicting_metadata; - conflicting ecosystem, package name, or version information between a purl
and explicit fields also fails closed as
conflicting_metadata; - an invalid purl also fails closed as
conflicting_metadatabecause it cannot establish an unambiguous canonical identity; - metadata differences across the before and after inputs remain normal diff evidence and do not become same-input conflicts.
The implementation introduces a frozen CanonicalComponentIdentity object and
keeps report presentation unchanged. The executable matrix is exposed through
canonicalization_rules() so tests can assert the supported ecosystem rules.
Tests cover CycloneDX-to-SPDX alignment, PyPI name normalization, case
preservation for ecosystems without a declared name rule, namespace retention,
versioned purls, exact duplicates, and conflicting metadata.
policy_schema identifies the serialized policy family. The existing integer
version continues to select rule capabilities and remains compatible with
v1.0 policy files.
Every emitted policy finding includes:
matched_rule_idfor the exact matching rule;exact_evidencefor the local values used in the comparison;decision_reasonas a stable machine-readable reason code;confidence_levelfor the recorded evidence source.
The legacy rule_id, policy_rule, matched_threshold, and observed_value
fields remain in report schema v1. Consumers can migrate to the grouped fields
without a flag day.
Before v1.1 release:
- Every checked-in full JSON report must declare
sbom-diff-risk.report.v1and satisfy required-field/type tests. - Every canonical policy example must declare
sbom-diff-risk.policy.v1; legacy omission remains a tested compatibility path. - Unknown policy schema identifiers must fail closed.
- Policy decisions in local, provenance, and Scorecard fixtures must expose rule, evidence, reason, and confidence fields.
- Existing v1.0 fields remain readable for the lifetime of report schema v1.
sbom-diff-and-risk remains under tools/ in this monorepo for v1.1. Naming
friction alone is not a migration trigger. A separate repository should be
considered only after real third-party adoption creates an independent release
cadence, issue stream, or packaging boundary. Production PyPI publishing is a
separate decision and is not implied by this route.