From e084d4e1a2f48f6434e6c5f84311bf2cc74b8794 Mon Sep 17 00:00:00 2001 From: Daniil Koryto Date: Tue, 23 Jun 2026 22:43:40 +0300 Subject: [PATCH] fix: allow future Qwen Code versions --- .../skills/coding-prompt-normalizer/SKILL.md | 11 ++-- .../coding-prompt-normalizer/evals/evals.json | 2 +- .../references/repo-context-routing.md | 7 +-- .../skills/coding-prompt-normalizer/SKILL.md | 11 ++-- .../coding-prompt-normalizer/evals/evals.json | 2 +- .../references/repo-context-routing.md | 7 +-- AGENTS.md | 6 ++- README.md | 5 +- docs/how-it-works.md | 6 ++- docs/specs/qwen-code-setup-prd/spec.md | 8 ++- docs/troubleshooting.md | 4 +- src/install/contracts/blockers.ts | 2 +- src/install/qwen.ts | 17 ++---- tasks.md | 2 +- test/install/errors.test.ts | 2 +- test/install/qwen.test.ts | 53 ++++++++++++------- 16 files changed, 81 insertions(+), 64 deletions(-) diff --git a/.agents/skills/coding-prompt-normalizer/SKILL.md b/.agents/skills/coding-prompt-normalizer/SKILL.md index 49b3416..52fa194 100644 --- a/.agents/skills/coding-prompt-normalizer/SKILL.md +++ b/.agents/skills/coding-prompt-normalizer/SKILL.md @@ -18,14 +18,15 @@ exact literals, and repository truth. Be honest about this repo: -- it is the scaffold for `npx @gonkagate/qwen-code-setup` -- the Qwen Code installer runtime is not implemented yet +- it is the public runtime for `npx @gonkagate/qwen-code-setup` +- the Qwen Code installer runtime is implemented - the main contract surfaces are `AGENTS.md`, `README.md`, `docs/`, - `src/cli.ts`, `src/constants/`, and `test/` + `src/cli.ts`, `src/install/`, `src/constants/`, and `test/` - the product source of truth is `docs/specs/qwen-code-setup-prd/spec.md` -- current audited Qwen Code baseline is `@qwen-code/qwen-code` `0.17.1` as of - June 12, 2026, but upstream must be rechecked for config/auth work +- current audited Qwen Code metadata is `@qwen-code/qwen-code` `0.18.0` as of + June 12, 2026, but newer parsed Qwen Code versions must not be blocked solely + because they are newer Do not turn a request into a fake implementation brief for runtime behavior that does not exist unless the user explicitly asks to build it. diff --git a/.agents/skills/coding-prompt-normalizer/evals/evals.json b/.agents/skills/coding-prompt-normalizer/evals/evals.json index a27ac0d..616f2dd 100644 --- a/.agents/skills/coding-prompt-normalizer/evals/evals.json +++ b/.agents/skills/coding-prompt-normalizer/evals/evals.json @@ -4,7 +4,7 @@ "prompt": "Normalize this into a handoff prompt: we need to implement qwen-code-setup like opencode-setup, but qwen uses ~/.qwen/settings.json, modelProviders.openai[], envKey, and /doctor. Don't code yet, make a planning prompt.", "expectations": [ "The output preserves qwen-code-setup, ~/.qwen/settings.json, modelProviders.openai[], envKey, and /doctor verbatim.", - "The output says the runtime is not implemented yet.", + "The output says the runtime is implemented.", "The output routes to planning and compatibility audit before implementation." ] } diff --git a/.agents/skills/coding-prompt-normalizer/references/repo-context-routing.md b/.agents/skills/coding-prompt-normalizer/references/repo-context-routing.md index 275ed25..82919a4 100644 --- a/.agents/skills/coding-prompt-normalizer/references/repo-context-routing.md +++ b/.agents/skills/coding-prompt-normalizer/references/repo-context-routing.md @@ -10,8 +10,7 @@ Read: - `docs/specs/qwen-code-setup-prd/spec.md` - relevant docs under `docs/` -Mention that the runtime is not implemented unless the task explicitly builds -it. +Mention that the runtime is implemented when runtime behavior is relevant. ## Qwen Code Compatibility @@ -30,8 +29,6 @@ Use `qwen-code-compatibility-audit` when the request depends on: Read: - `src/cli.ts` +- `src/install/` - `src/constants/` - relevant tests under `test/` - -For future runtime work, expect new modules under `src/install/`, but do not -pretend those files already exist. diff --git a/.claude/skills/coding-prompt-normalizer/SKILL.md b/.claude/skills/coding-prompt-normalizer/SKILL.md index 49b3416..52fa194 100644 --- a/.claude/skills/coding-prompt-normalizer/SKILL.md +++ b/.claude/skills/coding-prompt-normalizer/SKILL.md @@ -18,14 +18,15 @@ exact literals, and repository truth. Be honest about this repo: -- it is the scaffold for `npx @gonkagate/qwen-code-setup` -- the Qwen Code installer runtime is not implemented yet +- it is the public runtime for `npx @gonkagate/qwen-code-setup` +- the Qwen Code installer runtime is implemented - the main contract surfaces are `AGENTS.md`, `README.md`, `docs/`, - `src/cli.ts`, `src/constants/`, and `test/` + `src/cli.ts`, `src/install/`, `src/constants/`, and `test/` - the product source of truth is `docs/specs/qwen-code-setup-prd/spec.md` -- current audited Qwen Code baseline is `@qwen-code/qwen-code` `0.17.1` as of - June 12, 2026, but upstream must be rechecked for config/auth work +- current audited Qwen Code metadata is `@qwen-code/qwen-code` `0.18.0` as of + June 12, 2026, but newer parsed Qwen Code versions must not be blocked solely + because they are newer Do not turn a request into a fake implementation brief for runtime behavior that does not exist unless the user explicitly asks to build it. diff --git a/.claude/skills/coding-prompt-normalizer/evals/evals.json b/.claude/skills/coding-prompt-normalizer/evals/evals.json index a27ac0d..616f2dd 100644 --- a/.claude/skills/coding-prompt-normalizer/evals/evals.json +++ b/.claude/skills/coding-prompt-normalizer/evals/evals.json @@ -4,7 +4,7 @@ "prompt": "Normalize this into a handoff prompt: we need to implement qwen-code-setup like opencode-setup, but qwen uses ~/.qwen/settings.json, modelProviders.openai[], envKey, and /doctor. Don't code yet, make a planning prompt.", "expectations": [ "The output preserves qwen-code-setup, ~/.qwen/settings.json, modelProviders.openai[], envKey, and /doctor verbatim.", - "The output says the runtime is not implemented yet.", + "The output says the runtime is implemented.", "The output routes to planning and compatibility audit before implementation." ] } diff --git a/.claude/skills/coding-prompt-normalizer/references/repo-context-routing.md b/.claude/skills/coding-prompt-normalizer/references/repo-context-routing.md index 275ed25..82919a4 100644 --- a/.claude/skills/coding-prompt-normalizer/references/repo-context-routing.md +++ b/.claude/skills/coding-prompt-normalizer/references/repo-context-routing.md @@ -10,8 +10,7 @@ Read: - `docs/specs/qwen-code-setup-prd/spec.md` - relevant docs under `docs/` -Mention that the runtime is not implemented unless the task explicitly builds -it. +Mention that the runtime is implemented when runtime behavior is relevant. ## Qwen Code Compatibility @@ -30,8 +29,6 @@ Use `qwen-code-compatibility-audit` when the request depends on: Read: - `src/cli.ts` +- `src/install/` - `src/constants/` - relevant tests under `test/` - -For future runtime work, expect new modules under `src/install/`, but do not -pretend those files already exist. diff --git a/AGENTS.md b/AGENTS.md index 32144e3..ecdaa0e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -26,9 +26,11 @@ Current honest state: are present - the latest audited stable upstream Qwen Code release is `@qwen-code/qwen-code` `0.18.0` as of June 12, 2026 +- parsed newer Qwen Code versions are not blocked solely because they are newer; + real setup and verification checks should surface actual incompatibilities If the implementation status, package name, security flow, config locations, -transport contract, or verified Qwen Code baseline changes, this file must be +transport contract, or audited Qwen Code assumptions change, this file must be updated immediately so it stays truthful. ## Fixed Product Invariants @@ -56,6 +58,8 @@ These audited assumptions are the current implementation contract: - package: `@qwen-code/qwen-code` - binary: `qwen` - audited version: `0.18.0` +- runtime version policy: allow any parsed Qwen Code version; do not gate + future versions by exact audited-version equality - npm package engine: `>=22.0.0` - user settings path: `~/.qwen/settings.json` - OpenAI-compatible providers live under `modelProviders.openai[]` diff --git a/README.md b/README.md index 0b93eba..55f2e7e 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ redacted. The happy path is: -1. Detects the local `qwen` binary and verifies the audited Qwen Code baseline. +1. Detects the local `qwen` binary and records its version. 2. Collects or reuses `GONKAGATE_API_KEY` without printing the secret. 3. Calls authenticated `GET https://api.gonkagate.com/v1/models`. 4. Requires all supported GonkaGate models before writing config. @@ -68,6 +68,9 @@ model catalog: The compatibility baseline is `@qwen-code/qwen-code` `0.18.0`, audited on June 12, 2026. +Newer Qwen Code versions are not blocked solely because they are newer; the +setup and verification checks surface actual incompatibilities. + The current Qwen Code integration points are: - binary: `qwen` diff --git a/docs/how-it-works.md b/docs/how-it-works.md index a839090..ff73911 100644 --- a/docs/how-it-works.md +++ b/docs/how-it-works.md @@ -18,7 +18,9 @@ The current Qwen Code compatibility baseline is `@qwen-code/qwen-code` `0.18.0`, audited on June 12, 2026 with a `CONCERNS` verdict. The user-scope provider contract is viable, but project scope must stay gated because Qwen Code currently marks `modelProviders` with -`replace` merge semantics. +`replace` merge semantics. Newer Qwen Code versions are not blocked solely +because they are newer; actual setup and verification checks should surface +runtime incompatibilities. ## Intended Runtime Flow @@ -26,7 +28,7 @@ The runtime: 1. Resolve the current platform and home directory. 2. Detect `qwen` on `PATH`. -3. Verify the local Qwen Code version and config semantics. +3. Parse and record the local Qwen Code version. 4. Collect the GonkaGate API key through a safe input. 5. Fetch `https://api.gonkagate.com/v1/models` with that key. 6. Confirm all three supported GonkaGate models are available. diff --git a/docs/specs/qwen-code-setup-prd/spec.md b/docs/specs/qwen-code-setup-prd/spec.md index 260e98a..8bdf996 100644 --- a/docs/specs/qwen-code-setup-prd/spec.md +++ b/docs/specs/qwen-code-setup-prd/spec.md @@ -133,6 +133,10 @@ Audited on June 12, 2026: - Qwen Code package engine `>=22.0.0` - this installer engine `>=22.14.0` +The audited version is compatibility metadata, not an exact runtime allowlist. +The installer must not block newer parsed Qwen Code versions solely because +they are newer. + Primary evidence: - official Qwen Code auth docs: @@ -491,7 +495,7 @@ Write requirements: `install-state.json` must record: - installer version -- audited Qwen Code version +- audited Qwen Code version used for compatibility metadata - selected scope - selected curated model key - managed model ids written @@ -574,7 +578,7 @@ Never print: Important blocker classes: - `qwen_not_found` -- `qwen_version_unsupported` +- `qwen_version_unparseable` - `settings_parse_failed` - `managed_write_failed` - `model_conflict` diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index 2a97c90..fc7dc77 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -27,8 +27,8 @@ claims. - `qwen_not_found`: install `@qwen-code/qwen-code` and ensure `qwen` is on `PATH`. -- `qwen_version_unsupported`: use the audited Qwen Code version or rerun the - compatibility audit before changing runtime assumptions. +- `qwen_version_unparseable`: check that `qwen --version` prints a semantic + version, then rerun setup. - `settings_parse_failed`: fix malformed Qwen settings JSON/JSONC. - `managed_write_failed`: inspect filesystem permissions and backup/rollback paths. diff --git a/src/install/contracts/blockers.ts b/src/install/contracts/blockers.ts index f25e1c0..e627e11 100644 --- a/src/install/contracts/blockers.ts +++ b/src/install/contracts/blockers.ts @@ -1,6 +1,6 @@ export const PRD_INSTALL_BLOCKER_CODES = [ "qwen_not_found", - "qwen_version_unsupported", + "qwen_version_unparseable", "settings_parse_failed", "managed_write_failed", "model_conflict", diff --git a/src/install/qwen.ts b/src/install/qwen.ts index 49e61eb..3d6fa9f 100644 --- a/src/install/qwen.ts +++ b/src/install/qwen.ts @@ -62,21 +62,12 @@ export async function detectQwen( if (version === undefined) { return { ok: false, - blocker: unsupportedVersionBlocker( + blocker: unparseableVersionBlocker( "Unable to parse qwen version from --version output.", ), }; } - if (version !== QWEN_CODE_SETUP_CONTRACT.latestAuditedQwenCodeVersion) { - return { - ok: false, - blocker: unsupportedVersionBlocker( - `Found qwen ${version}; audited support is ${QWEN_CODE_SETUP_CONTRACT.latestAuditedQwenCodeVersion}.`, - ), - }; - } - return { ok: true, evidence: { @@ -127,12 +118,12 @@ function qwenNotFoundBlocker(detail: string): InstallBlocker { }); } -function unsupportedVersionBlocker(detail: string): InstallBlocker { +function unparseableVersionBlocker(detail: string): InstallBlocker { return createBlocker({ - code: "qwen_version_unsupported", + code: "qwen_version_unparseable", layer: "qwen-detection", message: redactSecrets(detail), nextAction: - "Use the audited @qwen-code/qwen-code version or rerun the compatibility audit before enabling setup.", + "Check that `qwen --version` prints a semantic version, then rerun setup.", }); } diff --git a/tasks.md b/tasks.md index 6630dd6..a0df7a8 100644 --- a/tasks.md +++ b/tasks.md @@ -486,7 +486,7 @@ safe smoke commands and version/source evidence. **Acceptance criteria:** - [x] Missing `qwen` fails with `qwen_not_found`. -- [x] Unsupported Qwen versions fail with `qwen_version_unsupported`. +- [x] Unparseable Qwen version output fails with `qwen_version_unparseable`. - [x] The detector records package, binary, version, Node engine expectations, and audited config semantics. - [x] Interactive `/doctor` is not treated as the success gate. diff --git a/test/install/errors.test.ts b/test/install/errors.test.ts index a9a67f4..639f4d8 100644 --- a/test/install/errors.test.ts +++ b/test/install/errors.test.ts @@ -10,7 +10,7 @@ import { InstallerError, toInstallerError } from "../../src/install/errors.js"; test("PRD blocker codes are stable typed values", () => { assert.deepEqual(PRD_INSTALL_BLOCKER_CODES, [ "qwen_not_found", - "qwen_version_unsupported", + "qwen_version_unparseable", "settings_parse_failed", "managed_write_failed", "model_conflict", diff --git a/test/install/qwen.test.ts b/test/install/qwen.test.ts index 9a7e8fc..a307081 100644 --- a/test/install/qwen.test.ts +++ b/test/install/qwen.test.ts @@ -18,7 +18,7 @@ function commandRunner( }; } -test("qwen detector records supported audited version and config semantics", async () => { +test("qwen detector records installed version and config semantics", async () => { const calls: Array<{ command: string; args: readonly string[] }> = []; const deps = createFakeInstallDependencies({ commands: commandRunner(async (command, args) => { @@ -51,6 +51,25 @@ test("qwen detector records supported audited version and config semantics", asy } }); +test("qwen detector allows versions newer than the audited metadata", async () => { + const result = await detectQwen( + createFakeInstallDependencies({ + commands: commandRunner(async () => ({ + exitCode: 0, + signal: null, + stdout: "qwen 0.19.0\n", + stderr: "", + })), + }), + ); + + assert.equal(result.ok, true); + if (result.ok) { + assert.equal(result.evidence.version, "0.19.0"); + assert.equal(result.evidence.supportedVersion, "0.18.0"); + } +}); + test("qwen detector blocks when command is missing or fails", async () => { const missing = await detectQwen( createFakeInstallDependencies({ @@ -80,24 +99,22 @@ test("qwen detector blocks when command is missing or fails", async () => { } }); -test("qwen detector blocks unsupported and unexpected version output", async () => { - for (const stdout of ["0.17.1\n", "Qwen Code dev build\n"]) { - const result = await detectQwen( - createFakeInstallDependencies({ - commands: commandRunner(async () => ({ - exitCode: 0, - signal: null, - stdout, - stderr: "", - })), - }), - ); +test("qwen detector blocks unexpected version output", async () => { + const result = await detectQwen( + createFakeInstallDependencies({ + commands: commandRunner(async () => ({ + exitCode: 0, + signal: null, + stdout: "Qwen Code dev build\n", + stderr: "", + })), + }), + ); - assert.equal(result.ok, false); - if (!result.ok) { - assert.equal(result.blocker.code, "qwen_version_unsupported"); - assert.equal(result.blocker.layer, "qwen-detection"); - } + assert.equal(result.ok, false); + if (!result.ok) { + assert.equal(result.blocker.code, "qwen_version_unparseable"); + assert.equal(result.blocker.layer, "qwen-detection"); } });