sequant 2.9.0 → 2.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (209) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.md +20 -5
  4. package/dist/bin/cli.js +143 -18
  5. package/dist/marketplace/external_plugins/sequant/.claude-plugin/plugin.json +1 -1
  6. package/dist/marketplace/external_plugins/sequant/.mcp.json +1 -1
  7. package/dist/marketplace/external_plugins/sequant/README.md +56 -2
  8. package/dist/marketplace/external_plugins/sequant/hooks/parallel-marker.sh +50 -0
  9. package/dist/marketplace/external_plugins/sequant/hooks/post-tool.sh +20 -7
  10. package/dist/marketplace/external_plugins/sequant/hooks/pre-tool.sh +75 -10
  11. package/dist/marketplace/external_plugins/sequant/skills/_shared/references/trust-model.md +18 -0
  12. package/dist/marketplace/external_plugins/sequant/skills/assess/SKILL.md +215 -390
  13. package/dist/marketplace/external_plugins/sequant/skills/assess/references/predicted-collision-detection.md +1 -1
  14. package/dist/marketplace/external_plugins/sequant/skills/docs/SKILL.md +4 -4
  15. package/dist/marketplace/external_plugins/sequant/skills/exec/SKILL.md +26 -6
  16. package/dist/marketplace/external_plugins/sequant/skills/fullsolve/SKILL.md +3 -1
  17. package/dist/marketplace/external_plugins/sequant/skills/loop/SKILL.md +38 -2
  18. package/dist/marketplace/external_plugins/sequant/skills/merger/SKILL.md +2 -2
  19. package/dist/marketplace/external_plugins/sequant/skills/qa/SKILL.md +337 -57
  20. package/dist/marketplace/external_plugins/sequant/skills/qa/references/anti-pattern-detection.md +6 -6
  21. package/dist/marketplace/external_plugins/sequant/skills/qa/references/call-site-review.md +2 -2
  22. package/dist/marketplace/external_plugins/sequant/skills/qa/references/code-review-checklist.md +2 -2
  23. package/dist/marketplace/external_plugins/sequant/skills/qa/references/fixtures/injection-issue-body.md +36 -0
  24. package/dist/marketplace/external_plugins/sequant/skills/qa/references/quality-gates.md +21 -7
  25. package/dist/marketplace/external_plugins/sequant/skills/qa/references/testing-requirements.md +1 -1
  26. package/dist/marketplace/external_plugins/sequant/skills/qa/scripts/quality-checks.sh +11 -11
  27. package/dist/marketplace/external_plugins/sequant/skills/release/SKILL.md +1 -1
  28. package/dist/marketplace/external_plugins/sequant/skills/setup/SKILL.md +5 -5
  29. package/dist/marketplace/external_plugins/sequant/skills/spec/SKILL.md +2 -0
  30. package/dist/marketplace/external_plugins/sequant/skills/test/SKILL.md +1 -1
  31. package/dist/src/commands/assess-render.d.ts +23 -0
  32. package/dist/src/commands/assess-render.js +60 -0
  33. package/dist/src/commands/doctor.js +8 -13
  34. package/dist/src/commands/init.js +13 -1
  35. package/dist/src/commands/locks.d.ts +20 -1
  36. package/dist/src/commands/locks.js +208 -4
  37. package/dist/src/commands/logs.js +14 -5
  38. package/dist/src/commands/merge.js +154 -2
  39. package/dist/src/commands/ready-tui-adapter.js +6 -1
  40. package/dist/src/commands/ready.d.ts +31 -3
  41. package/dist/src/commands/ready.js +53 -13
  42. package/dist/src/commands/resume.d.ts +113 -0
  43. package/dist/src/commands/resume.js +193 -0
  44. package/dist/src/commands/run-display.js +38 -4
  45. package/dist/src/commands/run-flags.d.ts +42 -1
  46. package/dist/src/commands/run-flags.js +53 -1
  47. package/dist/src/commands/run-progress.js +26 -1
  48. package/dist/src/commands/run.js +11 -10
  49. package/dist/src/commands/state.js +7 -0
  50. package/dist/src/commands/status.d.ts +9 -0
  51. package/dist/src/commands/status.js +24 -1
  52. package/dist/src/commands/sync.js +90 -19
  53. package/dist/src/commands/update.js +28 -5
  54. package/dist/src/commands/worktree.d.ts +31 -0
  55. package/dist/src/commands/worktree.js +95 -0
  56. package/dist/src/lib/ac-parser.d.ts +14 -0
  57. package/dist/src/lib/ac-parser.js +99 -5
  58. package/dist/src/lib/assess/index.d.ts +10 -0
  59. package/dist/src/lib/assess/index.js +9 -0
  60. package/dist/src/lib/assess/renderer.d.ts +64 -0
  61. package/dist/src/lib/assess/renderer.js +481 -0
  62. package/dist/src/lib/assess/types.d.ts +224 -0
  63. package/dist/src/lib/assess/types.js +241 -0
  64. package/dist/src/lib/ci/outputs.d.ts +15 -2
  65. package/dist/src/lib/ci/outputs.js +17 -5
  66. package/dist/src/lib/cli-flags.d.ts +56 -0
  67. package/dist/src/lib/cli-flags.js +89 -0
  68. package/dist/src/lib/cli-ui/run-renderer-types.d.ts +23 -7
  69. package/dist/src/lib/cli-ui/run-renderer.d.ts +10 -0
  70. package/dist/src/lib/cli-ui/run-renderer.js +69 -1
  71. package/dist/src/lib/errors.d.ts +78 -6
  72. package/dist/src/lib/errors.js +133 -15
  73. package/dist/src/lib/locks/checkout-lock.d.ts +193 -0
  74. package/dist/src/lib/locks/checkout-lock.js +389 -0
  75. package/dist/src/lib/locks/index.d.ts +7 -4
  76. package/dist/src/lib/locks/index.js +5 -3
  77. package/dist/src/lib/locks/lock-manager.d.ts +101 -3
  78. package/dist/src/lib/locks/lock-manager.js +277 -8
  79. package/dist/src/lib/locks/types.d.ts +106 -3
  80. package/dist/src/lib/locks/types.js +44 -0
  81. package/dist/src/lib/mcp-config.d.ts +45 -0
  82. package/dist/src/lib/mcp-config.js +77 -1
  83. package/dist/src/lib/merge-check/combined-branch-test.d.ts +21 -2
  84. package/dist/src/lib/merge-check/combined-branch-test.js +233 -110
  85. package/dist/src/lib/merge-check/command-result.d.ts +36 -0
  86. package/dist/src/lib/merge-check/command-result.js +56 -0
  87. package/dist/src/lib/merge-check/index.d.ts +4 -0
  88. package/dist/src/lib/merge-check/index.js +1 -1
  89. package/dist/src/lib/merge-check/report.js +5 -1
  90. package/dist/src/lib/merge-check/types.d.ts +9 -0
  91. package/dist/src/lib/merge-check/watch.d.ts +121 -0
  92. package/dist/src/lib/merge-check/watch.js +215 -0
  93. package/dist/src/lib/qa/infra-blocked-ci.d.ts +90 -0
  94. package/dist/src/lib/qa/infra-blocked-ci.js +80 -0
  95. package/dist/src/lib/settings.d.ts +87 -0
  96. package/dist/src/lib/settings.js +106 -8
  97. package/dist/src/lib/shutdown.d.ts +42 -2
  98. package/dist/src/lib/shutdown.js +75 -5
  99. package/dist/src/lib/skills-check.d.ts +26 -0
  100. package/dist/src/lib/skills-check.js +31 -0
  101. package/dist/src/lib/stacks.d.ts +110 -1
  102. package/dist/src/lib/stacks.js +244 -5
  103. package/dist/src/lib/templates.d.ts +74 -0
  104. package/dist/src/lib/templates.js +171 -16
  105. package/dist/src/lib/test-tautology-detector.d.ts +10 -2
  106. package/dist/src/lib/test-tautology-detector.js +213 -12
  107. package/dist/src/lib/version-check.d.ts +31 -0
  108. package/dist/src/lib/version-check.js +45 -2
  109. package/dist/src/lib/workflow/batch-executor.d.ts +125 -6
  110. package/dist/src/lib/workflow/batch-executor.js +552 -60
  111. package/dist/src/lib/workflow/chain-resume.d.ts +13 -11
  112. package/dist/src/lib/workflow/chain-resume.js +24 -20
  113. package/dist/src/lib/workflow/completed-status.d.ts +52 -0
  114. package/dist/src/lib/workflow/completed-status.js +57 -0
  115. package/dist/src/lib/workflow/config-resolver.d.ts +51 -0
  116. package/dist/src/lib/workflow/config-resolver.js +143 -2
  117. package/dist/src/lib/workflow/drivers/agent-driver.d.ts +22 -0
  118. package/dist/src/lib/workflow/drivers/aider.d.ts +2 -0
  119. package/dist/src/lib/workflow/drivers/aider.js +2 -0
  120. package/dist/src/lib/workflow/drivers/claude-code.d.ts +14 -0
  121. package/dist/src/lib/workflow/drivers/claude-code.js +25 -2
  122. package/dist/src/lib/workflow/effort-escalation.d.ts +73 -0
  123. package/dist/src/lib/workflow/effort-escalation.js +82 -0
  124. package/dist/src/lib/workflow/error-classifier.d.ts +4 -1
  125. package/dist/src/lib/workflow/error-classifier.js +4 -0
  126. package/dist/src/lib/workflow/git-diff-utils.d.ts +35 -3
  127. package/dist/src/lib/workflow/git-diff-utils.js +77 -4
  128. package/dist/src/lib/workflow/heartbeat.d.ts +38 -0
  129. package/dist/src/lib/workflow/heartbeat.js +90 -0
  130. package/dist/src/lib/workflow/log-writer.d.ts +54 -2
  131. package/dist/src/lib/workflow/log-writer.js +95 -6
  132. package/dist/src/lib/workflow/metrics-schema.d.ts +52 -9
  133. package/dist/src/lib/workflow/metrics-schema.js +33 -0
  134. package/dist/src/lib/workflow/metrics-writer.d.ts +11 -0
  135. package/dist/src/lib/workflow/phase-detection.d.ts +12 -0
  136. package/dist/src/lib/workflow/phase-detection.js +5 -1
  137. package/dist/src/lib/workflow/phase-executor.d.ts +203 -14
  138. package/dist/src/lib/workflow/phase-executor.js +467 -52
  139. package/dist/src/lib/workflow/phase-registry.js +2 -1
  140. package/dist/src/lib/workflow/platforms/github.d.ts +64 -0
  141. package/dist/src/lib/workflow/platforms/github.js +152 -0
  142. package/dist/src/lib/workflow/qa-cache.d.ts +6 -1
  143. package/dist/src/lib/workflow/qa-cache.js +22 -9
  144. package/dist/src/lib/workflow/ready-gate.d.ts +45 -4
  145. package/dist/src/lib/workflow/ready-gate.js +49 -12
  146. package/dist/src/lib/workflow/run-log-schema.d.ts +69 -0
  147. package/dist/src/lib/workflow/run-log-schema.js +78 -2
  148. package/dist/src/lib/workflow/run-orchestrator.d.ts +19 -0
  149. package/dist/src/lib/workflow/run-orchestrator.js +168 -20
  150. package/dist/src/lib/workflow/skills-preflight.d.ts +89 -0
  151. package/dist/src/lib/workflow/skills-preflight.js +112 -0
  152. package/dist/src/lib/workflow/spec-recommendation.d.ts +71 -0
  153. package/dist/src/lib/workflow/spec-recommendation.js +142 -0
  154. package/dist/src/lib/workflow/state-cleanup.js +9 -2
  155. package/dist/src/lib/workflow/state-manager.d.ts +33 -0
  156. package/dist/src/lib/workflow/state-manager.js +83 -0
  157. package/dist/src/lib/workflow/state-schema.d.ts +26 -0
  158. package/dist/src/lib/workflow/state-schema.js +47 -0
  159. package/dist/src/lib/workflow/types.d.ts +190 -4
  160. package/dist/src/lib/workflow/types.js +1 -0
  161. package/dist/src/lib/workflow/worktree-manager.d.ts +36 -3
  162. package/dist/src/lib/workflow/worktree-manager.js +111 -24
  163. package/dist/src/lib/workflow/worktree-resolver.d.ts +73 -0
  164. package/dist/src/lib/workflow/worktree-resolver.js +126 -0
  165. package/dist/src/mcp/tools/run.d.ts +9 -1
  166. package/dist/src/mcp/tools/run.js +20 -1
  167. package/dist/src/mcp/tools/status.js +6 -0
  168. package/dist/src/ui/tui/ElapsedTimer.d.ts +12 -3
  169. package/dist/src/ui/tui/ElapsedTimer.js +12 -10
  170. package/dist/src/ui/tui/IssueBox.js +10 -2
  171. package/dist/src/ui/tui/row-cap.js +2 -1
  172. package/dist/src/ui/tui/theme.d.ts +7 -0
  173. package/dist/src/ui/tui/theme.js +9 -0
  174. package/package.json +10 -8
  175. package/templates/hooks/parallel-marker.sh +50 -0
  176. package/templates/hooks/post-tool.sh +20 -7
  177. package/templates/hooks/pre-tool.sh +303 -10
  178. package/templates/scripts/cleanup-worktree.sh +217 -33
  179. package/templates/scripts/new-feature.sh +289 -35
  180. package/templates/skills/_shared/references/subagent-types.md +7 -18
  181. package/templates/skills/_shared/references/trust-model.md +18 -0
  182. package/templates/skills/assess/SKILL.md +220 -391
  183. package/templates/skills/assess/references/predicted-collision-detection.md +1 -1
  184. package/templates/skills/docs/SKILL.md +4 -4
  185. package/templates/skills/exec/SKILL.md +87 -13
  186. package/templates/skills/fullsolve/SKILL.md +130 -22
  187. package/templates/skills/loop/SKILL.md +94 -13
  188. package/templates/skills/merger/SKILL.md +100 -12
  189. package/templates/skills/qa/SKILL.md +396 -63
  190. package/templates/skills/qa/references/anti-pattern-detection.md +6 -6
  191. package/templates/skills/qa/references/call-site-review.md +2 -2
  192. package/templates/skills/qa/references/code-review-checklist.md +2 -2
  193. package/templates/skills/qa/references/fixtures/injection-issue-body.md +36 -0
  194. package/templates/skills/qa/references/quality-gates.md +21 -7
  195. package/templates/skills/qa/references/testing-requirements.md +1 -1
  196. package/templates/skills/qa/scripts/quality-checks.sh +11 -11
  197. package/templates/skills/release/SKILL.md +80 -1
  198. package/templates/skills/setup/SKILL.md +5 -5
  199. package/templates/skills/spec/SKILL.md +33 -15
  200. package/templates/skills/spec/references/recommended-workflow.md +14 -1
  201. package/templates/skills/test/SKILL.md +1 -1
  202. package/templates/skills/testgen/SKILL.md +23 -6
  203. package/dist/src/lib/phase-spinner.d.ts +0 -146
  204. package/dist/src/lib/phase-spinner.js +0 -255
  205. package/dist/src/lib/workflow/pr-operations.d.ts +0 -86
  206. package/dist/src/lib/workflow/pr-operations.js +0 -326
  207. package/dist/src/lib/workflow/run-summary.d.ts +0 -36
  208. package/dist/src/lib/workflow/run-summary.js +0 -142
  209. package/templates/agents/sequant-explorer.md +0 -24
