docs/design/review-toolchain-adapters.md
Accepted, implemented. This document covers the extraction of the toolchain
adapter boundary: the npm-specific qwen review build-test behavior moves
behind an internal contract without changing its command-line interface or
report format. The phase that adds the first second adapter appends its own
section here.
qwen review build-test currently combines three responsibilities in one
module:
The command works well for npm repositories, but its public report models the
implementation directly as toolchain: "npm" | "unsupported". Agent 7 falls
back to prompt-directed Maven, Gradle, Cargo, Go, or Python commands when the
npm path is unsupported. That fallback is useful, but it is not deterministic
infrastructure: module selection, command choice, result parsing, timeout
classification, and failure attribution remain agent decisions.
Adding Maven and Gradle directly to build-test.ts would create a growing
conditional command rather than a stable cross-language verification boundary.
It would also make the existing npm behavior harder to protect while new
languages are added.
P0 must:
qwen review build-test CLI arguments.BuildTestReport JSON shape and all npm behavior.runBuildTest, trimOutput, buildRunEnv,
spawnTimedOut, and unresolvedWorkspaceDeps test seams.P0 does not:
test-efficacy, which remains npm workspace and Vitest specific.BuildTestReport JSON schema.Multi-toolchain repositories are an expected future requirement, but P0 does not introduce an unused aggregation model. The adapter contract is scoped to one verification target so a later orchestrator can select multiple targets without changing an individual adapter.
The npm implementation currently:
npm ci only for an npm repository with an incomplete dependency tree.npm ci for warm Yarn, pnpm, and Bun trees.The existing focused test suite is the compatibility oracle for these rules.
Add an internal ReviewToolchainAdapter interface with:
applies method that decides whether the adapter owns the repository.run method that receives normalized build/test arguments and changed file
paths and returns the existing report shape.P0 registers one built-in adapter, npm. It applies when the root
package.json describes something npm can build — workspaces, or a root
build/test script; the adapter's existing execution logic then decides
whether the npm layout and dependency state are supported or require the
structured handoff used today. The registry is a fixed array in code. There is no extension
discovery or configuration surface.
P0 deliberately does not claim to solve mixed-toolchain selection. Static repository detection alone cannot know whether an adapter will later decline because of changed-file ownership or cold dependency state. The Maven phase must design target selection from two real adapters and their module models rather than freezing a speculative priority rule now.
build-test.ts remains the CLI boundary and compatibility facade. It:
The npm-specific implementation owns package discovery, install policy, workspace selection, build ordering, widening, tests, and npm-specific notes.
P0 deliberately keeps:
toolchain: "npm" | "unsupported"
Changing this to a new generic schema in the same refactor would require coordinated edits to Agent 7, base-tree, test-plan, test-delta, tests, and any external scripts consuming the report. The adapter boundary does not require that migration.
A later Maven/Gradle phase can widen the discriminant while adding the first new behavior, with tests for each downstream consumer.
Command execution, output trimming, timeout detection, and environment shaping remain shared exports from the command module in P0 because adjacent review commands and existing tests consume them. The npm-specific dependency widening helper moves with the npm adapter and is re-exported from the command module for compatibility.
The adapter receives the injectable executor already used by the existing unit tests. It does not import the command's runtime executor, so the dependency stays one-way: the command selects the adapter and passes execution in. Type-only imports may reference the existing report types without creating a runtime cycle. This preserves deterministic tests without spawning npm.
P0 changes:
packages/cli/src/commands/review/build-test.ts
packages/cli/src/commands/review/lib/toolchain.ts
packages/cli/src/commands/review/lib/npm-toolchain.ts
packages/cli/src/commands/review/lib/npm-toolchain.test.ts
packages/cli/src/commands/review/build-test.test.ts
Focused tests must prove:
runBuildTest.Verification commands:
cd packages/cli && npx vitest run src/commands/review/
npm run typecheck
The boundary exists so a second language lands as a registration rather than
another branch in build-test.ts. Whichever comes first — Maven, Gradle —
should prefer a checked-in wrapper, take its project model from the build tool
itself rather than re-deriving one from the manifests, select the projects the
diff changed, and parse the JUnit XML the run produced. A build it cannot
model must fail closed to an unsupported handoff, never to a partial green.
Istanbul/LCOV and JaCoCo should normalize into a language-independent changed-line and changed-branch coverage model. Coverage numbers are evidence for a concrete untested behavior, not an automatic Critical threshold.
A later orchestration layer may detect multiple verification roots and invoke one adapter per target. It should aggregate evidence while preserving each command's toolchain, root, module, and infrastructure status. This phase avoids specifying that before two real adapters demonstrate the common boundary.
build-test suite and
explicit report-shape assertions.package.json can scope something — workspaces or a root build/test script.
A package.json with neither workspaces nor build/test scripts (husky, a lint
config, a script-less docs site) does not apply, so a future adapter can own
such a root alone rather than losing it to a manifest npm cannot scope.Report-schema widening for a second toolchain — the toolchain discriminant
and any per-command classification flags — is deferred to the phase that
introduces that behavior, along with multi-toolchain aggregation.