Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions .agents/skills/coding-prompt-normalizer/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion .agents/skills/coding-prompt-normalizer/evals/evals.json
Original file line number Diff line number Diff line change
Expand Up @@ -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."
]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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.
11 changes: 6 additions & 5 deletions .claude/skills/coding-prompt-normalizer/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion .claude/skills/coding-prompt-normalizer/evals/evals.json
Original file line number Diff line number Diff line change
Expand Up @@ -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."
]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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.
6 changes: 5 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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[]`
Expand Down
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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`
Expand Down
6 changes: 4 additions & 2 deletions docs/how-it-works.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,17 @@ 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

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.
Expand Down
8 changes: 6 additions & 2 deletions docs/specs/qwen-code-setup-prd/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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`
Expand Down
4 changes: 2 additions & 2 deletions docs/troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion src/install/contracts/blockers.ts
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
17 changes: 4 additions & 13 deletions src/install/qwen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down Expand Up @@ -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.",
});
}
2 changes: 1 addition & 1 deletion tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion test/install/errors.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
53 changes: 35 additions & 18 deletions test/install/qwen.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down Expand Up @@ -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({
Expand Down Expand Up @@ -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");
}
});

Expand Down
Loading