@@ -8,7 +8,7 @@
8
8
  {
9
9
  "name": "sequant",
10
10
  "description": "AI coding agent orchestrator for Claude Code — resolve GitHub issues end-to-end with isolated git worktrees, quality gates, and an MCP server. Includes 17 skills, workflow MCP tools, and pre/post-tool hooks.",
11
- "version": "2.9.0",
11
+ "version": "2.10.0",
12
12
  "author": {
13
13
  "name": "sequant-io",
14
14
  "email": "hello@sequant.io"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "sequant",
3
3
  "description": "AI coding agent orchestrator for Claude Code — resolve GitHub issues end-to-end with isolated git worktrees and quality gates, through spec → exec → qa phases.",
4
- "version": "2.9.0",
4
+ "version": "2.11.0",
5
5
  "author": {
6
6
  "name": "sequant-io",
7
7
  "email": "hello@sequant.io"
package/README.md CHANGED
@@ -16,6 +16,14 @@ AI coding agents write code well, but leave you to run the workflow around it
16
16
 
17
17
  See the [CHANGELOG](CHANGELOG.md) for release notes, or the [migration guide](CHANGELOG.md#migration-from-v1x) if upgrading from v1.x.
18
18
 
19
+ ### What's new in 2.10
20
+
21
+ - **`--auto-wait <minutes>` rides out a rate-limit window** — opt in and a run whose limit window is hours out sleeps until it reopens and continues, instead of halting for a manual restart (#804). **Off by default**; the value is a *total* budget per issue, capped at 2 waits. Never waits on out-of-credits failures (credits are purchased, not waited out). The wait is in-process — for waits that must survive closing the terminal or a reboot, see halt-and-resume below; an exhausted `--auto-wait` budget still writes the halt record so `sequant resume` can pick up where it gave up. See [run-command.md](docs/reference/run-command.md#auto-wait-for-a-rate-limit-window).
22
+ - **Durable halt-and-resume + `sequant resume`** — a run that fails on an exhausted rate-limit window now writes a durable halt record (with its `resumeAt` time) and exits cleanly, releasing the per-issue lock. `sequant resume` re-enters after the window reopens, skipping completed phases and issues — safe to invoke from cron/launchd for unattended machines (recipes in [halt-and-resume.md](docs/reference/halt-and-resume.md)) (#892).
23
+ - **`--ready-gate` runs the post-QA ready gate inside `sequant run`** — opt in and, once an issue's standard phases succeed, `run` drives it through the same full-weight `qa → loop → qa` gate as `sequant ready` (to the configured `ready.policy`) **before** opening the PR, so the gate's fixes land in it. It automates the manual any-gaps/fix-gaps second look and **still stops at the human merge gate — it never merges** (#817). **Off by default**; without the flag the run path is unchanged (an `AC_MET_BUT_NOT_A_PLUS` verdict still breaks to PR per #749). Reuses `ready`'s policy, iteration cap, and stagnation guard — no new settings. See [run-command.md](docs/reference/run-command.md#ready-gate-post-qa-second-look).
24
+ - **`sequant merge --watch` waits for CI, then reports** — instead of polling checks by hand, `merge --watch` waits for each PR's CI checks to finish, then runs the merge-check and reports the result. It never merges (#818).
25
+ - **Stricter CLI contract for scripting** — malformed numeric flags (`--timeout 30m`, `--timeout abc`) are rejected with a clear error instead of silently coerced (#833, #845), and pre-flight rejections (uninitialized project, missing prerequisites) exit non-zero across `run`/`update`/`state`/`status`/`init` (#848). Runs terminated by a signal exit `128+signum` instead of `0` (#856).
26
+
19
27
  ### What's new in 2.9
20
28
 
21
29
  - **`--chain` survives a failed link** — re-running a partially-completed chain resumes from its last good link, skipping the completed prefix and rebasing onto that committed tip instead of redoing hours of finished work (#760). A warn-by-default content pre-flight also runs before the first worktree is provisioned, flagging missing ACs, mis-ordered dependencies, predicted file overlaps, and closed issues; `--strict-preflight` makes any warning a hard stop (#762).
@@ -25,7 +33,7 @@ See the [CHANGELOG](CHANGELOG.md) for release notes, or the [migration guide](CH
25
33
  ### What's new in 2.8
26
34
 
27
35
  - **Clearer failures when an agent stops early** — phases that hit a turn cap now preserve their partial work and halt cleanly for resume instead of discarding it (#739, #733), and rate-limit/out-of-credits failures are named for what they are (with reset time and credit-purchase hints) rather than buried under generic retry noise (#732).
28
- - **Runtime Node-version guard** — `sequant` checks the running Node against its `engines.node` floor (`>=22.12.0`) at startup and exits with a friendly upgrade message instead of crashing later on a Node-22-only API (#734).
36
+ - **Runtime Node-version guard** — `sequant` checks the running Node against its `engines.node` floor (`>=22.13.0`) at startup and exits with a friendly upgrade message instead of crashing later on a Node-22-only API (#734).
29
37
  - **`/assess` avoids npx version skew** — it now emits `sequant run …` when a global install is on `PATH` (and the unchanged `npx sequant run …` otherwise), so copy-pasted commands don't silently run a stale binary (#740).
30
38
 
31
39
  ### What's new in 2.7
@@ -55,7 +63,7 @@ See the [CHANGELOG](CHANGELOG.md) for release notes, or the [migration guide](CH
55
63
  - [GitHub CLI](https://cli.github.com/) — run `gh auth login`
56
64
  - Git — for worktree-based isolation
57
65
 
58
- **For the npm/CLI install path:** Node.js 22.12+
66
+ **For the npm/CLI install path:** Node.js 22.13+
59
67
 
60
68
  **Optional MCP (Model Context Protocol) servers — enhanced features:**
61
69
  - `chrome-devtools` — enables `/test` for browser-based UI testing
@@ -83,6 +91,8 @@ npx sequant init # install skills into your project
83
91
  npx sequant doctor # verify setup
84
92
  ```
85
93
 
94
+ > **Commit `.claude/skills/`.** It is a runtime dependency, not a cache: `sequant run` phase agents load skills from that directory only, so a checkout (or cleanup) that drops it breaks every run. `sequant run` pre-flights the directory and fails fast with the fix (`sequant sync`) if it is missing.
95
+
86
96
  ### Your first run
87
97
 
88
98
  Inside Claude Code, solve an issue end-to-end:
@@ -236,7 +246,7 @@ Most work goes through a handful of top-level commands. The rest are either pipe
236
246
  | `/assess <issues…>` | Triage one or more issues; emits a dashboard + ready-to-paste `run` commands (6-action vocabulary). |
237
247
  | `npx sequant run <issues…>` | Headless equivalent of `/fullsolve`; batches run in parallel. Add `-Q` for the quality loop. |
238
248
  | `/qa <issue>` | Code review + quality gate; posts findings as issue comments. |
239
- | `npx sequant merge <issues…>` | Batch integration QA before merging. |
249
+ | `npx sequant merge <issues…>` | Batch integration QA before merging. Add `--watch` to wait for each PR's CI checks to finish (configurable `--interval`/`--timeout`), then run the checks and report — it never merges. |
240
250
 
241
251
  ### Pipeline internals
242
252
 
@@ -247,6 +257,7 @@ Most work goes through a handful of top-level commands. The rest are either pipe
247
257
  | Command | What it does |
248
258
  |---------|--------------|
249
259
  | `sequant ready <issue>` | Post-resolve full-weight A+ QA gate; drives to merge-readiness, then stops at the human merge gate (never merges). |
260
+ | `sequant resume` | Re-enter runs halted on a rate-limit window once it reopens; no-op before `resumeAt`, so it is safe to schedule via cron/launchd. See [halt-and-resume.md](docs/reference/halt-and-resume.md). |
250
261
  | `/merger` | Multi-issue merge coordination. |
251
262
  | `/improve` | Codebase analysis and improvement discovery. |
252
263
  | `/security-review` | Deep security analysis. |
@@ -285,12 +296,16 @@ Multi-issue runs are parallel by default, and a per-issue lock (`.sequant/locks/
285
296
  "run": {
286
297
  "qualityLoop": false,
287
298
  "maxIterations": 3,
288
- "defaultBase": "feature/dashboard" // Optional: custom default base branch
299
+ "defaultBase": "feature/dashboard", // Optional: custom default base branch
300
+ "phases": {
301
+ "exec": { "model": "sonnet", "effort": "medium" } // Optional: per-phase model/effort override
302
+ },
303
+ "effortEscalation": false // Optional: escalate effort one tier on a quality-loop retry
289
304
  }
290
305
  }
291
306
  ```
292
307
 
293
- See [Customization Guide](docs/guides/customization.md) for all options.
308
+ See [Customization Guide](docs/guides/customization.md) for all options, [Per-Phase Model & Effort](docs/reference/run-command.md#per-phase-model--effort) for the `run.phases` shape, the `--models`/`--efforts` flags, and precedence, and [Effort Escalation on Retries](docs/reference/run-command.md#effort-escalation-on-retries) for `run.effortEscalation`/`--escalate-effort`.
294
309
 
295
310
  ---
296
311
 
package/dist/bin/cli.js CHANGED
@@ -18,6 +18,9 @@ import { readFileSync } from "fs";
18
18
  import { initCommand } from "../src/commands/init.js";
19
19
  import { buildHomeStrayWarning, getInstallRoot, isHomeStrayInstall, isLocalNodeModulesInstall, } from "../src/lib/version-check.js";
20
20
  import { configureUI, banner } from "../src/lib/cli-ui.js";
21
+ import { parseWholeNumber, parsePositiveSeconds, parsePhaseSpecFlag, } from "../src/lib/cli-flags.js";
22
+ import { getPhaseNames } from "../src/lib/workflow/phase-registry.js";
23
+ import { EFFORT_LEVELS } from "../src/lib/settings.js";
21
24
  import { isCI, isStdoutTTY } from "../src/lib/tty.js";
22
25
  import { detectPackageManagerSync, getPackageManagerCommands, } from "../src/lib/stacks.js";
23
26
  // Read version from package.json dynamically
@@ -48,6 +51,7 @@ import { updateCommand } from "../src/commands/update.js";
48
51
  import { doctorCommand } from "../src/commands/doctor.js";
49
52
  import { statusCommand } from "../src/commands/status.js";
50
53
  import { runCommand } from "../src/commands/run.js";
54
+ import { resumeCommand } from "../src/commands/resume.js";
51
55
  import { logsCommand } from "../src/commands/logs.js";
52
56
  import { statsCommand } from "../src/commands/stats.js";
53
57
  import { dashboardCommand } from "../src/commands/dashboard.js";
@@ -56,10 +60,12 @@ import { syncCommand, areSkillsOutdated, checkAndWarnSkillsOutdated, } from "../
56
60
  import { mergeCommand } from "../src/commands/merge.js";
57
61
  import { readyCommand, } from "../src/commands/ready.js";
58
62
  import { conventionsCommand } from "../src/commands/conventions.js";
59
- import { locksListCommand, locksClearCommand, locksAcquireCommand, locksReleaseCommand, locksCheckCommand, locksCheckBatchCommand, } from "../src/commands/locks.js";
63
+ import { locksListCommand, locksClearCommand, locksAcquireCommand, locksReleaseCommand, locksCheckCommand, locksCheckBatchCommand, locksCheckoutCommand, } from "../src/commands/locks.js";
64
+ import { worktreeResolveCommand, worktreeVerifyCommand, } from "../src/commands/worktree.js";
60
65
  import { promptCommand } from "../src/commands/prompt.js";
61
66
  import { watchCommand } from "../src/commands/watch.js";
62
67
  import { abortCommand } from "../src/commands/abort.js";
68
+ import { assessRenderCommand } from "../src/commands/assess-render.js";
63
69
  import { getManifest } from "../src/lib/manifest.js";
64
70
  import { phaseRegistry } from "../src/lib/workflow/phase-registry.js";
65
71
  /**
@@ -142,7 +148,7 @@ program
142
148
  program
143
149
  .command("sync")
144
150
  .description("Sync skills and templates from the Sequant package (non-interactive)")
145
- .option("-f, --force", "Sync even if versions match")
151
+ .option("-f, --force", "Sync even if versions match; also overwrites in-place customizations (e.g. the constitution)")
146
152
  .option("-q, --quiet", "Suppress output")
147
153
  .option("-d, --dry-run", "Show what sync would write without making changes (exits non-zero if work is pending)")
148
154
  .action(syncCommand);
@@ -155,13 +161,13 @@ program
155
161
  program
156
162
  .command("status")
157
163
  .description("Show Sequant version, configuration, and workflow state")
158
- .argument("[issue]", "Issue number to show details for", parseInt)
164
+ .argument("[issue]", "Issue number to show details for", parseWholeNumber("issue", { min: 1 }))
159
165
  .option("--issues", "Show all tracked issues")
160
166
  .option("--json", "Output as JSON")
161
167
  .option("--rebuild", "Rebuild state from run logs")
162
168
  .option("--cleanup", "Clean up stale/orphaned entries")
163
169
  .option("--dry-run", "Preview cleanup without changes")
164
- .option("--max-age <days>", "Remove entries older than N days", parseInt)
170
+ .option("--max-age <days>", "Remove entries older than N days", parseWholeNumber("--max-age", { min: 1, unit: "days" }))
165
171
  .option("--all", "Show all entries including expired; with --cleanup removes all orphaned")
166
172
  .option("--offline", "Skip GitHub queries (pure local state)")
167
173
  .action((issue, options) => {
@@ -179,7 +185,9 @@ program
179
185
  .option("--sequential", "Stop on first issue failure (default: continue)")
180
186
  .option("-d, --dry-run", "Preview without execution")
181
187
  .option("-v, --verbose", "Verbose output with streaming")
182
- .option("--timeout <seconds>", "Timeout per phase in seconds", parseInt)
188
+ // #833: `parseInt` here let `--timeout abc` reach `setTimeout` as `NaN`,
189
+ // which clamps to 0 and aborts every phase the moment it starts.
190
+ .option("--timeout <seconds>", "Timeout per phase in seconds", parsePositiveSeconds("--timeout"))
183
191
  .option("--log-json", "Enable structured JSON logging (default: true)")
184
192
  .option("--no-log", "Disable JSON logging for this run")
185
193
  .option("--log-path <path>", "Custom log directory path")
@@ -189,7 +197,13 @@ program
189
197
  // `runCommand` ORs `qualityLoopAlias` into `qualityLoop`. `-q` no longer maps
190
198
  // to --quiet, which moved to `-s` to end the `-q`/`-Q` collision.
191
199
  .addOption(new Option("-q, --quality-loop-alias", "Alias for -Q/--quality-loop").hideHelp())
192
- .option("--max-iterations <n>", "Max iterations for quality loop (default: 3)", parseInt)
200
+ // #833: a `NaN` bound makes `while (iteration < maxIterations)` in
201
+ // `batch-executor.ts` false on entry, so the issue silently runs zero phases.
202
+ .option("--max-iterations <n>", "Max iterations for quality loop (default: 3)", parseWholeNumber("--max-iterations", {
203
+ min: 1,
204
+ unit: "iterations",
205
+ unitSingular: "iteration",
206
+ }))
193
207
  .option("--batch <issues>", 'Group of issues to run together (e.g., --batch "1 2" --batch "3")', (value, prev) => prev.concat([value]), [])
194
208
  .option("--smart-tests", "Enable smart test detection (default)")
195
209
  .option("--no-smart-tests", "Disable smart test detection")
@@ -198,17 +212,48 @@ program
198
212
  .option("-s, --quiet", "Suppress version warnings and non-essential output (heartbeat-only)")
199
213
  .option("--chain", "Chain issues: each successor is rebased onto the previous issue's committed work before it runs (implies --sequential)")
200
214
  .option("--stacked", "Stack PRs: middle PRs target predecessor branch instead of main; first/last target main (implies --chain)")
201
- .option("--qa-gate", "Wait for QA pass before starting next issue in chain (requires --chain)")
215
+ // #795: deliberately a visible `.option()`, NOT `.hideHelp()` like
216
+ // `--experimental-tui` below. The two are different cases: `--qa-gate`
217
+ // shipped in #133 and was documented across four user-facing docs, so users
218
+ // need to find the deprecation notice; `--experimental-tui` was never
219
+ // advertised. A flag that still works but is absent from --help is the worst
220
+ // of both worlds — scripts keep passing it and nothing explains why to stop.
221
+ .option("--qa-gate", "DEPRECATED (#795): no-op, still accepted. --chain already halts on any failed issue, QA included")
202
222
  .option("--strict-preflight", "Make --chain content pre-flight warnings (missing AC, dependency/overlap order, closed issues) fatal before any worktree is provisioned")
203
223
  .option("--base <branch>", "Base branch for worktree creation (default: main or settings.run.defaultBase)")
204
224
  .option("--no-mcp", "Disable MCP server injection in headless mode")
205
225
  .option("--no-retry", "Disable automatic retry with MCP fallback (useful for debugging)")
226
+ .option("--auto-wait <minutes>", "Total minutes to wait for an exhausted rate-limit window to reopen instead of halting (default: 0, off)",
227
+ // #833: min 0 — #804 defines 0 as "off", so 0 is a meaningful value here
228
+ // and must keep parsing. `NaN` was silently coerced to 0 by
229
+ // `createAutoWaitLedger`, so `--auto-wait 30m` quietly bought 30 minutes.
230
+ parseWholeNumber("--auto-wait", {
231
+ min: 0,
232
+ unit: "minutes",
233
+ unitSingular: "minute",
234
+ }))
206
235
  .option("--resume", "Resume from last completed phase (reads phase markers from GitHub)")
207
236
  .option("--no-rebase", "Skip pre-PR rebase onto origin/main (use when you want to handle rebasing manually)")
208
237
  .option("--no-pr", "Skip PR creation after successful QA (manual PR workflow)")
238
+ // #817: opt-in post-QA ready gate. Reuses the `sequant ready` engine and its
239
+ // bounds (policy from settings.ready.policy, iteration cap, stagnation guard,
240
+ // Non-Goals) — no new policy surface. Never merges; stops at the human gate.
241
+ .option("--ready-gate", "After phases succeed, run the post-QA ready gate (qa→loop→qa to the configured policy) — never merges, stops at the human merge gate")
242
+ // #914: per-phase model/effort override for the claude-code driver. Bare
243
+ // value applies to every phase; comma list of phase=value pairs applies
244
+ // per phase. CLI > settings.run.phases > absent (resolvePhasePolicies).
245
+ .option("--models <spec>", "Per-phase model override, e.g. 'sonnet' or 'spec=fable,exec=sonnet' (default: none — inherits the CLI default model)", parsePhaseSpecFlag("--models", getPhaseNames()))
246
+ .option("--efforts <spec>", "Per-phase reasoning-effort override (low|medium|high|xhigh|max), same grammar as --models (default: none — inherits the SDK default)", parsePhaseSpecFlag("--efforts", getPhaseNames(), EFFORT_LEVELS))
247
+ // #915: opt-in escalation on quality-loop retries. Raises token cost, so
248
+ // off by default — matches --ready-gate's opt-in shape above.
249
+ .option("--escalate-effort", "On a quality-loop retry (loop iteration ≥ 2), run that retried phase one reasoning-effort tier above its resolved base (default: off — see --efforts)")
209
250
  .option("-f, --force", "Force re-execution of completed issues (bypass pre-flight state guard) and take over per-issue locks")
210
251
  .option("--signal-other", "With --force, SIGTERM the prior PID holding the lock (same-host alive only)")
211
- .option("--concurrency <n>", "Max concurrent issues in parallel mode (default: 3)", parseInt)
252
+ .option("--concurrency <n>", "Max concurrent issues in parallel mode (default: 3)",
253
+ // #833: `run.ts` already rejects non-integers, but only after `parseInt`
254
+ // has silently turned `--concurrency 3x` into 3. Validating the raw string
255
+ // catches that; the downstream check stays as a programmatic backstop.
256
+ parseWholeNumber("--concurrency", { min: 1 }))
212
257
  .option("--isolate-parallel", "Isolate parallel agent groups in separate worktrees (prevents file conflicts)")
213
258
  .option("--reflect", "Analyze run results and suggest improvements")
214
259
  .option("--agent <name>", 'Agent driver for phase execution (default: "claude-code")')
@@ -220,12 +265,26 @@ program
220
265
  .addOption(new Option("--experimental-tui").hideHelp())
221
266
  .option("--no-relay", "Disable interactive relay (#383); `sequant prompt` cannot reach this run")
222
267
  .action(runCommand);
268
+ // Durable halt-and-resume re-entry (#892). Safe to invoke from cron/launchd:
269
+ // a no-op (exit 0) until a halted issue's `resumeAt` passes.
270
+ program
271
+ .command("resume")
272
+ .description("Re-enter runs halted on a rate-limit window (no-op until resumeAt; see docs/reference/halt-and-resume.md)")
273
+ .argument("[issues...]", "Issue numbers to resume (default: all halted)")
274
+ .option("-d, --dry-run", "Show what would be resumed without running")
275
+ // Arrow wrapper: commander passes the Command instance as a third
276
+ // positional, which must not land in resumeCommand's injectable deps param.
277
+ .action((issues, options) => resumeCommand(issues, options));
223
278
  program
224
279
  .command("prompt")
225
280
  .description("Send a message into a running headless sequant session (#383)")
226
281
  .argument("[args...]", '[<issue>] "<message>"')
227
282
  .option("--type <type>", "Message type: query (default), directive, abort", "query")
228
- .option("--wait <seconds>", "Block until a reply arrives or the timeout elapses (#645, Gap 4)", parseInt)
283
+ .option("--wait <seconds>", "Block until a reply arrives or the timeout elapses (#645, Gap 4)", parseWholeNumber("--wait", {
284
+ min: 0,
285
+ unit: "seconds",
286
+ unitSingular: "second",
287
+ }))
229
288
  .option("--json", "Output as JSON")
230
289
  .action((args, options) => {
231
290
  return promptCommand({
@@ -253,7 +312,15 @@ program
253
312
  .description("Out-of-band abort: signal a running sequant session directly (#645)")
254
313
  .argument("[issue]", "Issue number (auto-resolved when a single run is active)")
255
314
  .option("--force", "Skip the SIGINT grace period; SIGTERM immediately")
256
- .option("--grace <seconds>", "Seconds to wait after SIGINT before escalating (default: 10)", parseInt)
315
+ .option("--grace <seconds>", "Seconds to wait after SIGINT before escalating (default: 10)",
316
+ // #833: min 0 — `--grace 0` means "escalate immediately" and is meaningful.
317
+ // `NaN` was worse than useless: `Math.max(0, NaN * 1000)` is `NaN`, so the
318
+ // grace period was skipped entirely and SIGTERM followed at once.
319
+ parseWholeNumber("--grace", {
320
+ min: 0,
321
+ unit: "seconds",
322
+ unitSingular: "second",
323
+ }))
257
324
  .option("--json", "Output as JSON")
258
325
  .action((issueArg, options) => {
259
326
  const args = issueArg === undefined ? [] : [issueArg];
@@ -275,6 +342,9 @@ program
275
342
  .option("--review", "Run Phase 1 + 2 + 3 AI briefing")
276
343
  .option("--all", "Run all phases")
277
344
  .option("--post", "Post report to GitHub as PR comments")
345
+ .option("--watch", "Poll each PR's CI checks until terminal, then run merge-check (never merges)")
346
+ .option("--interval <seconds>", "Watch poll interval in seconds (default 30)", parsePositiveSeconds("--interval"))
347
+ .option("--timeout <seconds>", "Watch give-up timeout in seconds (default 1800)", parsePositiveSeconds("--timeout"))
278
348
  .option("--json", "Output as JSON")
279
349
  .option("-v, --verbose", "Enable verbose output")
280
350
  .action(mergeCommand);
@@ -283,13 +353,39 @@ program
283
353
  .description("Post-resolve A+ QA gate — drive an issue to merge-readiness, then stop at the human merge gate (never merges)")
284
354
  .argument("<issue>", "Issue number to drive to readiness")
285
355
  .option("--policy <policy>", "Gate policy: 'ac' (default, stop at ACs met) or 'a-plus' (loop to READY_FOR_MERGE)")
286
- .option("--max-iterations <n>", "Max QA passes before halting for human review (default: settings.run.maxIterations)", parseInt)
287
- .option("--budget <tokens>", "Token budget; halt cleanly with a 'needs human' message on exhaustion", parseInt)
288
- .option("--timeout <seconds>", "Timeout per phase in seconds", parseInt)
356
+ .option("--max-iterations <n>", "Max QA passes before halting for human review (default: settings.run.maxIterations)",
357
+ // #833: `ready.ts` guards each of these three with `> 0`, so `NaN` already
358
+ // fell back to the default rather than reaching a timer. What it could not
359
+ // catch is the silent misparse — `--budget 10k` became 10, `--timeout 30m`
360
+ // became 30 seconds — and a fallback is not the same as telling the user.
361
+ parseWholeNumber("--max-iterations", {
362
+ min: 1,
363
+ unit: "iterations",
364
+ unitSingular: "iteration",
365
+ }))
366
+ .option("--budget <tokens>", "Token budget; halt cleanly with a 'needs human' message on exhaustion", parseWholeNumber("--budget", {
367
+ min: 1,
368
+ unit: "tokens",
369
+ unitSingular: "token",
370
+ }))
371
+ .option("--timeout <seconds>", "Timeout per phase in seconds", parsePositiveSeconds("--timeout"))
289
372
  .option("--no-mcp", "Disable MCP server injection in headless mode")
373
+ .option("--models <spec>", "Per-phase model override for the qa/loop phases this gate runs, e.g. 'sonnet' or 'qa=sonnet' (default: none)", parsePhaseSpecFlag("--models", getPhaseNames()))
374
+ .option("--efforts <spec>", "Per-phase reasoning-effort override (low|medium|high|xhigh|max), same grammar as --models (default: none)", parsePhaseSpecFlag("--efforts", getPhaseNames(), EFFORT_LEVELS))
375
+ // #915: same opt-in escalation as `sequant run --escalate-effort`, applied
376
+ // to this gate's qa/loop retry passes.
377
+ .option("--escalate-effort", "On a QA-pass retry (pass ≥ 2), run that retried phase one reasoning-effort tier above its resolved base (default: off)")
290
378
  .option("--json", "Output as JSON")
291
379
  .option("-v, --verbose", "Enable verbose output")
292
380
  .action((issue, options) => readyCommand(issue, options));
381
+ // #823: internal surface for the /assess skill, deliberately hidden from the
382
+ // top-level help — it takes a JSON payload no human hand-writes. `sequant
383
+ // assess-render --help` still works for debugging.
384
+ program
385
+ .command("assess-render", { hidden: true })
386
+ .description("(internal) Render an /assess AssessResult JSON payload to stdout")
387
+ .argument("<file>", "Path to an AssessResult JSON file")
388
+ .action(assessRenderCommand);
293
389
  program
294
390
  .command("conventions")
295
391
  .description("View and manage codebase conventions")
@@ -301,9 +397,9 @@ program
301
397
  .command("logs")
302
398
  .description("View and analyze workflow run logs")
303
399
  .option("-p, --path <path>", "Custom log directory path")
304
- .option("-n, --last <n>", "Show last N runs", parseInt)
400
+ .option("-n, --last <n>", "Show last N runs", parseWholeNumber("--last", { min: 1 }))
305
401
  .option("--json", "Output as JSON")
306
- .option("-i, --issue <number>", "Filter by issue number", parseInt)
402
+ .option("-i, --issue <number>", "Filter by issue number", parseWholeNumber("--issue", { min: 1 }))
307
403
  .option("--failed", "Show only failed runs")
308
404
  .option("--rotate", "Rotate logs (delete oldest to meet thresholds)")
309
405
  .option("-d, --dry-run", "Show what would be rotated without deleting")
@@ -322,7 +418,7 @@ program
322
418
  program
323
419
  .command("dashboard")
324
420
  .description("Start visual workflow dashboard in browser")
325
- .option("-p, --port <port>", "Port to run server on", parseInt)
421
+ .option("-p, --port <port>", "Port to run server on", parseWholeNumber("--port", { min: 1 }))
326
422
  .option("--no-open", "Don't automatically open browser")
327
423
  .option("-v, --verbose", "Enable verbose logging")
328
424
  .action(dashboardCommand);
@@ -330,7 +426,7 @@ program
330
426
  .command("serve")
331
427
  .description("Start MCP server for workflow orchestration")
332
428
  .option("--transport <type>", "Transport type: stdio (default) or sse", "stdio")
333
- .option("--port <port>", "Port for SSE transport (default: 3100)", parseInt)
429
+ .option("--port <port>", "Port for SSE transport (default: 3100)", parseWholeNumber("--port", { min: 1 }))
334
430
  .action(async (options) => {
335
431
  const mod = await import("../src/commands/serve.js").catch(() => null);
336
432
  if (!mod) {
@@ -364,7 +460,7 @@ stateCmd
364
460
  .option("--json", "Output as JSON")
365
461
  .option("-v, --verbose", "Enable verbose output")
366
462
  .option("-d, --dry-run", "Preview cleanup without changes")
367
- .option("--max-age <days>", "Remove entries older than N days", parseInt)
463
+ .option("--max-age <days>", "Remove entries older than N days", parseWholeNumber("--max-age", { min: 1, unit: "days" }))
368
464
  .option("--all", "Remove all orphaned entries (merged and abandoned)")
369
465
  .action(stateCleanCommand);
370
466
  // Per-issue concurrency locks (#625)
@@ -406,6 +502,35 @@ locksCmd
406
502
  .description("Batch read-only probe: emit canonical ⚠ warning lines for held issues (for /assess dashboard)")
407
503
  .option("--json", "Output as JSON instead of canonical text lines")
408
504
  .action(locksCheckBatchCommand);
505
+ // Checkout-scoped lock (#901). The per-issue locks above give no mutual
506
+ // exclusion on the shared working tree — two sessions on different issues take
507
+ // different lock files, yet `git checkout`/`reset`/`rebase`/`merge` are global
508
+ // to the tree. This lock represents the tree itself.
509
+ locksCmd
510
+ .command("checkout <action>")
511
+ .description("Working-tree lock: acquire|release|check|clear (guards branch-mutating git in the main checkout)")
512
+ .option("--issue <issue>", "Issue this session is working on (acquire, release; proves ownership on release)")
513
+ .option("--command <command>", "Human-readable command label", "unknown")
514
+ .option("--session-id <id>", "Claude Code session id; preferred holder identity for skill shells")
515
+ .option("--skip-pid-check", "Mark the lock so stale recovery skips same-host PID checks (use from skill shells)")
516
+ .option("-f, --force", "Clear even a fresh holder (clear)")
517
+ .option("--json", "Output as JSON")
518
+ .action(locksCheckoutCommand);
519
+ // Repo-scoped worktree resolution for skill bodies (#899)
520
+ const worktreeCmd = program
521
+ .command("worktree")
522
+ .description("Resolve and verify this repository's issue worktrees");
523
+ worktreeCmd
524
+ .command("resolve <issue>")
525
+ .description("Print the absolute path of this repo's worktree for an issue (exit 1 if none)")
526
+ .option("--json", "Output as JSON")
527
+ .action(worktreeResolveCommand);
528
+ worktreeCmd
529
+ .command("verify <path>")
530
+ .description("Confirm a path is a worktree of this repo, not a foreign or stale one (exit 1 if not)")
531
+ .option("--issue <issue>", "Also require the branch to belong to this issue")
532
+ .option("--json", "Output as JSON")
533
+ .action(worktreeVerifyCommand);
409
534
  // Auto-sync skills after npm upgrade (version mismatch detection)
410
535
  // Only triggers when skills were previously synced (has .sequant-version marker).
411
536
  // Projects that manage skills manually (no marker) are not affected.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "sequant",
3
3
  "description": "AI coding agent orchestrator for Claude Code — resolve GitHub issues end-to-end with isolated git worktrees and quality gates, through spec → exec → qa phases.",
4
- "version": "2.9.0",
4
+ "version": "2.10.0",
5
5
  "author": {
6
6
  "name": "sequant-io",
7
7
  "email": "hello@sequant.io"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "sequant": {
3
3
  "command": "npx",
4
- "args": ["-y", "sequant@latest", "serve"]
4
+ "args": ["-y", "sequant@2.10.0", "serve"]
5
5
  }
6
6
  }
@@ -6,7 +6,7 @@ AI coding agent orchestrator for Claude Code — resolve GitHub issues end-to-en
6
6
 
7
7
  - **Git** with a GitHub remote
8
8
  - **GitHub CLI** (`gh`) authenticated (`gh auth status`)
9
- - **Node.js 22.12+** (for MCP server via `npx`)
9
+ - **Node.js 22.13+** (for MCP server via `npx`)
10
10
 
11
11
  ## Installation
12
12
 
@@ -31,7 +31,7 @@ sequant init
31
31
 
32
32
  ## What You Get
33
33
 
34
- ### 17 Workflow Skills
34
+ ### 19 Workflow Skills
35
35
 
36
36
  | Skill | Purpose |
37
37
  |-------|---------|
@@ -51,6 +51,8 @@ sequant init
51
51
  | `/security-review` | Deep security analysis |
52
52
  | `/solve` | Generate the recommended workflow for one or more issues |
53
53
  | `/merger` | Multi-issue integration and merge |
54
+ | `/release` | Version bump, git tag, GitHub release, npm publish |
55
+ | `/upstream` | Monitor Claude Code releases for breaking changes |
54
56
  | `/setup` | Project initialization for plugin users |
55
57
 
56
58
  ### MCP Tools (automatic with plugin)
@@ -83,6 +85,58 @@ sequant ready # Boxed pre-flight: which issues are ready to run?
83
85
 
84
86
  See the [`sequant ready` command reference](https://github.com/sequant-io/sequant/blob/main/docs/reference/ready-command.md) for the full pre-flight readiness check.
85
87
 
88
+ ## Riding out a rate-limit window
89
+
90
+ By default, a rate limit whose window reopens hours from now halts the run, so a multi-hour job needs a manual restart. `--auto-wait <minutes>` opts into waiting instead:
91
+
92
+ ```bash
93
+ sequant run 42 --auto-wait 360 # wait up to 6 hours total for the window to reopen
94
+ ```
95
+
96
+ - **Off by default** (`0`) — the halt behavior is unchanged unless you ask for the wait.
97
+ - The value is a **total** budget per issue, not per occurrence, capped at 2 waits.
98
+ - **Never waits on out-of-credits failures** — credits are purchased, not waited out. (These do carry a reset timestamp, so the gate is the error type, not the timestamp.)
99
+ - The wait is shown live and Ctrl-C ends it promptly.
100
+ - **In-process only: it does not survive closing the terminal** — for waits that must survive a closed terminal or reboot, use halt-and-resume below.
101
+ - Worktree and issue locks are held throughout — deliberate, since Claude rate limits are account-wide and no other run could progress during the window anyway.
102
+
103
+ Also settable as `run.autoWaitMinutes` or `SEQUANT_AUTO_WAIT_MINUTES`. Full details in the [run command reference](https://github.com/sequant-io/sequant/blob/main/docs/reference/run-command.md#auto-wait-for-a-rate-limit-window).
104
+
105
+ ### Durable recovery: `sequant resume`
106
+
107
+ Without `--auto-wait` (or once its budget is exhausted), a run that fails on a waitable rate-limit window writes a durable halt record with its reset time and exits cleanly, releasing the per-issue lock. `sequant resume` re-enters after the window reopens, skipping completed phases and issues — safe to invoke repeatedly, so a single cron/launchd entry handles unattended machines. Recipes and full mechanics in the [halt-and-resume reference](https://github.com/sequant-io/sequant/blob/main/docs/reference/halt-and-resume.md).
108
+
109
+ ## Wait for CI, then verify (`merge --watch`)
110
+
111
+ `sequant merge <issue> --watch` kills the "merge after green" polling loop: it waits for the PR's CI checks to finish before running merge-check, so the verdict is real the moment the command exits.
112
+
113
+ ```bash
114
+ sequant merge 818 --watch # poll CI to terminal, then run merge-check
115
+ sequant merge 818 --watch --scan --post # composes with depth flags and --post
116
+ ```
117
+
118
+ - **It never merges** — `--watch` only decides _when_ the existing report runs; the human merge gate stays.
119
+ - **Foreground only** — a plain poll loop, no daemon or OS notifications. Chain on the exit code to notify yourself.
120
+ - **Configurable** `--interval <seconds>` (default 30) and `--timeout <seconds>` (default 1800); a timeout exits with a distinct code (3).
121
+ - **Dispatch blocks short-circuit to BLOCKED** with the cause — merge conflicts (`CONFLICTING`), zero checks after a dispatch block, and a runner-never-started billing lockout — instead of polling until timeout.
122
+
123
+ Full details in the [merge command reference](https://github.com/sequant-io/sequant/blob/main/docs/reference/merge-command.md).
124
+
125
+ ## Running the ready gate inside `run`
126
+
127
+ `--ready-gate` opts a `sequant run` into the same post-QA gate as `sequant ready`, without the second manual command — it automates the habitual any-gaps → fix-gaps second look:
128
+
129
+ ```bash
130
+ sequant run 42 --ready-gate # phases, then the gate, then a PR — never merges
131
+ ```
132
+
133
+ - **Off by default** — without the flag the run path is unchanged.
134
+ - Once the standard phases pass, the gate runs a full-weight `qa → loop → qa` pass (to `ready.policy`) **before** the PR opens, so its fixes land in the PR.
135
+ - **It never merges.** The run stops at the human merge gate with the issue `waiting_for_human_merge` (threshold reached) or `blocked` (a guard halted it).
136
+ - Reuses `ready`'s policy, iteration cap, and stagnation guard — **no new settings**.
137
+
138
+ Full details in the [run command reference](https://github.com/sequant-io/sequant/blob/main/docs/reference/run-command.md#ready-gate-post-qa-second-look).
139
+
86
140
  ## Documentation
87
141
 
88
142
  - [Getting Started](https://github.com/sequant-io/sequant/tree/main/docs/getting-started)
@@ -0,0 +1,50 @@
1
+ #!/bin/bash
2
+ # Shared naming contract for /exec parallel-group marker files (#881).
3
+ #
4
+ # One writer (the /exec skill) and two readers (pre-tool.sh, post-tool.sh) must
5
+ # agree on (a) the marker filename and (b) how the owning project is identified.
6
+ # Before #881 the filename was a global constant, so a parallel group in ONE
7
+ # project silently redirected worktree enforcement for EVERY concurrent Claude
8
+ # session on the machine — the first matching marker in the shared temp dir won,
9
+ # regardless of which project wrote it. Keeping the scheme in this single sourced
10
+ # file is what stops the writer and the readers from drifting apart (AC-4).
11
+ #
12
+ # Marker filename: ${TMPDIR}/claude-parallel-<project-hash>-<group-id>.marker
13
+ # Marker contents: line 1 = worktree path, line 2 = owning project root.
14
+
15
+ # parallel_marker_project_root — the canonical identity of the current project,
16
+ # derived identically in skill-context and hook-context. CLAUDE_PROJECT_DIR is
17
+ # set by Claude Code for both the skill's shell and the hook's shell within the
18
+ # same session; the git-toplevel fallback resolves to the same worktree when it
19
+ # is unset. A mismatch here would silently re-introduce the cross-project
20
+ # collision this fix exists to close, so the two contexts MUST agree.
21
+ parallel_marker_project_root() {
22
+ if [[ -n "${CLAUDE_PROJECT_DIR:-}" ]]; then
23
+ printf '%s' "$CLAUDE_PROJECT_DIR"
24
+ else
25
+ git rev-parse --show-toplevel 2>/dev/null || printf '%s' "$PWD"
26
+ fi
27
+ }
28
+
29
+ # parallel_marker_hash — a stable per-project hash used in the marker filename so
30
+ # two projects running parallel groups at once cannot collide (AC-1). Reuses the
31
+ # md5-with-md5sum-fallback idiom already used for the per-file lock in
32
+ # pre-tool.sh. printf (no trailing newline) keeps writer and reader identical.
33
+ parallel_marker_hash() {
34
+ local root
35
+ root="$(parallel_marker_project_root)"
36
+ printf '%s' "$root" | md5 -q 2>/dev/null || printf '%s' "$root" | md5sum | cut -d' ' -f1
37
+ }
38
+
39
+ # parallel_marker_prefix — the ${TMPDIR}-anchored, project-scoped filename prefix
40
+ # that both the writer and the readers glob on (AC-2). Honors $TMPDIR (macOS's
41
+ # per-user temp) with a /tmp fallback, matching the hooks' own _TMPDIR.
42
+ parallel_marker_prefix() {
43
+ local tmp="${TMPDIR:-/tmp}"
44
+ printf '%s/claude-parallel-%s-' "$tmp" "$(parallel_marker_hash)"
45
+ }
46
+
47
+ # parallel_marker_path <group-id> — the full marker path for one group.
48
+ parallel_marker_path() {
49
+ printf '%s%s.marker' "$(parallel_marker_prefix)" "$1"
50
+ }
@@ -77,22 +77,35 @@ mkdir -p "$_LOG_DIR" 2>/dev/null || _LOG_DIR="${_TMPDIR}"
77
77
  TIMING_LOG="${_LOG_DIR}/claude-timing.log"
78
78
  QUALITY_LOG="${_LOG_DIR}/claude-quality.log"
79
79
  TESTS_LOG="${_LOG_DIR}/claude-tests.log"
80
- PARALLEL_MARKER_PREFIX="${_TMPDIR}/claude-parallel-"
80
+
81
+ # Parallel-group marker naming (#881). Source the shared helper next to this
82
+ # hook so the project-scoped prefix stays identical to the writer and pre-tool.sh.
83
+ _MARKER_HELPER="$(dirname "${BASH_SOURCE[0]:-$0}")/parallel-marker.sh"
84
+ if [[ -f "$_MARKER_HELPER" ]]; then
85
+ # shellcheck source=parallel-marker.sh disable=SC1091
86
+ source "$_MARKER_HELPER"
87
+ PARALLEL_MARKER_PREFIX="$(parallel_marker_prefix)"
88
+ else
89
+ PARALLEL_MARKER_PREFIX="${_TMPDIR}/claude-parallel-"
90
+ fi
81
91
 
82
92
  # === AGENT ID DETECTION ===
83
- # For parallel agents, detect group ID from marker files
84
- # Format: ${_TMPDIR}/claude-parallel-<group-id>.marker
93
+ # For parallel agents, detect group ID from marker files. The glob is scoped to
94
+ # the current project's marker prefix (#881) so a foreign project's marker never
95
+ # labels this session's timing rows.
85
96
  AGENT_ID=""
86
97
  IS_PARALLEL_AGENT="false"
98
+ _MARKER_BASE=$(basename "$PARALLEL_MARKER_PREFIX")
87
99
  # Find marker files using find (works in both bash and zsh)
88
100
  while IFS= read -r marker; do
89
101
  if [[ -n "$marker" && -f "$marker" ]]; then
90
- # Extract group ID from marker filename
91
- AGENT_ID=$(basename "$marker" | sed 's/claude-parallel-//' | sed 's/\.marker//')
102
+ # Extract group ID: strip the project-scoped prefix and the suffix.
103
+ AGENT_ID=$(basename "$marker" .marker)
104
+ AGENT_ID=${AGENT_ID#"$_MARKER_BASE"}
92
105
  IS_PARALLEL_AGENT="true"
93
106
  break
94
107
  fi
95
- done < <(find "${_TMPDIR}" -maxdepth 1 -name "claude-parallel-*.marker" 2>/dev/null)
108
+ done < <(find "${_TMPDIR}" -maxdepth 1 -name "${_MARKER_BASE}*.marker" 2>/dev/null)
96
109
 
97
110
  # === TIMING END ===
98
111
  # Include agent ID in log format if available (AC-4)
@@ -253,7 +266,7 @@ if [[ "${CLAUDE_HOOKS_COVERAGE:-}" == "true" ]]; then
253
266
  COVERAGE_LOG="${_LOG_DIR}/claude-coverage.log"
254
267
 
255
268
  # Get changed source files (excluding tests)
256
- changed_files=$(git diff main...HEAD --name-only 2>/dev/null | grep -E '\.(ts|tsx|js|jsx)$' | grep -v -E '\.test\.|\.spec\.|__tests__' || true)
269
+ changed_files=$(git diff origin/main...HEAD --name-only 2>/dev/null | grep -E '\.(ts|tsx|js|jsx)$' | grep -v -E '\.test\.|\.spec\.|__tests__' || true)
257
270
 
258
271
  if [[ -n "$changed_files" ]]; then
259
272
  echo "$(date +%H:%M:%S) COVERAGE_ANALYSIS: Checking test coverage for changed files" >> "$QUALITY_LOG"