[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::resolution returns None when 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 with vendor_sbt_no_resolution_evidence. malformed_evidence_is_fail_closed pins this behavior.
- scala-cli fails open.
scala_evidence::discover continues past a Bloop project file over MAX_FILE_BYTES (L202-L205), an unreadable file, or one that parse_bloop rejects (L210-L216). It then builds the resolution from the files that remain. caps_and_special_files pins "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
Dependencies
None. Independent of #1014.
[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.
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_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
-testtwin,coursier_gate::gaterunscheck_newover the main project's resolution alone. A//> using test.depat another version is never seen, sovendor_scala_cli_version_conflictdoes not fire. When the skipped file is the newest main project, an older input set's project becomes "newest" instead.Permalinks (main @ a80b89e):
Proof (executed twice on a80b89e). I used a throwaway test in
scala_evidence::tests, then reverted it. The workspace hasmain.scalaandmain.test.scala.sc_p.jsonresolvesg:a@1.0.0, andsc_p-test.jsonresolvesg:a@2.0.0. Both evidence files are newer than the sources. The gate ran forg:a@1.0.0:sc_p-test.jsondiscover().filesg:agate1.0.0,2.0.0vendor_scala_cli_version_conflictsc_p.jsononly1.0.0okSo 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.jsonentry that is oversized, unreadable or not a Bloop project as fail-closed evidence (None), the same wayformats::sbt::evidence::resolutiondoes. 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-.jsonentries and other workspaces' projects stay ignored. Delete the "skipping" branch, and changecaps_and_special_filesto assertNonefor 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
<newest>-test.jsonor<newest>.jsongivesdiscover() == None, andgateskips withvendor_scala_cli_resolution_missing. An already vendored GAV still re-plans, as it does today.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.