Files
qwen-code/scripts
Shaojin Wen 623572bd16 feat(verify-pr): add a watch mode backed by the PR probe gate (#15409)
* feat(verify-pr): add a watch mode backed by the PR probe gate

A maintainer round ends with the report; noticing the next push and starting
the next round is manual. The two halves of an automatic trigger already ship
(the PR probe that prints only transitions, and a scheduled task whose gate
runs a shell command outside the model), but nothing connects them, so each
maintainer rebuilds the same cron and re-learns its failure modes.

--watch composes them: a recurring gated task whose gate is
`autofix-pr snapshot <n> --gate`, so a fire costs one shell command and the
round runs only when the head actually moves. The contract adds the guardrails
the pieces do not enforce for each other — an absolute CLI path that carries
the probe, gh auth before scheduling, a state file written before the round so
a dead round does not replay the same head, and cron_delete on stop.

* fix(verify-pr): filter gate events and refuse a shared probe cursor

Two review findings on the watch mode, both composition defects rather than
wording.

The gate's output is what starts a turn: the scheduler hands any non-empty
output to the model, so a probe line for a new comment or a failed check
(`action: "fix"`) woke the watch while the head stood still — the prompt cannot
un-start a turn that already began. scripts/verify-pr-watch-gate.mjs now wraps
the probe and forwards only `resync` and `stop`; unparseable output is
forwarded rather than swallowed, and a failing probe still surfaces as a gate
failure.

The probe keeps one cursor per PR, so a second `snapshot --gate` watcher on the
same PR takes the head moves and `stop` the first one needs, and a prepared
`autofix-pr` watch suppresses heads through `knownHeads`. Verification-owned
state would need a probe flag that does not exist, so the wrapper's `--check`
refuses to install when the PR's gate cursor or prepared state already exists,
and the skill says coexistence is unsupported instead of letting two watchers
starve each other.

* fix(verify-pr): run the watch gate on encoded paths and reserve the PR atomically

Two review findings on the wrapper.

The entry-point check compared `import.meta.url` against `file://` + a raw
path. `import.meta.url` is percent-encoded, so a checkout containing a space or
non-ASCII characters never ran `main()` — the gate exited 0 with empty output,
which the scheduler reads as a quiet tick, for head moves, stops and probe
errors alike. Both sides are now resolved through `realpath`, which also covers
a symlinked checkout because Node resolves the entry module's symlinks while
`argv[1]` keeps the shell's path.

The setup check only read files. Two sessions sharing a clone could both pass it
before either probe created its cursor, and both install a watch on one cursor.
Setup now takes an atomic claim (`wx`) on
`.qwen/tmp/verify-pr-watch-<n>.owner.json`; a read-only check reports an
existing claim too, and a claim is never taken over from a timestamp — deciding
staleness that way is the same race — so a dead setup's claim is released with
`--release`, which the skill also runs when the task stops or creation fails.
2026-10-24 17:49:17 +08:00
..
2025-08-27 15:23:21 +08:00