Skip to content

v1.18.3

Choose a tag to compare

@KentQi KentQi released this 01 Nov 05:02
· 12 commits to main since this release
279cd4f

1.18.3 — 2026-10-29

Third pass: the items the previous two rounds left open, plus two findings from the maintainer's re-review (the policy fingerprint was cached for 5 minutes, and a run killed by a signal after printing a result was still reported as success).

  • [P1-16] Two concurrent fix-mode reviews on the same repository could overwrite each other's working tree. Fix mode now takes a per-repository lock (activeFixRepos, keyed by the resolved path) for the duration of the run; a second fix request is refused with an actionable message while read-only reviews stay unrestricted. The lock is released on every settle path and on every preflight refusal, so a rejected call cannot leave a repository locked.
  • [P1-8] A cancelled synchronous review lost the paid-for work. waitForResult=true never registered a job, so when the client cancelled, the SDK dropped the response and the subagent reports were unreachable. Both review and followup now create and register the job before branching: a cancelled sync call leaves a pollable job (claude_review_status with no jobId lists it by repository), and the synchronous return value is unchanged.
  • [P2-14] The stale-process fingerprint check was cached for 5 minutes. The guard self-test result carries a TTL, but the hook is spawned from disk on every call — so an upgrade without a host restart could run up to 5 minutes with two different policies while everything looked green. When codeDrift() says the sources on disk are newer than the process, the self-test now runs with refresh: true and refuses immediately.
  • [SEC-14] CLAUDE_REVIEW_ALLOW_UNGUARDED=1 also bypassed a failed guard installation. That is exactly the case it must never bypass: the run would advertise a guard while no command is policed. The escape hatch now applies only to a guard the operator deliberately switched off (CLAUDE_REVIEW_SHELL_GUARD=0); installation and self-test failures always refuse, with instructions to fix the guard.
  • [P2-7] A run killed by a signal (or exiting non-zero) after printing a result was reported as success. Only the JSON event was consulted. The report is now preserved, but the result is isError: true with status: "completed_with_error", a banner naming the signal/exit code, and exitCode/signal in structuredContent (followup too).
  • [P2-4] Result size had no bound. A very large report could exceed what an MCP client will deliver. claude_review_status now caps the text and structuredContent.report at 512 KiB with UTF-8-safe truncation, a marker line, and pointers to the log/partial-report files; job.result itself is untouched, so repeated polls do not accumulate markers.
  • [P2-1] Two report formats coexisted in one prompt. REPORT_FORMAT, the bundled SKILL.md block, the native appendix and the fallback prompt are now one contract, verbatim: ## Code Review Summary / <verdict>, ## Findings (N) with one ### [SEVERITY] title — path:line block per finding + What/Why/Fix/Evidence, severity critical | major | minor | nit, ## Notes, and an explicit no-findings branch. The fleet prompt carries the block exactly once.
  • [P2-5] cancel-test.mjs only worked on Windows (tasklist). It now inspects ps -axo pid=,command= on POSIX and matches the resolved claude CLI path, so the real-CLI cancellation path can be exercised on macOS/Linux (still opt-in and billed).
  • [scanner] Three more command-executing git keys are refused: pager.<subcommand>, core.askPass, sequence.editor (live values only — a dead path stays a note; pager.* booleans are inert and allowed). UNC values (//server/share or the backslash form) are refused before any filesystem probe, so a Windows UNC core.attributesFile/[include] no longer stalls the pre-spawn scan for ~21 s.
  • Suites: shell-guard 238, audit-fixes 272, p0-regression 95, validation 16, cross-cases 149/149 (C1–C26: new C23 signal-exit, C24–C26 concurrency/sync-job), smoke PASS — npm test green.