ADR 0003 — What counts as a Scala project root
- Status: partly superseded by ADR-0004
- Date: 2026-09-04
- Applies to:
ProjectRootDiscovery - Superseded parts: the install-flow decisions here (global install as the documented default,
and a fix to the now-deleted
scripts/install.sh) are replaced wholesale by ADR-0004. The fail-closed behaviour is amended by it too: discovery failure no longer exits the process. What remains live is the discovery rule below.
Context
Generated configs pass . as the server root, relying on every supported client spawning the server
with cwd = the project holding the config. The server did zero validation of that: . went
straight to SemanticIndex.fromProject. Trusting cwd blindly is tolerable when the launcher lives
in the project and was verified once by hand; it is not once one shared binary is registered for
every project on the machine, because a client with an unexpected cwd then indexes the wrong
directory and returns confident-looking count: 0 answers instead of an error.
Decision
Validate only when the root argument is the default "." — an explicit root is always trusted as
given, which preserves pinned-path configs and anything scripting the server directly.
- cwd carries a build marker → use cwd. The common case; all supported clients already do this.
Markers:
build.mill,build.sc,build.sbt,pom.xml,build.gradle,build.gradle.kts,project.scala, or nestedproject/build.properties. - Otherwise walk up — at most 8 levels, stopping at
$HOMEor the filesystem root — and use the first ancestor with a marker. Covers a client spawned inside a monorepo subpackage. - Otherwise report failure rather than indexing cwd anyway.
SCALASEMANTIC_SKIP_ROOT_CHECK=1opts back into unconditional cwd for a build tool with no recognised marker.
/repo/build.sbt ← marker
/repo/modules/core/ ← cwd → walks up, resolves /repo
/tmp/some-python-app/ ← cwd → no marker within range: failure
How step 3 reports is ADR-0004's change: it was a sys.exit(1) at startup; it is now an error
returned from each tool call, so the server stays connectable in non-Scala directories.
Rejected
- Match on
.gitalone — cheap but false-positive-prone: a monorepo super-repo's.gitsits above the actual Scala module, and a dotfiles repo in$HOMEwould match. Build markers are specific to "this is a Scala project root". - Never fail, always fall back to cwd — reintroduces exactly the silent-wrong-root failure this exists to close.
${workspaceFolder}-style config variables instead of runtime discovery — only the VS Code family supports them; Claude Code, Codex, Gemini CLI and Antigravity do not.
Consequences
- A misconfigured client gets a clear error instead of a silently wrong index.
- A project whose build tool uses none of the markers must add one (an empty
build.sbtis needed for SemanticDB anyway) or setSCALASEMANTIC_SKIP_ROOT_CHECK=1.