-
Notifications
You must be signed in to change notification settings - Fork 3k
refactor(review): extract the toolchain adapter boundary #8776
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
d24b9e6
374105b
ece4bb6
864251f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,234 @@ | ||
| # Review toolchain adapters | ||
|
|
||
| ## Status | ||
|
|
||
| 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. | ||
|
|
||
| ## Problem | ||
|
|
||
| `qwen review build-test` currently combines three responsibilities in one | ||
| module: | ||
|
|
||
| 1. Reading the review plan and selecting changed files. | ||
| 2. Deciding which repository toolchain can be verified deterministically. | ||
| 3. Implementing npm workspace installation, affected-package selection, | ||
| dependency widening, build execution, test execution, and result reporting. | ||
|
|
||
| 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. | ||
|
|
||
| ## Goals | ||
|
|
||
| P0 must: | ||
|
|
||
| - Introduce a small internal toolchain adapter contract. | ||
| - Move npm repository detection and npm build/test execution behind the npm | ||
| adapter. | ||
| - Preserve the `qwen review build-test` CLI arguments. | ||
| - Preserve the existing `BuildTestReport` JSON shape and all npm behavior. | ||
| - Preserve the exported `runBuildTest`, `trimOutput`, `buildRunEnv`, | ||
| `spawnTimedOut`, and `unresolvedWorkspaceDeps` test seams. | ||
| - Keep unsupported repositories on the existing Agent 7 fallback path. | ||
| - Make Maven and Gradle additions possible without modifying command routing or | ||
| verdict composition. | ||
|
|
||
| ## Non-goals | ||
|
|
||
| P0 does not: | ||
|
|
||
| - Execute Maven or Gradle. | ||
| - Support multiple toolchains in one repository. | ||
| - Define a third-party plugin API or dynamic adapter loading. | ||
| - Parse test coverage artifacts such as Istanbul, LCOV, or JaCoCo. | ||
| - Generalize `test-efficacy`, which remains npm workspace and Vitest specific. | ||
| - Change Agent 7 prompts, findings, verdicts, or coverage gates. | ||
| - Change the `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. | ||
|
|
||
| ## Current behavior to preserve | ||
|
|
||
| The npm implementation currently: | ||
|
|
||
| - Treats a root package with build or test scripts as a single package. | ||
| - Supports the modeled npm workspace glob shapes. | ||
| - Selects changed workspaces from plan file paths. | ||
| - Builds affected workspaces and their reverse dependents. | ||
| - Widens or reorders the build set when the compiler names an undeclared | ||
| workspace dependency. | ||
| - Tests the affected workspaces and every workspace declared to depend on | ||
| them that defines a test script. | ||
| - Runs `npm ci` only for an npm repository with an incomplete dependency tree. | ||
| - Avoids `npm ci` for warm Yarn, pnpm, and Bun trees. | ||
| - Classifies unsupported layouts as a handoff, not a successful verification. | ||
| - Classifies timeouts, insufficient disk, and unusable installs as | ||
| infrastructure rather than PR findings. | ||
| - Removes failed intermediate widening attempts from the final evidence. | ||
| - Supports build-only verification for merge-base trees. | ||
|
|
||
| The existing focused test suite is the compatibility oracle for these rules. | ||
|
|
||
| ## Design | ||
|
|
||
| ### Adapter contract | ||
|
|
||
| Add an internal `ReviewToolchainAdapter` interface with: | ||
|
|
||
| - An `applies` method that decides whether the adapter owns the repository. | ||
| - A `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. | ||
|
|
||
| ### Command boundary | ||
|
|
||
| `build-test.ts` remains the CLI boundary and compatibility facade. It: | ||
|
|
||
| 1. Resolves the worktree. | ||
| 2. Reads and validates changed file paths from the review plan. | ||
| 3. Selects the sole applicable built-in adapter, failing closed to the | ||
| unsupported report when zero or more than one apply. | ||
| 4. Calls the adapter. | ||
| 5. Emits the unchanged JSON report. | ||
|
|
||
| The npm-specific implementation owns package discovery, install policy, | ||
| workspace selection, build ordering, widening, tests, and npm-specific notes. | ||
|
|
||
| ### Report compatibility | ||
|
|
||
| P0 deliberately keeps: | ||
|
|
||
| ```text | ||
| 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. | ||
|
|
||
| ### Shared execution primitives | ||
|
|
||
| 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 | ||
|
Comment on lines
+142
to
+144
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Suggestion] R1-15: The doc states command execution "remain[s] shared exports from the command module … because adjacent review commands and existing tests consume them", but the executor Suggested fix: rewrite as "Output trimming, timeout detection, and environment shaping remain shared exports … Command execution stays private to the command module and reaches the adapter through the injected 中文说明文档声称命令执行「仍是命令模块的共享导出……因为相邻 review 命令和现有测试会消费它们」,但执行器 修复建议: 改写为 「Output trimming, timeout detection, and environment shaping remain shared exports … Command execution stays private to the command module and reaches the adapter through the injected — qwen3.8-max via Qwen Code /review (v0.21.8) |
||
| 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. | ||
|
|
||
| ## Files | ||
|
|
||
| P0 changes: | ||
|
Comment on lines
+154
to
+156
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Suggestion] R1-7: The Suggested fix: add a bullet: 中文说明
修复建议: 补一条: — qwen3.8-max via Qwen Code /review (v0.21.8) |
||
|
|
||
| - `packages/cli/src/commands/review/build-test.ts` | ||
| - Retains CLI routing and compatibility exports. | ||
| - Selects and invokes the built-in adapter. | ||
| - `packages/cli/src/commands/review/lib/toolchain.ts` | ||
| - Defines the internal adapter and detection contracts. | ||
| - Selects the sole applicable adapter (zero or more than one fails closed). | ||
| - `packages/cli/src/commands/review/lib/npm-toolchain.ts` | ||
| - Owns npm detection and the existing npm verification algorithm. | ||
| - `packages/cli/src/commands/review/lib/npm-toolchain.test.ts` | ||
| - Pins adapter selection and contract-level behavior. | ||
| - `packages/cli/src/commands/review/build-test.test.ts` | ||
| - Remains the end-to-end compatibility suite for the command facade. | ||
|
|
||
| ## Testing | ||
|
|
||
| Focused tests must prove: | ||
|
|
||
| 1. An npm workspace selects the npm adapter. | ||
| 2. A single-root npm package selects the npm adapter. | ||
| 3. A non-npm repository produces the existing unsupported report. | ||
| 4. An unmodeled npm layout remains unsupported rather than returning a false | ||
|
Comment on lines
+176
to
+178
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Suggestion] R1-13: This Testing item claims a non-npm repository "produces the existing unsupported report", but for roots with no Suggested fix: either emit the pre-PR note string verbatim from the generic fallback (restoring byte-compat), or amend the doc's Goals/Testing wording to say the no- 中文说明该 Testing 条目声称非 npm 仓库会「产生既有的 unsupported 报告」,但对完全没有 修复建议: 要么让通用兜底逐字输出 PR 前的 note 字符串(恢复字节级兼容),要么修订文档 Goals/Testing 的措辞说明无 — qwen3.8-max via Qwen Code /review (v0.21.8) |
||
| green result. | ||
| 5. Existing build ordering, widening, install, timeout, disk, and test behavior | ||
| remains unchanged through `runBuildTest`. | ||
| 6. The serialized report shape remains unchanged. | ||
|
|
||
| Verification commands: | ||
|
|
||
| ```bash | ||
| cd packages/cli && npx vitest run src/commands/review/ | ||
| npm run typecheck | ||
| ``` | ||
|
|
||
| ## Future phases | ||
|
|
||
| ### A second toolchain | ||
|
|
||
| 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. | ||
|
|
||
| ### Coverage artifacts | ||
|
|
||
| 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. | ||
|
|
||
| ### Multiple toolchains | ||
|
|
||
| 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. | ||
|
|
||
| ## Risks | ||
|
|
||
| - **Accidental report drift:** protected by the existing `build-test` suite and | ||
| explicit report-shape assertions. | ||
| - **Adapter abstraction without behavior:** this phase is justified only if npm | ||
| detection and execution move behind the adapter rather than adding an empty | ||
| interface around unchanged branching. | ||
| - **Premature generalization:** the contract intentionally excludes coverage, | ||
| mutation, CI discovery, and multi-toolchain aggregation. | ||
| - **False applicability:** the npm adapter applies only when the root | ||
| `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. | ||
|
|
||
| ## Open questions | ||
|
|
||
| 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. | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[Suggestion] R1-16: The documented command-boundary flow (and the Goals bullet "Move npm repository detection … behind the npm adapter") omits that on the zero-adapters path
build-test.tskeeps its own npm-shapedness detection and calls the npm adapter directly, outside the registry:if (existsSync(join(root, 'package.json'))) return npmToolchainAdapter.run(runArgs);. — Failure scenario: the numbered contract says zero adapters → unsupported report, full stop, and claims detection moved behind the adapter → a second-adapter author implementing against it emits the generic note for zero-adapter roots and drops npm's precise handoff note for every unscopable npm root (unmodeled glob, empty glob, script-less package.json) — exactly the note-quality property the renamed test in this PR pins.Suggested fix: add the delegation step to the command-boundary list (zero adapters + root
package.json→ delegate the handoff to the npm adapter for its precise note; zero adapters + nopackage.json→ generic unsupported report), and qualify the Goals bullet to say npm applicability detection moves behind the adapter while the facade retains the npm-shaped fallback routing.中文说明
文档记载的命令边界流程(以及 Goals 中「把 npm 仓库检测移到 npm adapter 之后」一条)遗漏了:在零 adapter 路径上,
build-test.ts保留了自己的 npm 形态检测,并在注册表之外直接调用 npm adapter:if (existsSync(join(root, 'package.json'))) return npmToolchainAdapter.run(runArgs);。— 失败场景:编号契约说零 adapter → unsupported 报告,到此为止,并声称检测已移入 adapter → 第二个 adapter 的作者照此实现时,会对零 adapter 的根输出通用 note,丢掉所有无法 scope 的 npm 根(未建模 glob、空 glob、无脚本 package.json)上 npm 的精确交接 note——正是本 PR 重命名测试所钉住的 note 质量属性。修复建议: 在命令边界清单中补上委托步骤(零 adapter + 根有
package.json→ 委托 npm adapter 产出精确 note 的交接;零 adapter + 无package.json→ 通用 unsupported 报告),并限定 Goals 的措辞:npm 适用性检测移入 adapter,facade 保留 npm 形态的兜底路由。— qwen3.8-max via Qwen Code /review (v0.21.8)