Skip to content

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::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

  • A truncated or oversized <newest>-test.json or <newest>.json gives discover() == None, and gate skips with vendor_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.dep conflict → no ok).
  • newest_project_and_its_test_twin_win, caps_and_special_files (updated), symlinks_and_fifos_are_not_evidence and the coursier_gate tests stay green.

Dependencies

None. Independent of #1014.

Activity

  1. added
    bugSomething isn't working
    arch-auditFiled by a scheduled architecture audit routine (see the architecture review discussion)
    on Oct 9, 2026
  2. mikolalysenko commented on Oct 9, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Triaged: priority:p3 (scala-cli / JVM vendored gate). Not a duplicate; no open PR covers it.


    Generated by Claude Code

  3. mikolalysenko commented on Oct 9, 2026

    @mikolalysenko
    CollaboratorAuthor

    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.

  4. mikolalysenko commented on Oct 9, 2026

    @mikolalysenko
    CollaboratorAuthor

    [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

  5. mikolalysenko commented on Oct 9, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Draft PR: #1358.


    Generated by Claude Code

  6. added a commit that references this issue on Oct 9, 2026
    d762714
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    agent:claimedagent:triagedarch-auditFiled by a scheduled architecture audit routine (see the architecture review discussion)bugSomething isn't workingpriority:p3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions