Skip to content

feat: bring facts generation up to date with the shipping pipeline - #7

Open
Jeppe Fredsgaard Blaabjerg (jfblaa) wants to merge 5 commits into
mainfrom
jfblaa/bring-facts-generation-up-to-date
Open

feat: bring facts generation up to date with the shipping pipeline#7
Jeppe Fredsgaard Blaabjerg (jfblaa) wants to merge 5 commits into
mainfrom
jfblaa/bring-facts-generation-up-to-date

Conversation

@jfblaa

@jfblaa Jeppe Fredsgaard Blaabjerg (jfblaa) commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

This package was seeded from socket-cli's manifest emitters and has not tracked it since, so the two describe the same bytes differently in both directions. The bar for this work is that socket-cli can swap its vendored emitters for this package without its own tests or snapshots moving.

Direction of truth: socket-cli for the shared pipeline, this package for the dotnet emitter, config-glob.mts, and configsByProject.

Evidence the swap is a no-op

All four of socket-cli's shared-pipeline suites are ported verbatim under test/repo/unit/parity/ and pass with zero assertion edits — only import paths changed. An assertion that has to be edited to pass is a divergence, not a test fix.

Ported suite Tests
sidecar.test.mts 10
exclude-paths-glob.test.mts 26
resolution-report-render.test.mts + assemble.test.mts 12

35 test files / 283 tests, zero skips. preflight --tests clean. check --all reports no failures the default branch does not already report.

Two defects found and fixed

The sbt global base was deleted before the caller could use it. sbt provisions the Scala toolchain under <global base>/boot, and withFiles artifact paths point into it. The invocation always wrapped in a self-deleting temp dir, so an sbt run with withFiles returned paths under a directory it had already removed. The caller can now own that directory.

This package passed its own build-tool properties before the caller's options. Every tool resolves a repeated property last-one-wins — measured on Maven -D, Gradle -P and the JVM's -D, and read from the dotnet tool's own argument parser. A caller option could therefore unload the emitter and produce an empty SBOM at exit 0. Properties now go last, which also covers alias spellings a collision check cannot enumerate: Maven accepts --define as a synonym for -D.

Sidecar wire format

Now keyed by the absolute .socket.facts.json path, with entries carried through verbatim from the facts SBOM plus resolved paths. The key is the scope, which is what per-subproject reachability reads, and there is no cross-reactor dedup.

was is
group namespace
ext qualifiers.ext
classifier qualifiers.classifier
ecosystem type (required)
ResolvedComponent[] keyed record
Exclude paths, and dotnet support for them

