Repository navigation
Vendored scala-cli gate passes a version conflict when a Bloop project file is truncated or oversized #1270
Description
[agent] Filed by the scheduled architecture audit routine (ecosystems and formats). Register: discussion #560 register.
Kind: bug. Source: new finding; register row E95.
Problem
The vendored JVM gates read two kinds of resolution evidence, and they disagree about evidence they can't read.
- sbt fails closed.
formats::sbt::evidence::resolutionreturnsNonewhen any evidence file is malformed ("a truncated write, an unknown schema: a project whose record cannot be read could hide a conflicting version"). Over a cap, the IO side yields "no evidence at all (never a partial resolution…)". The gate then skips withvendor_sbt_no_resolution_evidence.malformed_evidence_is_fail_closedpins this behavior. - scala-cli fails open.
scala_evidence::discovercontinues past a Bloop project file overMAX_FILE_BYTES(L202-L205), an unreadable file, or one thatparse_blooprejects (L210-L216). It then builds the resolution from the files that remain.caps_and_special_filespins "an oversized file is skipped".
When the skipped file is the newest project's -test twin, coursier_gate::gate runs check_new over the main project's resolution alone. A //> using test.dep at another version is never seen, so vendor_scala_cli_version_conflict does not fire. When the skipped file is the newest main project, an older input set's project becomes "newest" instead.
Permalinks (main @ a80b89e):
- sbt evidence resolution, L207-L245
- sbt evidence IO caps, L1-L14
- scala-cli discover loop, L186-L218
- scala-cli caps test, L712-L733
- coursier gate, L72-L113
Proof (executed twice on a80b89e). I used a throwaway test in scala_evidence::tests, then reverted it. The workspace has main.scala and main.test.scala. sc_p.json resolves g:a@1.0.0, and sc_p-test.json resolves g:a@2.0.0. Both evidence files are newer than the sources. The gate ran for g:a@1.0.0:
sc_p-test.json |
discover().files |
versions of g:a |
gate |
|---|---|---|---|
| intact | both | 1.0.0, 2.0.0 |
vendor_scala_cli_version_conflict |
| truncated to half | sc_p.json only |
1.0.0 |
ok |
So vendoring proceeds, and the test classpath keeps resolving 2.0.0, which the vendored tree does not serve. The sbt reader refuses the same truncated-record situation.
Symptoms
None filed. Related: E69/#1014 (scala-cli marker lists) and #690 (sbt/scala-cli support).
Impact
Low frequency, but it is the "a project whose record cannot be read could hide a conflicting version" case that the sbt side explicitly guards against. A scala-cli build interrupted mid-write (Bloop writes the twin after the main project) or a large classpath over 8 MiB is enough to trigger it. The two JVM evidence readers keep one policy each, and that is how they drift.
Proposed change
In scala_evidence::discover, treat a .json entry that is oversized, unreadable or not a Bloop project as fail-closed evidence (None), the same way formats::sbt::evidence::resolution does. Alternatively, fail closed only when the skipped file's base name joins the chosen group. That needs the name from the file name, because its content is unreadable. Non-.json entries and other workspaces' projects stay ignored. Delete the "skipping" branch, and change caps_and_special_files to assert None for the right reason.
Size and scope
crawlers/scala_evidence.rs (~10 production lines plus tests). Out of scope: the sbt reader, the coursier gate's rules, and the Bloop schema.
Acceptance criteria
- A truncated or oversized
<newest>-test.jsonor<newest>.jsongivesdiscover() == None, andgateskips withvendor_scala_cli_resolution_missing. An already vendored GAV still re-plans, as it does today. - A regression test with the table above (truncated twin plus a
test.depconflict → nook). -
newest_project_and_its_test_twin_win,caps_and_special_files(updated),symlinks_and_fifos_are_not_evidenceand thecoursier_gatetests stay green.
Dependencies
None. Independent of #1014.
Activity
audit-ecosystems: run 20261009T125415Z (E78 fixed, E95 filed #1270)
mikolalysenko commentedon Oct 9, 2026
[agent] Triaged: priority:p3 (scala-cli / JVM vendored gate). Not a duplicate; no open PR covers it.
Generated by Claude Code
mikolalysenko commentedon Oct 9, 2026
v5 triage: P3, not a release blocker. Truncated/oversized Bloop evidence is malformed-input/crash hardening, outside the requested ordinary single-instance workflow. Keep P3, not a v5 blocker.
This follows the maintainer's release scope: one normally completing CLI instance, prioritizing valid-lockfile patch/install behavior, compatibility, and actionable CLI UX.
mikolalysenko commentedon Oct 9, 2026
[agent] Claiming this issue for the architecture refactor routine (highest leverage: the top free candidate while 76 open PRs hold the larger refactors' files; it gives the two JVM evidence readers one fail-closed policy). Branch: arch-refactor/1270-scala-evidence-fail-closed. Claim-ID: 2026-10-09T17:56:13Z-7c3e1a
Generated by Claude Code
mikolalysenko commentedon Oct 9, 2026
[agent] Draft PR: #1358.
Generated by Claude Code
Fail closed on unreadable scala-cli evidence