You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
#131 fixed a bug where no Scala 3 artifact had ever produced a docstring from a released native binary. The entire test suite passed throughout, and it would still pass if the bug were reintroduced.
Two distinct gaps let that happen.
1. No test asserts docstrings appear at all
grep -r docstring lib/test/src cli/test/src returns nothing. DocstringExtractor has no direct coverage and no integration-level assertion that a docstring reaches stdout.
This is cheap to close, and the fixtures are already set up for it — e.g. fixtureScala3/src/cellar/fixture/scala3/CellarTrait.scala carries /** Sealed ADT hierarchy for testing sealedChildren extraction. */, so an IntegrationTest case can fetch it from the local m2 repo and assert the text appears.
Note this alone would not have caught #131: the JVM path worked correctly. It closes a real coverage hole, not this bug class.
2. Nothing ever runs the native binary
This is the gap that matters. The failure mode of #131 was JVM works, native-image silently differs — invisible to ./mill _.test, which only ever runs on the JVM.
ci.yml runs ./mill _.test and nix flake show. cli.nativeImage is built only in snapshot.yml and release.yml, and in both cases the binary is packaged and published without being executed once. A binary that cannot resolve a stdlib, or cannot start at all, ships green.
Proposal
Add a native smoke job to ci.yml: build cli.nativeImage, then run the resulting binary and assert on real output. At minimum:
--version exits 0 and reports native-image as the runtime (CellarApp.scala:18 already distinguishes it)
get-external on a Scala 3 artifact prints a docstring — the Fix missing docstrings in the native-image binary #131 regression, and the one assertion that requires the coursier stdlib fetch to have resolved a real version
get-external on a Java artifact prints a signature — exercises the bundled-JRE path (bundledJreJar, build.mill:111), which is native-only by construction and equally untested
Scope and cost notes:
One target (linux-x86_64) is enough; the divergences are runtime/resource-embedding issues, not per-arch. No need to mirror snapshot.yml's 4-way matrix.
The native build takes ~90s locally warm, longer cold in CI once the GraalVM JVM download is counted. Put it in its own job with its own timeout-minutes rather than inside the existing test job, which is capped at 15.
Prefer the locally published fixtures (./mill publishFixtures) over a third-party coordinate for the docstring assertion, so CI does not depend on a Maven Central artifact staying put. The stdlib fetch still hits the network either way, which is the point.
Other native-only surfaces worth asserting once the harness exists
These share the #131 failure mode — no JVM test can see them:
resource embedding generally (this bug was compiler.properties not being embedded while library.properties was)
reflection/proxy config reachable only at image runtime
--java-home fallback and JRT filesystem behavior (graal#10013 is already worked around by hand)
slf4j-nop / logging init under native-image
Acceptance
CI fails if a native binary is built that cannot print a Scala 3 docstring.
Problem
#131 fixed a bug where no Scala 3 artifact had ever produced a docstring from a released native binary. The entire test suite passed throughout, and it would still pass if the bug were reintroduced.
Two distinct gaps let that happen.
1. No test asserts docstrings appear at all
grep -r docstring lib/test/src cli/test/srcreturns nothing.DocstringExtractorhas no direct coverage and no integration-level assertion that a docstring reaches stdout.This is cheap to close, and the fixtures are already set up for it — e.g.
fixtureScala3/src/cellar/fixture/scala3/CellarTrait.scalacarries/** Sealed ADT hierarchy for testing sealedChildren extraction. */, so anIntegrationTestcase can fetch it from the local m2 repo and assert the text appears.Note this alone would not have caught #131: the JVM path worked correctly. It closes a real coverage hole, not this bug class.
2. Nothing ever runs the native binary
This is the gap that matters. The failure mode of #131 was JVM works, native-image silently differs — invisible to
./mill _.test, which only ever runs on the JVM.ci.ymlruns./mill _.testandnix flake show.cli.nativeImageis built only insnapshot.ymlandrelease.yml, and in both cases the binary is packaged and published without being executed once. A binary that cannot resolve a stdlib, or cannot start at all, ships green.Proposal
Add a native smoke job to
ci.yml: buildcli.nativeImage, then run the resulting binary and assert on real output. At minimum:--versionexits 0 and reportsnative-imageas the runtime (CellarApp.scala:18already distinguishes it)get-externalon a Scala 3 artifact prints a docstring — the Fix missing docstrings in the native-image binary #131 regression, and the one assertion that requires the coursier stdlib fetch to have resolved a real versionget-externalon a Java artifact prints a signature — exercises the bundled-JRE path (bundledJreJar, build.mill:111), which is native-only by construction and equally untestedScope and cost notes:
linux-x86_64) is enough; the divergences are runtime/resource-embedding issues, not per-arch. No need to mirrorsnapshot.yml's 4-way matrix.timeout-minutesrather than inside the existingtestjob, which is capped at 15../mill publishFixtures) over a third-party coordinate for the docstring assertion, so CI does not depend on a Maven Central artifact staying put. The stdlib fetch still hits the network either way, which is the point.Other native-only surfaces worth asserting once the harness exists
These share the #131 failure mode — no JVM test can see them:
compiler.propertiesnot being embedded whilelibrary.propertieswas)--java-homefallback and JRT filesystem behavior (graal#10013 is already worked around by hand)slf4j-nop/ logging init under native-imageAcceptance
🤖 Generated with Claude Code