Skip to content

feat(validate): read Python, Rust, PHP and Java dependency files - #10

Merged
protonspy merged 1 commit into
mainfrom
feat/stack-more-ecosystems
Aug 2, 2026
Merged

protonspy merged 1 commit into
mainfrom
feat/stack-more-ecosystems

Conversation

@protonspy

Copy link
Copy Markdown
Owner

The stack check knew go.mod and package.json. Everything else got zero enforcement: dependencies() came back empty, and Stack returned before it ever looked for docs/stack.md. Silence is the correct answer for a file scc cannot read — it is why a Python project never failed — but it also meant the rule the knowledge base is built on held in exactly two ecosystems.

Five readers

File What it reads
requirements.txt one requirement per line, PEP 508 names. Options (-r, -e, --hash), comments, markers, extras, direct URLs and backslash continuations all fall out
pyproject.toml PEP 621 ([project] dependencies, optional-dependencies), PEP 735 (dependency-groups), and Poetry's tables including per-group
Cargo.toml dependencies, dev-, build-, [target.'cfg(…)'.dependencies], [workspace.dependencies], and the [dependencies.<crate>] long form
composer.json require and require-dev, minus the platform keys (php, ext-*, lib-*)
pom.xml project/dependencies/dependency and nothing else — <dependencyManagement> pins versions rather than adopting, and a <profile>'s dependencies may not apply

build.gradle, Gemfile, mix.exs and Package.swift are deliberately absent: those manifests are executable code, and reading one honestly would mean evaluating it.

The TOML reader is not a TOML parser

pyproject.toml and Cargo.toml share one, and its doc comment says outright that it must not become a parser. It recognizes exactly two shapes — a table header alone on a line, and key = value under it — which is how every dependency table in the wild is written, and ignores everything else. An array that never closes yields nothing rather than half a dependency list.

Two behavior changes worth reviewing

  • Either separator counts as documented. PyPI treats Flask-SQLAlchemy and flask_sqlalchemy as one project. Each candidate spelling is now also tried with the separators swapped — and an extra spelling can only remove a finding, never add one, which is what makes the generosity safe.
  • The silence contract is now asserted, not emergent. TestAnEcosystemWithNoReaderIsNotChecked drops a Gemfile, a mix.exs and a build.gradle in a root and requires zero findings. That property was true before this PR by accident of control flow; it is now a test.

Structure

The readers move to internal/validate/stack_manifests.go. The rule is fixed; the readers grow whenever somebody uses an ecosystem scc cannot see, and that difference in tempo is why they no longer share a file. Its header states the three rules every reader obeys — absent is not an error, direct dependencies only, and what cannot be read confidently is not read at all.

gofmt, go vet, go test ./... clean.

🤖 Generated with Claude Code

https://claude.ai/code/session_01GchWF6iL5PjTdXAT8H3mhb

The stack check only knew go.mod and package.json, so a Python or Java
project got no findings at all: dependencies() came back empty and Stack
returned before it ever looked for docs/stack.md. Silence is the right
answer for an ecosystem scc cannot read, but it also meant the rule the
whole knowledge base is built on held in exactly two ecosystems.

Five readers, each with a parse that cannot be wrong:

  requirements.txt  one requirement per line, PEP 508 names; options,
                    markers, extras and direct URLs all fall out
  pyproject.toml    PEP 621, PEP 735 and Poetry's own tables
  Cargo.toml        dependencies, dev-, build-, per-target, workspace,
                    and the [dependencies.<crate>] long form
  composer.json     require and require-dev, minus the platform keys
  pom.xml           project/dependencies/dependency and nothing else,
                    so dependencyManagement and profiles stay out

The two TOML files share a reader that recognizes exactly two shapes, a
table header and a key = value line, and ignores everything else. It is
not a TOML parser and the comment says so: an array that never closes
yields nothing rather than half a dependency list.

Also: a dependency documented with the other separator now counts. PyPI
treats Flask-SQLAlchemy and flask_sqlalchemy as one project, and an extra
spelling can only remove a finding, never add one.

The readers move to stack_manifests.go. The rule is fixed; the readers
grow whenever somebody uses an ecosystem scc cannot see.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GchWF6iL5PjTdXAT8H3mhb
@protonspy
protonspy merged commit b9cd3fc into main Aug 2, 2026
4 checks passed
@protonspy
protonspy deleted the feat/stack-more-ecosystems branch August 2, 2026 23:59
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