One compiler; each emitter only compiles the patterns it is handed. Three per-language implementations are gone — NIO PathMatcher construction, /** variant expansion and a zeroDepthVariants BFS in each of Groovy, Scala and Java — for a net 70 fewer lines.

The dotnet tool gains --exclude-paths, so a wholly excluded project emits no records, matching the JVM emitters. Covered end to end against a real dotnet build, including that a non-matching pattern drops nothing — which is what proves the pattern is doing the work rather than an unrelated load failure.

Workspace enumeration

The workspaces emitter family moves in, and both lifecycle participants ship in the one shaded jar. A test asserts that against the packaged bytes and the sisu index, because a class present but unindexed is silently inert — and Maven with no participant emits an empty SBOM, which reads downstream as "no dependencies" rather than as a failure.

Both emitter families now share one parameterized invocation path, so their property sets and argument order cannot drift apart.

Conformance and gate changes
  • A dotnet conformance fixture holds that resolver to resolve-once, diffed against MSBuild's own property evaluation rather than a golden file. Its version is generated per run, so it appears in no committed file and no static parse can satisfy it.
  • The repo gate now type-checks src/ and test/, which the fleet check config covers neither of. It found two real type errors on its first run.
  • A missing built emitter asset is now built on demand instead of silently skipping 7 conformance tests on every fresh clone.
  • Docs: private-repo references removed, and the trust-boundary document now states the consumer's vetting obligation directly.

Not in this PR

Release preparation.

Consumer-side vetting of bin and opts stays with the consumer, unchanged by this PR — see docs/agents.md/repo/trust-boundary.md.


Note

High Risk
Breaking sidecar wire format and build-invocation ordering affect scan completeness and reachability handoff; requires coordinated consumer release and careful rollout.

Overview
Brings this package’s facts-generation pipeline in line with the shipping socket-cli emitters so a drop-in swap should not move downstream tests. The largest change is the resolved-paths sidecar: it is no longer a flat ResolvedComponent[] but a record keyed by absolute .socket.facts.json paths, with SBOM-shaped projects / components entries plus optional targets / sources, updated validators, and serialization that scopes reachability per facts file without cross-reactor dedup.

Exclude paths are centralized in exclude-paths-glob.mts (shared with config globs via glob-syntax.mts); JVM emitters and the dotnet tool now receive pre-compiled regex sources instead of duplicating NIO glob logic. Dotnet gains --exclude-paths and skips wholly excluded projects like the JVM tools.

Adds a workspace enumeration path (Gradle/Maven/sbt init scripts/plugins plus Maven socket-workspaces participant in the same jar) exposed as enumerateWorkspaces, sharing invoke-build-tool.mts with full facts runs. Invocation fixes pass caller build-tool options before this package’s properties (so Maven --define cannot unload the extension) and allow a caller-owned sbt tmpDir so withFiles paths under boot survive the call. Artifact-path assembly prefers a module’s build output over its published jar when both exist.

Conformance and gates expand with socket-cli parity unit tests, dotnet dynamic-version and exclude-paths compat suites, workspace enumeration e2e, sources-are-type-checked for src//test/, and on-demand emitter builds in compat tooling. Docs and CLAUDE.md describe the new contract and consumer trust obligations; fleet/gitignore and permission tweaks are ancillary.

Reviewed by Cursor Bugbot for commit 4c77702. Configure here.

Reconciles the shared pipeline with socket-cli's current generation, so a
consumer can swap its vendored emitters for this package without its own tests
or snapshots moving. All four of socket-cli's shared-pipeline suites are ported
verbatim and pass with no assertion edits.

Squashed from 23 commits: intermediate states carried tracker references, and
this repository is public, so they must not reach its history. The logical
chunks were:

- sidecar: keyed by absolute facts-file path, with entries carried through
  verbatim from the facts SBOM plus resolved paths, and no cross-reactor dedup
- exclude paths: one compiler, and each emitter only compiles the patterns it
  is handed; three per-language glob implementations deleted
- dotnet: honours excludePaths, so a wholly excluded project emits no records
- workspaces: the enumeration emitter family moves in, both lifecycle
  participants ship in the one shaded jar, and both emitter families share one
  parameterized invocation path
- pipeline: a coordinate that is both a resolved node and a first-party project
  reports the project's output roots, not its published jar
- sbt: the caller can own the global base, which withFiles artifact paths point
  into and which was previously deleted before the caller could read them
- invocation: this package passes its own build-tool properties AFTER the
  caller's options, because every tool resolves a repeated property
  last-one-wins and a caller option could otherwise unload the emitter
- gates: the repo check now type-checks src/ and test/, which the fleet check
  config covers neither of; a missing built emitter asset is built on demand
  instead of silently skipping conformance tests
- conformance: a dotnet fixture holds that resolver to resolve-once, diffed
  against MSBuild's own property evaluation rather than a golden file
- docs: private-repo references removed, and the trust boundary states the
  consumer's vetting obligation directly

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is ON. A cloud agent has been kicked off to fix the reported issue.

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 4c77702. Configure here.

Comment thread src/pipeline/sidecar.mts
acc: SidecarAccumulator,
facts: SocketFactsSbom,
artifactPaths: ResolvedArtifactPaths,
factsFile: string,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fuzz sidecar missing facts key

Medium Severity

accumulateSidecar now requires a factsFile key for the new path-scoped sidecar, but the records fuzz target still calls it with three arguments. At runtime that stores under undefined, so serializeSidecar emits a bucket keyed by "undefined". The fuzz then validates that wrong shape and no longer guards the absolute .socket.facts.json key contract this PR introduced. The new sources-are-type-checked gate only includes test/**/*.mts, so this .ts fuzz file is never type-checked either.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 4c77702. Configure here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 976de4f — both halves were real.

The call. accumulateSidecar now gets a factsFile, and the fuzz asserts the payload has exactly that one key. The second part matters because the validator accepts any string key, so the fuzz was passing on a payload keyed literally "undefined" and guarding nothing about the contract this PR introduces. accumulateSidecar always sets exactly one key, so the assertion is safe for arbitrary input.

The gate. I tried widening it to test/**/*.ts and backed it out: the fuzz framework there is an optional devDependency that is not installed, and the .ts extension lands in CommonJS resolution while the tree is .mts/ESM — so including it turned the gate red on a fresh clone for unrelated reasons. It stays .mts-only, with the gap recorded in the check script rather than left implicit.

Verified by running the fuzz body logic through vitest on empty, garbage and real records input, since the fuzz runner cannot run locally.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant