Skip to content

Commit 2216a78

Browse files
bomly-guyclaude
andcommitted
docs: address review — origin gates named, module identity path-scoped, ref semantics industry-aligned
Evidence-qualifier redirection is defined as passing through the ADR-0033 origin constructors, which reject signed or tokenized links outright — nothing is sanitized into publication. Module identity always includes the declaring manifest path, so a recursive scan's two unrelated same-name projects can never fold. Readable-ID stability is scoped the way the major SBOM formats scope it: CycloneDX bom-refs and SPDX element IDs are document-local handles, cross-run identity lives in the data fields, and the singleton-keeps-bare-PURL choice is recorded with the rejected alternative. The architecture narrative now points at ADR-0041. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 642c223 commit 2216a78

2 files changed

Lines changed: 36 additions & 12 deletions

File tree

dev-docs/ARCHITECTURE.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,12 @@ is one file with an ID, date, and status. To record a new decision, copy
1111
the [index](adr/README.md). This document stays the architecture narrative;
1212
it links to ADRs where a section's behavior comes from a recorded decision.
1313

14-
An active cross-repo program to mature the SDK model (content-addressable
15-
identity, SBOM-complete typed fields, single-home PURL/SPDX behavior, Go
16-
1.27) is planned in [`SDK_MATURITY_PLAN.md`](SDK_MATURITY_PLAN.md), backed by
17-
ADR-0036 through ADR-0039. The standing placement rule the program restores —
14+
An active cross-repo program to mature the SDK model (canonical-PURL
15+
identity on a typed node union, SBOM-complete typed fields, single-home
16+
PURL/SPDX behavior, Go 1.27) is planned in
17+
[`SDK_MATURITY_PLAN.md`](SDK_MATURITY_PLAN.md), backed by ADR-0037 through
18+
ADR-0039 and [ADR-0041](adr/0041-identity-is-the-canonical-purl-on-typed-nodes.md)
19+
(which supersedes ADR-0036's content-addressable design). The standing placement rule the program restores —
1820
model behavior lands in the SDK first; the CLI and plugins hold only what is
1921
theirs by nature — is
2022
[ADR-0040](adr/0040-the-sdk-is-the-default-home-for-behavior.md).

dev-docs/adr/0041-identity-is-the-canonical-purl-on-typed-nodes.md

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,12 @@ for map grouping — with a three-way origin relation:
105105
Comparison is kind-scoped: nodes of different kinds are never equal, so a
106106
module node and a dependency node sharing a PURL — or sharing the absence
107107
of an origin — can never fold into each other, whatever the insertion
108-
order. Module nodes compare by canonical PURL when both carry one and by
109-
(declaring manifest path, name) otherwise; manifest nodes compare by path.
108+
order. Module nodes always compare with their declaring manifest path as
109+
part of the identity — beside the canonical PURL when one exists, beside
110+
the module name otherwise — because a recursive scan can legitimately
111+
discover two unrelated projects carrying the same ecosystem, name, and
112+
version, and a PURL-only comparison would fold those separate roots and
113+
union their dependency edges. Manifest nodes compare by path.
110114
Keys are in-process comparison values, kind-prefixed, and never appear in
111115
scan JSON, SBOMs, or any published document.
112116

@@ -120,15 +124,33 @@ specification's URL-valued evidence keys — `repository_url`,
120124
`download_url`, and `vcs_url` — whose values are resolution evidence that
121125
can embed credentials and signed links: identity normalization strips them
122126
from the PURL and their content belongs in `Origin`, behind the ADR-0033
123-
gates, so they shape the occurrence, never a published ID. When distinct
127+
gates, so they shape the occurrence, never a published ID. Redirection
128+
means passing the value through the ADR-0033 origin constructors, never
129+
stapling it on: those constructors reject a query-carrying artifact URL
130+
outright — a signed or tokenized download link is discarded entirely, not
131+
sanitized into something publishable — so a credential embedded in a
132+
qualifier can reach neither a published ID nor an exported origin field. When distinct
124133
occurrences of one canonical PURL must coexist, each origin-bearing
125134
occurrence carries a deterministic suffix — a single space and a short
126135
lowercase-hex hash of its kind-prefixed normalized origin. Deriving the
127-
suffix from the occurrence's own origin keeps IDs stable when the
128-
occurrence set changes: adding or removing a sibling never renumbers the
129-
others, which positional ordinals could not promise, and the normalized
130-
origin is already credential-free, so the hash publishes nothing the
131-
origin field does not. Occurrences distinguishable only by raw evidence
136+
suffix from the occurrence's own origin means a contested occurrence keeps
137+
one suffix for as long as it is contested — adding or removing a sibling
138+
never renumbers the others, which positional ordinals could not promise —
139+
and the normalized origin is already credential-free, so the hash
140+
publishes nothing the origin field does not. A singleton keeps the bare
141+
PURL, and gaining a first sibling does change its rendered ID; that is
142+
deliberate, because readable IDs carry the reference semantics the major
143+
SBOM formats define: CycloneDX `bom-ref` and SPDX element IDs are
144+
document-local handles required only to be unique within one document,
145+
with cross-document identity carried by the data fields (the PURL,
146+
external references) rather than the handle. Bomly follows that industry
147+
contract — IDs are unique within a run's outputs and as human-meaningful
148+
as uniqueness allows, while cross-run identity lives in the identity
149+
fields (canonical PURL, normalized origin) that diff, baselines, and
150+
matching already join on. Suffixing every origin-bearing occurrence
151+
unconditionally was considered and rejected: it would trade the common
152+
case's readability for stability of a handle the formats define as
153+
document-local. Occurrences distinguishable only by raw evidence
132154
have nothing publishable to hash; they carry run-local ordinal suffixes
133155
(`o1`, `o2`, … in sorted raw-key order) with the stated caveat that this
134156
rare class is run-scoped and not stable across evidence changes — never a

0 commit comments

Comments
 (0)