Commit a7cd84a
build: require Go 1.27 (#412)
* docs: plan the SDK maturity program (ADR-0036 through ADR-0039)
Records the four architecture decisions behind the cross-repo SDK
maturity effort — content-addressable dependency identity, SBOM
assertions as typed model fields, a single SDK home for PURL and SPDX
behavior, and the Go 1.27 move with strict untrusted-document parsing —
and adds dev-docs/SDK_MATURITY_PLAN.md, the phased execution plan that
sequences them across bomly-sdk, the plugin repos, and this CLI. The
plan folds in issues #410 and #396 and five previously unreported
fidelity defects found while surveying the current state.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs: record the SDK-first placement rule as ADR-0040
The principle behind the maturity program becomes a standing rule:
behavior about shared domain objects lands in bomly-sdk first, and the
CLI and plugins keep only what is theirs by nature. Recorded as
ADR-0040 and wired into the fix-at-the-right-depth convention in
CLAUDE.md and AGENTS.md so future fixes and features default to the
source of truth instead of restarting the drift.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs: address review — identity facet safety, PURL suffix delimiter, precision fixes
ADR-0036: the occurrence suffix delimiter moves off '#', which PURL
syntax uses for subpaths, and raw ResolvedURL is excluded from the
persistent content-address facets (machine-specific, credential-bearing,
and hashing does not protect low-entropy secrets); nodes distinguishable
only by raw evidence keep distinct run-local IDs but share the stable
facet address. ADR-0039: duplicate-key semantics described accurately
(input order, replace-or-merge by target type) and the toolchain
auto-download claim qualified to Go 1.21+ with GOTOOLCHAIN=auto. Plan:
PURL qualifier loss limited to the BuildPackageURL fallback; strict
ingest wording covers invalid UTF-8 alongside duplicate names.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs: address review — published-ID safety, injective encoding, per-entry assertions
ADR-0036: readable IDs never encode raw evidence even hashed (raw-only
discriminators become run-local ordinals); PURL qualifiers enter
identity only through a purlkit allowlist with URL gates; the content
address encoding becomes length-prefixed so untrusted delimiter bytes
cannot collide facets. ADR-0037: document assertions attach to
GraphEntry (GraphContainer is multi-entry), and the scope mapping is
defined for the set with a scalar projection rule plus a lossless
bomly:scopes side channel. ADR-0038: the deprecated-SPDX-ID replacement
map relocates into spdxkit as an audited authority rather than being
deleted — the license list marks deprecation but does not encode
replacements.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs: address review — facet timing, ADR-0040 loan, plan/ADR consistency
ADR-0036: occurrence facets are assigned at consolidation, after
contradiction is established — never at node creation — so ADR-0033's
gap-filling keeps folding origin-less witnesses instead of duplicating
nodes. Plan: the phase-0 CycloneDX scope fix is recorded as an explicit
ADR-0040 loan (SDK issue filed and linked, replaced in 2.6); phases 1.2
and 1.4 now match the amended ADRs (replacement map relocates; document
assertions attach per GraphEntry).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs: address review — identity URL strictness, export entry path, address is not a node key
ADR-0036: the identity facet strips query and fragment from artifact
URLs too — stricter than ADR-0033's publication rule, which keeps a
benign artifact query — so signed/tokenized URLs cannot enter the
address or the published suffix; and the content address is documented
as a stable-facet class identifier, never a per-node primary key
(address-keyed stores pair it with a local occurrence discriminator).
ADR-0037/plan 2.5: the export surface takes the prepared entries rather
than the bare merged graph, so per-entry document assertions actually
reach the codec instead of being discarded at consolidation.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs: address review — fallback ID escaping, merged-export projection, plugin toolchain phase
ADR-0036: the occurrence delimiter is reserved in both ID families —
the fallback coordinate form percent-encodes whitespace and the
delimiter per field, so an unescaped base like 'a@b 1' can no longer
collide with 'a@b' plus suffix '1'. ADR-0037: the merged-export
projection is defined — one document links each source's identity
(SPDX externalDocumentRefs, CycloneDX bom-type external reference)
rather than re-asserting it, and the fixed-point promise is scoped to
single-source flows. ADR-0039/plan 0.2: the nine bomly-plugin-* repos
join the toolchain phase, each bumping before adopting the first 1.27
SDK tag, since a dependency's go directive sets their minimum.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs: address review — query-distinguished occurrences, typed reference locators
ADR-0036: contradiction detection keeps using the ADR-0033-normalized
origin (query intact); when identity normalization makes established
contradictions coincide — two artifacts distinguished only by query —
they are handled like raw-evidence-only occurrences with distinct
run-local ordinals and a shared stable-facet address, so safety
normalization narrows what persists but never folds occurrences
ADR-0033 keeps distinct. ADR-0037: ExternalReference carries a typed
locator validated per reference category — URL categories pass the
ADR-0033 gates, identifier categories (purl, cpe, advisory IDs)
validate against their own grammars — since Bomly itself emits non-URL
SPDX locators today.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs: usage facts carry their attribution (reachable ∧ runtime ∧ direct)
Scope, relationship, and reachability are usage facts, and conjunctive
filters must hold of one usage — today Scopes is a node-level union
with no site attribution, Relationship a merged scalar, and
Reachability vulnerability-level with no module link, so a workspace
using a package directly-in-dev and transitively-at-runtime satisfies
'reachable, runtime, direct' node-wise while no single usage does.
ADR-0037 and phase 1.4 now record scope/relationship per
PackageLocation (the union becomes derived) and the establishing
module root on Reachability, so filters select usages and displays
show the path whose attribution satisfies every conjunct.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs: address review — repeatable reachability evidence, producer migrations
ADR-0037: reachability becomes a repeatable evidence list keyed by
module root, with the vulnerability annotation as the derived summary —
a singular annotation with one root field could keep at most one
analysis after vulnerability consolidation — and the conjunctive join
is evidence-to-location within the same root. Plan: new phase 2.8
migrates the producers and consumers (native detectors write per-site
scopes/relationship, the four analyzer repos emit per-module evidence
as behavioral bumps, CLI/MCP filtering joins through attribution, with
a regression test for the dev-direct vs runtime-transitive workspace);
the risk table no longer calls analyzer bumps mechanical.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs: address review — ephemeral insertion discriminator, kind-preserving copies, SPDX link data
ADR-0036: the ID-keyed graph means same-detector contradicting records
must coexist before consolidation classifies them, so the SDK insertion
entry point assigns an ephemeral non-durable discriminator that
consolidation finalizes (fold as gap, or replace with the durable
facet) — the ephemeral form never reaches output or persistence.
ADR-0037: the SDK ships a kind-preserving edge-copy/rename primitive so
consolidation's edge rebuilds cannot flatten ingested relationship
kinds to depends-on, with a guard test; and the per-entry carrier
retains what source links require, captured at ingest — SPDX namespace
plus document checksum, CycloneDX serial plus version.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs: address review — normative parameters for identity, scopes, references, projections
ADR-0036: fallback tuples enter identity only post-normalization with
idempotence as the guarded invariant; ordinal ordering is stated
(lexicographic by contradiction key, ties by manifest path then
location); readable-ID parameters fixed (space delimiter, 6-byte sha256
hex suffix, o-decimal ordinals, percent-escaping, last-unescaped-space
decode); the address encoding is byte-fixed (UTF-8, 4-byte big-endian
lengths, tag/identity/occurrence order, zero-length absents, 16-byte
truncation) with golden vectors as a 1.3 deliverable; addresses are
post-consolidation only; silent shortening is prohibited. ADR-0037:
ExternalReference gains Category to round-trip the SPDX triple, hashes
ride the bomly: comment channel in SPDX; complete scope-carrier rules
(sorted deduped list, carrier-beats-scalar, unknown-drop-with-warning,
scalar ingest mapping); the usage join key is (module root, declaration
site) with no cross-site mixing; the merged-document projection is
defined per format (SPDX DESCRIBES-per-root + externalDocumentRef with
checksum, CycloneDX BOM-Link urn:cdx:serial/version) with checksums
captured for every ingested document. ADR-0039: the strict-ingest
guarantee is narrowed to exactly duplicate names and invalid UTF-8.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs: address review — location module root, optional-scope preservation, locator-kind dispatch
ADR-0037: the location record carries its owning module root as a field
(two modules can share one lockfile path, so the site alone cannot join
evidence to its usage); the ingested CycloneDX scope scalar is
preserved as a source assertion and re-emitted verbatim on
single-source export so optional and excluded never collapse; and
external-reference validation dispatches on an explicit locator kind
derived from the normalized format/category/type tuple, never on the
SPDX-only Category axis. Plan 2.8 has detectors populate the module
root on locations.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs: reachability evidence references exact occurrence nodes; reverse index is derived
Folds the agreed occurrence-attribution model into ADR-0037 and the
plan: reachability evidence may carry optional DependencyRefs to the
exact graph nodes involved (final by analysis time — analyzers run
post-consolidation), with the module root as the mandatory attribution
floor and node refs the precision ceiling for analyzers that can
attribute (govulncheck build module versions, jsreach nested
node_modules copies). Display follows the evidence honestly: exact
occurrence with refs, 'reachable via this module' / 'not established
here' without — never 'unreachable'. The package→nodes direction stays
derived via an SDK reverse-index helper; a stored back-reference would
break the registry's position-free design and go stale on renames.
Plan 1.4/2.7/2.8 updated accordingly.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* build: require Go 1.27
Bumps the go directive to 1.27.0 (plan step 0.2, CLI slice; ADR-0039)
and absorbs the one-time go mod tidy require-block consolidation. Every
workflow reads the toolchain via go-version-file, so no CI edits.
CONTRIBUTING gains the Go prerequisite (GOTOOLCHAIN=auto downloads it
automatically); .gitignore gains go.work/go.work.sum so the local
cross-repo workspace never lands in a commit — main must stay
replace-free for remote go install.
The bump exposed one latent test defect: Go 1.27 test binaries carry
dependency build info, so TestRootVersion_IncludesTrackedDependencyVersions
ran its assertion loop for the first time in years and failed — the
rendered format gained the module path long ago while the test still
expected 'Syft:'. The test now asserts the exact rendered line and
fails loudly when dependency resolution comes back empty, so it can
never pass vacuously again.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* build: bump golangci-lint to v2.13.2 for Go 1.27
golangci-lint v2.12.0 is built with go1.26 and refuses a module whose
go directive targets 1.27 ('the Go language version used to build
golangci-lint is lower than the targeted Go version'). v2.13.2 is the
current release built on Go 1.27; make lint passes with 0 issues.
Bumped in both pin sites — the Makefile and the lint workflow step.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs: pin the v1 wire's lenient decode with fixtures when strictness lands
Review nitpick on the stacked toolchain PR, folded into ADR-0039 and
plan 2.6: the plugin wire's compatibility contract is frozen fixtures,
and the leniency itself — duplicate names accepted, invalid UTF-8
replaced — gets pinned by SDK wire fixtures when json/v2 strict ingest
lands, so the migration cannot tighten the trusted plugin wire by
accident.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>1 parent cff609e commit a7cd84a
8 files changed
Lines changed: 41 additions & 22 deletions
File tree
- .github/workflows
- dev-docs
- adr
- internal/cli
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
29 | 29 | | |
30 | 30 | | |
31 | 31 | | |
32 | | - | |
| 32 | + | |
33 | 33 | | |
34 | 34 | | |
35 | 35 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
40 | 40 | | |
41 | 41 | | |
42 | 42 | | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
32 | 32 | | |
33 | 33 | | |
34 | 34 | | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
35 | 40 | | |
36 | 41 | | |
37 | 42 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | | - | |
| 3 | + | |
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
160 | 160 | | |
161 | 161 | | |
162 | 162 | | |
163 | | - | |
| 163 | + | |
164 | 164 | | |
165 | 165 | | |
166 | 166 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
43 | 43 | | |
44 | 44 | | |
45 | 45 | | |
46 | | - | |
47 | | - | |
48 | | - | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
49 | 53 | | |
50 | 54 | | |
51 | 55 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | | - | |
| 3 | + | |
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
| |||
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
| 19 | + | |
19 | 20 | | |
20 | 21 | | |
21 | 22 | | |
| |||
32 | 33 | | |
33 | 34 | | |
34 | 35 | | |
35 | | - | |
36 | | - | |
37 | | - | |
38 | | - | |
39 | | - | |
40 | | - | |
41 | | - | |
42 | | - | |
43 | | - | |
44 | | - | |
45 | | - | |
46 | | - | |
47 | 36 | | |
48 | 37 | | |
49 | 38 | | |
| |||
70 | 59 | | |
71 | 60 | | |
72 | 61 | | |
| 62 | + | |
73 | 63 | | |
74 | 64 | | |
75 | 65 | | |
| |||
80 | 70 | | |
81 | 71 | | |
82 | 72 | | |
| 73 | + | |
83 | 74 | | |
| 75 | + | |
84 | 76 | | |
85 | 77 | | |
86 | 78 | | |
| |||
114 | 106 | | |
115 | 107 | | |
116 | 108 | | |
117 | | - | |
118 | 109 | | |
119 | 110 | | |
120 | 111 | | |
| |||
152 | 143 | | |
153 | 144 | | |
154 | 145 | | |
| 146 | + | |
155 | 147 | | |
156 | 148 | | |
157 | 149 | | |
158 | 150 | | |
159 | 151 | | |
160 | 152 | | |
161 | 153 | | |
| 154 | + | |
162 | 155 | | |
163 | 156 | | |
164 | 157 | | |
| |||
252 | 245 | | |
253 | 246 | | |
254 | 247 | | |
| 248 | + | |
255 | 249 | | |
256 | 250 | | |
257 | 251 | | |
| |||
315 | 309 | | |
316 | 310 | | |
317 | 311 | | |
| 312 | + | |
318 | 313 | | |
319 | 314 | | |
320 | 315 | | |
321 | 316 | | |
322 | 317 | | |
323 | 318 | | |
324 | 319 | | |
| 320 | + | |
325 | 321 | | |
326 | 322 | | |
327 | 323 | | |
328 | 324 | | |
329 | 325 | | |
330 | 326 | | |
| 327 | + | |
331 | 328 | | |
332 | 329 | | |
333 | 330 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
136 | 136 | | |
137 | 137 | | |
138 | 138 | | |
139 | | - | |
140 | | - | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
141 | 149 | | |
142 | 150 | | |
143 | 151 | | |
| |||
0 commit comments