sequant 2.8.0 → 2.10.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 (211) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.md +23 -4
  4. package/dist/bin/cli.js +99 -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 +58 -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 +38 -10
  10. package/dist/marketplace/external_plugins/sequant/hooks/pre-tool.sh +405 -67
  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 +250 -344
  13. package/dist/marketplace/external_plugins/sequant/skills/assess/references/predicted-collision-detection.md +10 -7
  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 +4 -2
  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/reflect/SKILL.md +27 -13
  28. package/dist/marketplace/external_plugins/sequant/skills/reflect/references/documentation-tiers.md +80 -68
  29. package/dist/marketplace/external_plugins/sequant/skills/reflect/references/phase-reflection.md +31 -15
  30. package/dist/marketplace/external_plugins/sequant/skills/release/SKILL.md +11 -3
  31. package/dist/marketplace/external_plugins/sequant/skills/setup/SKILL.md +5 -5
  32. package/dist/marketplace/external_plugins/sequant/skills/spec/SKILL.md +2 -0
  33. package/dist/marketplace/external_plugins/sequant/skills/spec/references/verification-criteria.md +1 -1
  34. package/dist/marketplace/external_plugins/sequant/skills/test/SKILL.md +1 -1
  35. package/dist/src/commands/assess-render.d.ts +23 -0
  36. package/dist/src/commands/assess-render.js +60 -0
  37. package/dist/src/commands/doctor.js +8 -13
  38. package/dist/src/commands/init.js +13 -1
  39. package/dist/src/commands/locks.js +2 -0
  40. package/dist/src/commands/logs.js +20 -6
  41. package/dist/src/commands/merge.js +154 -2
  42. package/dist/src/commands/ready-tui-adapter.js +6 -1
  43. package/dist/src/commands/ready.d.ts +25 -3
  44. package/dist/src/commands/ready.js +39 -13
  45. package/dist/src/commands/resume.d.ts +113 -0
  46. package/dist/src/commands/resume.js +193 -0
  47. package/dist/src/commands/run-display.d.ts +20 -0
  48. package/dist/src/commands/run-display.js +117 -5
  49. package/dist/src/commands/run-flags.d.ts +42 -1
  50. package/dist/src/commands/run-flags.js +53 -1
  51. package/dist/src/commands/run-progress.js +26 -1
  52. package/dist/src/commands/run.js +11 -10
  53. package/dist/src/commands/state.js +7 -0
  54. package/dist/src/commands/stats.js +47 -0
  55. package/dist/src/commands/status.d.ts +9 -0
  56. package/dist/src/commands/status.js +24 -1
  57. package/dist/src/commands/sync.js +90 -19
  58. package/dist/src/commands/update.js +28 -5
  59. package/dist/src/lib/ac-parser.d.ts +14 -0
  60. package/dist/src/lib/ac-parser.js +99 -5
  61. package/dist/src/lib/assess/index.d.ts +10 -0
  62. package/dist/src/lib/assess/index.js +9 -0
  63. package/dist/src/lib/assess/renderer.d.ts +64 -0
  64. package/dist/src/lib/assess/renderer.js +481 -0
  65. package/dist/src/lib/assess/types.d.ts +224 -0
  66. package/dist/src/lib/assess/types.js +241 -0
  67. package/dist/src/lib/assess-collision-detect.d.ts +19 -2
  68. package/dist/src/lib/assess-collision-detect.js +68 -4
  69. package/dist/src/lib/ci/outputs.d.ts +15 -2
  70. package/dist/src/lib/ci/outputs.js +17 -5
  71. package/dist/src/lib/cli-flags.d.ts +33 -0
  72. package/dist/src/lib/cli-flags.js +46 -0
  73. package/dist/src/lib/cli-ui/run-renderer-types.d.ts +21 -7
  74. package/dist/src/lib/cli-ui/run-renderer.d.ts +10 -0
  75. package/dist/src/lib/cli-ui/run-renderer.js +79 -9
  76. package/dist/src/lib/errors.d.ts +84 -6
  77. package/dist/src/lib/errors.js +142 -17
  78. package/dist/src/lib/locks/index.d.ts +3 -3
  79. package/dist/src/lib/locks/index.js +2 -2
  80. package/dist/src/lib/locks/lock-manager.d.ts +20 -2
  81. package/dist/src/lib/locks/lock-manager.js +47 -3
  82. package/dist/src/lib/locks/types.d.ts +34 -3
  83. package/dist/src/lib/locks/types.js +16 -0
  84. package/dist/src/lib/manifest.js +1 -17
  85. package/dist/src/lib/mcp-config.d.ts +45 -0
  86. package/dist/src/lib/mcp-config.js +77 -1
  87. package/dist/src/lib/merge-check/combined-branch-test.d.ts +21 -2
  88. package/dist/src/lib/merge-check/combined-branch-test.js +233 -110
  89. package/dist/src/lib/merge-check/command-result.d.ts +36 -0
  90. package/dist/src/lib/merge-check/command-result.js +56 -0
  91. package/dist/src/lib/merge-check/index.d.ts +4 -0
  92. package/dist/src/lib/merge-check/index.js +1 -1
  93. package/dist/src/lib/merge-check/report.js +5 -1
  94. package/dist/src/lib/merge-check/types.d.ts +9 -0
  95. package/dist/src/lib/merge-check/watch.d.ts +121 -0
  96. package/dist/src/lib/merge-check/watch.js +215 -0
  97. package/dist/src/lib/qa/infra-blocked-ci.d.ts +90 -0
  98. package/dist/src/lib/qa/infra-blocked-ci.js +80 -0
  99. package/dist/src/lib/settings.d.ts +14 -0
  100. package/dist/src/lib/settings.js +61 -8
  101. package/dist/src/lib/shutdown.d.ts +42 -2
  102. package/dist/src/lib/shutdown.js +75 -5
  103. package/dist/src/lib/skills-check.d.ts +26 -0
  104. package/dist/src/lib/skills-check.js +31 -0
  105. package/dist/src/lib/stacks.d.ts +110 -1
  106. package/dist/src/lib/stacks.js +244 -5
  107. package/dist/src/lib/templates.d.ts +74 -0
  108. package/dist/src/lib/templates.js +171 -16
  109. package/dist/src/lib/test-tautology-detector.d.ts +9 -2
  110. package/dist/src/lib/test-tautology-detector.js +153 -12
  111. package/dist/src/lib/version-check.d.ts +31 -0
  112. package/dist/src/lib/version-check.js +46 -7
  113. package/dist/src/lib/workflow/batch-executor.d.ts +138 -6
  114. package/dist/src/lib/workflow/batch-executor.js +550 -54
  115. package/dist/src/lib/workflow/chain-preflight.d.ts +89 -0
  116. package/dist/src/lib/workflow/chain-preflight.js +199 -0
  117. package/dist/src/lib/workflow/chain-resume.d.ts +118 -0
  118. package/dist/src/lib/workflow/chain-resume.js +170 -0
  119. package/dist/src/lib/workflow/completed-status.d.ts +52 -0
  120. package/dist/src/lib/workflow/completed-status.js +57 -0
  121. package/dist/src/lib/workflow/config-resolver.d.ts +26 -0
  122. package/dist/src/lib/workflow/config-resolver.js +54 -2
  123. package/dist/src/lib/workflow/dependency-markers.d.ts +29 -0
  124. package/dist/src/lib/workflow/dependency-markers.js +79 -0
  125. package/dist/src/lib/workflow/drivers/agent-driver.d.ts +7 -0
  126. package/dist/src/lib/workflow/drivers/aider.d.ts +2 -0
  127. package/dist/src/lib/workflow/drivers/aider.js +2 -0
  128. package/dist/src/lib/workflow/drivers/claude-code.d.ts +21 -0
  129. package/dist/src/lib/workflow/drivers/claude-code.js +50 -8
  130. package/dist/src/lib/workflow/error-classifier.d.ts +9 -2
  131. package/dist/src/lib/workflow/error-classifier.js +14 -1
  132. package/dist/src/lib/workflow/git-diff-utils.d.ts +35 -3
  133. package/dist/src/lib/workflow/git-diff-utils.js +77 -4
  134. package/dist/src/lib/workflow/heartbeat.d.ts +38 -0
  135. package/dist/src/lib/workflow/heartbeat.js +90 -0
  136. package/dist/src/lib/workflow/log-writer.d.ts +44 -1
  137. package/dist/src/lib/workflow/log-writer.js +81 -14
  138. package/dist/src/lib/workflow/metrics-schema.d.ts +42 -3
  139. package/dist/src/lib/workflow/metrics-schema.js +16 -0
  140. package/dist/src/lib/workflow/metrics-writer.d.ts +2 -1
  141. package/dist/src/lib/workflow/phase-executor.d.ts +234 -13
  142. package/dist/src/lib/workflow/phase-executor.js +533 -56
  143. package/dist/src/lib/workflow/phase-registry.js +2 -1
  144. package/dist/src/lib/workflow/platforms/github.d.ts +64 -0
  145. package/dist/src/lib/workflow/platforms/github.js +152 -0
  146. package/dist/src/lib/workflow/qa-cache.d.ts +6 -1
  147. package/dist/src/lib/workflow/qa-cache.js +22 -9
  148. package/dist/src/lib/workflow/ready-gate.d.ts +17 -4
  149. package/dist/src/lib/workflow/ready-gate.js +25 -9
  150. package/dist/src/lib/workflow/run-log-schema.d.ts +37 -0
  151. package/dist/src/lib/workflow/run-log-schema.js +92 -2
  152. package/dist/src/lib/workflow/run-orchestrator.d.ts +33 -0
  153. package/dist/src/lib/workflow/run-orchestrator.js +429 -47
  154. package/dist/src/lib/workflow/skills-preflight.d.ts +89 -0
  155. package/dist/src/lib/workflow/skills-preflight.js +112 -0
  156. package/dist/src/lib/workflow/state-cleanup.js +9 -2
  157. package/dist/src/lib/workflow/state-manager.d.ts +33 -0
  158. package/dist/src/lib/workflow/state-manager.js +83 -0
  159. package/dist/src/lib/workflow/state-schema.d.ts +26 -0
  160. package/dist/src/lib/workflow/state-schema.js +47 -0
  161. package/dist/src/lib/workflow/status-derivation.d.ts +30 -0
  162. package/dist/src/lib/workflow/status-derivation.js +27 -0
  163. package/dist/src/lib/workflow/types.d.ts +149 -4
  164. package/dist/src/lib/workflow/types.js +1 -0
  165. package/dist/src/lib/workflow/worktree-manager.d.ts +70 -2
  166. package/dist/src/lib/workflow/worktree-manager.js +201 -52
  167. package/dist/src/mcp/tools/run.d.ts +11 -1
  168. package/dist/src/mcp/tools/run.js +22 -1
  169. package/dist/src/mcp/tools/status.js +6 -0
  170. package/dist/src/ui/tui/ElapsedTimer.d.ts +12 -3
  171. package/dist/src/ui/tui/ElapsedTimer.js +12 -10
  172. package/dist/src/ui/tui/IssueBox.js +10 -2
  173. package/dist/src/ui/tui/row-cap.js +2 -1
  174. package/dist/src/ui/tui/theme.d.ts +7 -0
  175. package/dist/src/ui/tui/theme.js +9 -0
  176. package/package.json +9 -10
  177. package/templates/hooks/parallel-marker.sh +50 -0
  178. package/templates/hooks/post-tool.sh +38 -10
  179. package/templates/hooks/pre-tool.sh +405 -67
  180. package/templates/scripts/cleanup-worktree.sh +282 -30
  181. package/templates/scripts/new-feature.sh +264 -16
  182. package/templates/skills/_shared/references/trust-model.md +18 -0
  183. package/templates/skills/assess/SKILL.md +250 -344
  184. package/templates/skills/assess/references/predicted-collision-detection.md +10 -7
  185. package/templates/skills/docs/SKILL.md +4 -4
  186. package/templates/skills/exec/SKILL.md +26 -6
  187. package/templates/skills/fullsolve/SKILL.md +4 -2
  188. package/templates/skills/loop/SKILL.md +38 -2
  189. package/templates/skills/merger/SKILL.md +2 -2
  190. package/templates/skills/qa/SKILL.md +337 -57
  191. package/templates/skills/qa/references/anti-pattern-detection.md +6 -6
  192. package/templates/skills/qa/references/call-site-review.md +2 -2
  193. package/templates/skills/qa/references/code-review-checklist.md +2 -2
  194. package/templates/skills/qa/references/fixtures/injection-issue-body.md +36 -0
  195. package/templates/skills/qa/references/quality-gates.md +21 -7
  196. package/templates/skills/qa/references/testing-requirements.md +1 -1
  197. package/templates/skills/qa/scripts/quality-checks.sh +11 -11
  198. package/templates/skills/reflect/SKILL.md +27 -13
  199. package/templates/skills/reflect/references/documentation-tiers.md +80 -68
  200. package/templates/skills/reflect/references/phase-reflection.md +31 -15
  201. package/templates/skills/release/SKILL.md +11 -3
  202. package/templates/skills/setup/SKILL.md +5 -5
  203. package/templates/skills/spec/SKILL.md +2 -0
  204. package/templates/skills/spec/references/verification-criteria.md +1 -1
  205. package/templates/skills/test/SKILL.md +1 -1
  206. package/dist/src/lib/phase-spinner.d.ts +0 -146
  207. package/dist/src/lib/phase-spinner.js +0 -255
  208. package/dist/src/lib/workflow/pr-operations.d.ts +0 -86
  209. package/dist/src/lib/workflow/pr-operations.js +0 -326
  210. package/dist/src/lib/workflow/run-summary.d.ts +0 -36
  211. package/dist/src/lib/workflow/run-summary.js +0 -142
@@ -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.8.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.8.0",
4
+ "version": "2.10.0",
5
5
  "author": {
6
6
  "name": "sequant-io",
7
7
  "email": "hello@sequant.io"
package/README.md CHANGED
@@ -16,10 +16,24 @@ 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
+
27
+ ### What's new in 2.9
28
+
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).
30
+ - **Rate limits stop burning hours** — a rate limit hit inside a phase now skips doomed cold-start retries, and the run summary labels the chain halt with its cause and how to resume, instead of cascading into a ~2h retry ladder (#761).
31
+ - **Stale plugin-cache warning** — `pre-tool.sh` now prints a once-per-day, network-free reminder (`claude plugin update sequant@sequant`) when a plugin-channel install has drifted behind the marketplace (#784, #788).
32
+
19
33
  ### What's new in 2.8
20
34
 
21
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).
22
- - **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).
23
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).
24
38
 
25
39
  ### What's new in 2.7
@@ -42,14 +56,14 @@ See the [CHANGELOG](CHANGELOG.md) for release notes, or the [migration guide](CH
42
56
  ### Prerequisites
43
57
 
44
58
  **An AI coding agent — one of:**
45
- - [Claude Code](https://claude.ai/code) — default agent
59
+ - [Claude Code](https://claude.ai/code) — default agent. **Recommended: Claude Code ≥ 2.1.208.** The pre-tool hooks lean on Claude Code's native dangerous-`rm` analyzer (which fires even under `bypassPermissions`) instead of re-implementing catastrophic-delete detection; that analyzer's command-substitution coverage landed in 2.1.208. This is a recommendation, not an enforced floor — plugins cannot declare a minimum Claude Code version, so nothing gates install, and the pre-2.1.208 command-substitution gap (e.g. `echo "$(rm -rf ~)"`) is accepted rather than guarded.
46
60
  - [Aider](https://aider.chat/) — alternative, via `--agent aider`
47
61
 
48
62
  **Always required (both):**
49
63
  - [GitHub CLI](https://cli.github.com/) — run `gh auth login`
50
64
  - Git — for worktree-based isolation
51
65
 
52
- **For the npm/CLI install path:** Node.js 22.12+
66
+ **For the npm/CLI install path:** Node.js 22.13+
53
67
 
54
68
  **Optional MCP (Model Context Protocol) servers — enhanced features:**
55
69
  - `chrome-devtools` — enables `/test` for browser-based UI testing
@@ -68,6 +82,8 @@ Pick the path that matches **where you run Sequant**:
68
82
  /sequant:setup
69
83
  ```
70
84
 
85
+ > **Plugins do not auto-update.** Claude Code pins a plugin to the version you installed and never updates it on its own — even as new releases ship. To pick up a new version, run `claude plugin update sequant@sequant`, then restart Claude Code. Sequant's pre-tool hook warns once a day when your installed version falls behind the marketplace.
86
+
71
87
  **Headless / CI (npm package)** — drive runs from the terminal or a CI job:
72
88
  ```bash
73
89
  npm install sequant # or: pnpm add / yarn add / bun add sequant
@@ -75,6 +91,8 @@ npx sequant init # install skills into your project
75
91
  npx sequant doctor # verify setup
76
92
  ```
77
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
+
78
96
  ### Your first run
79
97
 
80
98
  Inside Claude Code, solve an issue end-to-end:
@@ -228,7 +246,7 @@ Most work goes through a handful of top-level commands. The rest are either pipe
228
246
  | `/assess <issues…>` | Triage one or more issues; emits a dashboard + ready-to-paste `run` commands (6-action vocabulary). |
229
247
  | `npx sequant run <issues…>` | Headless equivalent of `/fullsolve`; batches run in parallel. Add `-Q` for the quality loop. |
230
248
  | `/qa <issue>` | Code review + quality gate; posts findings as issue comments. |
231
- | `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. |
232
250
 
233
251
  ### Pipeline internals
234
252
 
@@ -239,6 +257,7 @@ Most work goes through a handful of top-level commands. The rest are either pipe
239
257
  | Command | What it does |
240
258
  |---------|--------------|
241
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). |
242
261
  | `/merger` | Multi-issue merge coordination. |
243
262
  | `/improve` | Codebase analysis and improvement discovery. |
244
263
  | `/security-review` | Deep security analysis. |
package/dist/bin/cli.js CHANGED
@@ -18,6 +18,7 @@ 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, } from "../src/lib/cli-flags.js";
21
22
  import { isCI, isStdoutTTY } from "../src/lib/tty.js";
22
23
  import { detectPackageManagerSync, getPackageManagerCommands, } from "../src/lib/stacks.js";
23
24
  // Read version from package.json dynamically
@@ -48,6 +49,7 @@ import { updateCommand } from "../src/commands/update.js";
48
49
  import { doctorCommand } from "../src/commands/doctor.js";
49
50
  import { statusCommand } from "../src/commands/status.js";
50
51
  import { runCommand } from "../src/commands/run.js";
52
+ import { resumeCommand } from "../src/commands/resume.js";
51
53
  import { logsCommand } from "../src/commands/logs.js";
52
54
  import { statsCommand } from "../src/commands/stats.js";
53
55
  import { dashboardCommand } from "../src/commands/dashboard.js";
@@ -60,6 +62,7 @@ import { locksListCommand, locksClearCommand, locksAcquireCommand, locksReleaseC
60
62
  import { promptCommand } from "../src/commands/prompt.js";
61
63
  import { watchCommand } from "../src/commands/watch.js";
62
64
  import { abortCommand } from "../src/commands/abort.js";
65
+ import { assessRenderCommand } from "../src/commands/assess-render.js";
63
66
  import { getManifest } from "../src/lib/manifest.js";
64
67
  import { phaseRegistry } from "../src/lib/workflow/phase-registry.js";
65
68
  /**
@@ -142,7 +145,7 @@ program
142
145
  program
143
146
  .command("sync")
144
147
  .description("Sync skills and templates from the Sequant package (non-interactive)")
145
- .option("-f, --force", "Sync even if versions match")
148
+ .option("-f, --force", "Sync even if versions match; also overwrites in-place customizations (e.g. the constitution)")
146
149
  .option("-q, --quiet", "Suppress output")
147
150
  .option("-d, --dry-run", "Show what sync would write without making changes (exits non-zero if work is pending)")
148
151
  .action(syncCommand);
@@ -155,13 +158,13 @@ program
155
158
  program
156
159
  .command("status")
157
160
  .description("Show Sequant version, configuration, and workflow state")
158
- .argument("[issue]", "Issue number to show details for", parseInt)
161
+ .argument("[issue]", "Issue number to show details for", parseWholeNumber("issue", { min: 1 }))
159
162
  .option("--issues", "Show all tracked issues")
160
163
  .option("--json", "Output as JSON")
161
164
  .option("--rebuild", "Rebuild state from run logs")
162
165
  .option("--cleanup", "Clean up stale/orphaned entries")
163
166
  .option("--dry-run", "Preview cleanup without changes")
164
- .option("--max-age <days>", "Remove entries older than N days", parseInt)
167
+ .option("--max-age <days>", "Remove entries older than N days", parseWholeNumber("--max-age", { min: 1, unit: "days" }))
165
168
  .option("--all", "Show all entries including expired; with --cleanup removes all orphaned")
166
169
  .option("--offline", "Skip GitHub queries (pure local state)")
167
170
  .action((issue, options) => {
@@ -179,7 +182,9 @@ program
179
182
  .option("--sequential", "Stop on first issue failure (default: continue)")
180
183
  .option("-d, --dry-run", "Preview without execution")
181
184
  .option("-v, --verbose", "Verbose output with streaming")
182
- .option("--timeout <seconds>", "Timeout per phase in seconds", parseInt)
185
+ // #833: `parseInt` here let `--timeout abc` reach `setTimeout` as `NaN`,
186
+ // which clamps to 0 and aborts every phase the moment it starts.
187
+ .option("--timeout <seconds>", "Timeout per phase in seconds", parsePositiveSeconds("--timeout"))
183
188
  .option("--log-json", "Enable structured JSON logging (default: true)")
184
189
  .option("--no-log", "Disable JSON logging for this run")
185
190
  .option("--log-path <path>", "Custom log directory path")
@@ -189,25 +194,55 @@ program
189
194
  // `runCommand` ORs `qualityLoopAlias` into `qualityLoop`. `-q` no longer maps
190
195
  // to --quiet, which moved to `-s` to end the `-q`/`-Q` collision.
191
196
  .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)
197
+ // #833: a `NaN` bound makes `while (iteration < maxIterations)` in
198
+ // `batch-executor.ts` false on entry, so the issue silently runs zero phases.
199
+ .option("--max-iterations <n>", "Max iterations for quality loop (default: 3)", parseWholeNumber("--max-iterations", {
200
+ min: 1,
201
+ unit: "iterations",
202
+ unitSingular: "iteration",
203
+ }))
193
204
  .option("--batch <issues>", 'Group of issues to run together (e.g., --batch "1 2" --batch "3")', (value, prev) => prev.concat([value]), [])
194
205
  .option("--smart-tests", "Enable smart test detection (default)")
195
206
  .option("--no-smart-tests", "Disable smart test detection")
196
207
  .option("--testgen", "Run testgen phase after spec")
197
208
  .option("--security-review", "Run security-review phase after spec")
198
209
  .option("-s, --quiet", "Suppress version warnings and non-essential output (heartbeat-only)")
199
- .option("--chain", "Chain issues: each branches from previous (implies --sequential)")
210
+ .option("--chain", "Chain issues: each successor is rebased onto the previous issue's committed work before it runs (implies --sequential)")
200
211
  .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)")
212
+ // #795: deliberately a visible `.option()`, NOT `.hideHelp()` like
213
+ // `--experimental-tui` below. The two are different cases: `--qa-gate`
214
+ // shipped in #133 and was documented across four user-facing docs, so users
215
+ // need to find the deprecation notice; `--experimental-tui` was never
216
+ // advertised. A flag that still works but is absent from --help is the worst
217
+ // of both worlds — scripts keep passing it and nothing explains why to stop.
218
+ .option("--qa-gate", "DEPRECATED (#795): no-op, still accepted. --chain already halts on any failed issue, QA included")
219
+ .option("--strict-preflight", "Make --chain content pre-flight warnings (missing AC, dependency/overlap order, closed issues) fatal before any worktree is provisioned")
202
220
  .option("--base <branch>", "Base branch for worktree creation (default: main or settings.run.defaultBase)")
203
221
  .option("--no-mcp", "Disable MCP server injection in headless mode")
204
222
  .option("--no-retry", "Disable automatic retry with MCP fallback (useful for debugging)")
223
+ .option("--auto-wait <minutes>", "Total minutes to wait for an exhausted rate-limit window to reopen instead of halting (default: 0, off)",
224
+ // #833: min 0 — #804 defines 0 as "off", so 0 is a meaningful value here
225
+ // and must keep parsing. `NaN` was silently coerced to 0 by
226
+ // `createAutoWaitLedger`, so `--auto-wait 30m` quietly bought 30 minutes.
227
+ parseWholeNumber("--auto-wait", {
228
+ min: 0,
229
+ unit: "minutes",
230
+ unitSingular: "minute",
231
+ }))
205
232
  .option("--resume", "Resume from last completed phase (reads phase markers from GitHub)")
206
233
  .option("--no-rebase", "Skip pre-PR rebase onto origin/main (use when you want to handle rebasing manually)")
207
234
  .option("--no-pr", "Skip PR creation after successful QA (manual PR workflow)")
235
+ // #817: opt-in post-QA ready gate. Reuses the `sequant ready` engine and its
236
+ // bounds (policy from settings.ready.policy, iteration cap, stagnation guard,
237
+ // Non-Goals) — no new policy surface. Never merges; stops at the human gate.
238
+ .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")
208
239
  .option("-f, --force", "Force re-execution of completed issues (bypass pre-flight state guard) and take over per-issue locks")
209
240
  .option("--signal-other", "With --force, SIGTERM the prior PID holding the lock (same-host alive only)")
210
- .option("--concurrency <n>", "Max concurrent issues in parallel mode (default: 3)", parseInt)
241
+ .option("--concurrency <n>", "Max concurrent issues in parallel mode (default: 3)",
242
+ // #833: `run.ts` already rejects non-integers, but only after `parseInt`
243
+ // has silently turned `--concurrency 3x` into 3. Validating the raw string
244
+ // catches that; the downstream check stays as a programmatic backstop.
245
+ parseWholeNumber("--concurrency", { min: 1 }))
211
246
  .option("--isolate-parallel", "Isolate parallel agent groups in separate worktrees (prevents file conflicts)")
212
247
  .option("--reflect", "Analyze run results and suggest improvements")
213
248
  .option("--agent <name>", 'Agent driver for phase execution (default: "claude-code")')
@@ -219,12 +254,26 @@ program
219
254
  .addOption(new Option("--experimental-tui").hideHelp())
220
255
  .option("--no-relay", "Disable interactive relay (#383); `sequant prompt` cannot reach this run")
221
256
  .action(runCommand);
257
+ // Durable halt-and-resume re-entry (#892). Safe to invoke from cron/launchd:
258
+ // a no-op (exit 0) until a halted issue's `resumeAt` passes.
259
+ program
260
+ .command("resume")
261
+ .description("Re-enter runs halted on a rate-limit window (no-op until resumeAt; see docs/reference/halt-and-resume.md)")
262
+ .argument("[issues...]", "Issue numbers to resume (default: all halted)")
263
+ .option("-d, --dry-run", "Show what would be resumed without running")
264
+ // Arrow wrapper: commander passes the Command instance as a third
265
+ // positional, which must not land in resumeCommand's injectable deps param.
266
+ .action((issues, options) => resumeCommand(issues, options));
222
267
  program
223
268
  .command("prompt")
224
269
  .description("Send a message into a running headless sequant session (#383)")
225
270
  .argument("[args...]", '[<issue>] "<message>"')
226
271
  .option("--type <type>", "Message type: query (default), directive, abort", "query")
227
- .option("--wait <seconds>", "Block until a reply arrives or the timeout elapses (#645, Gap 4)", parseInt)
272
+ .option("--wait <seconds>", "Block until a reply arrives or the timeout elapses (#645, Gap 4)", parseWholeNumber("--wait", {
273
+ min: 0,
274
+ unit: "seconds",
275
+ unitSingular: "second",
276
+ }))
228
277
  .option("--json", "Output as JSON")
229
278
  .action((args, options) => {
230
279
  return promptCommand({
@@ -252,7 +301,15 @@ program
252
301
  .description("Out-of-band abort: signal a running sequant session directly (#645)")
253
302
  .argument("[issue]", "Issue number (auto-resolved when a single run is active)")
254
303
  .option("--force", "Skip the SIGINT grace period; SIGTERM immediately")
255
- .option("--grace <seconds>", "Seconds to wait after SIGINT before escalating (default: 10)", parseInt)
304
+ .option("--grace <seconds>", "Seconds to wait after SIGINT before escalating (default: 10)",
305
+ // #833: min 0 — `--grace 0` means "escalate immediately" and is meaningful.
306
+ // `NaN` was worse than useless: `Math.max(0, NaN * 1000)` is `NaN`, so the
307
+ // grace period was skipped entirely and SIGTERM followed at once.
308
+ parseWholeNumber("--grace", {
309
+ min: 0,
310
+ unit: "seconds",
311
+ unitSingular: "second",
312
+ }))
256
313
  .option("--json", "Output as JSON")
257
314
  .action((issueArg, options) => {
258
315
  const args = issueArg === undefined ? [] : [issueArg];
@@ -274,6 +331,9 @@ program
274
331
  .option("--review", "Run Phase 1 + 2 + 3 AI briefing")
275
332
  .option("--all", "Run all phases")
276
333
  .option("--post", "Post report to GitHub as PR comments")
334
+ .option("--watch", "Poll each PR's CI checks until terminal, then run merge-check (never merges)")
335
+ .option("--interval <seconds>", "Watch poll interval in seconds (default 30)", parsePositiveSeconds("--interval"))
336
+ .option("--timeout <seconds>", "Watch give-up timeout in seconds (default 1800)", parsePositiveSeconds("--timeout"))
277
337
  .option("--json", "Output as JSON")
278
338
  .option("-v, --verbose", "Enable verbose output")
279
339
  .action(mergeCommand);
@@ -282,13 +342,34 @@ program
282
342
  .description("Post-resolve A+ QA gate — drive an issue to merge-readiness, then stop at the human merge gate (never merges)")
283
343
  .argument("<issue>", "Issue number to drive to readiness")
284
344
  .option("--policy <policy>", "Gate policy: 'ac' (default, stop at ACs met) or 'a-plus' (loop to READY_FOR_MERGE)")
285
- .option("--max-iterations <n>", "Max QA passes before halting for human review (default: settings.run.maxIterations)", parseInt)
286
- .option("--budget <tokens>", "Token budget; halt cleanly with a 'needs human' message on exhaustion", parseInt)
287
- .option("--timeout <seconds>", "Timeout per phase in seconds", parseInt)
345
+ .option("--max-iterations <n>", "Max QA passes before halting for human review (default: settings.run.maxIterations)",
346
+ // #833: `ready.ts` guards each of these three with `> 0`, so `NaN` already
347
+ // fell back to the default rather than reaching a timer. What it could not
348
+ // catch is the silent misparse — `--budget 10k` became 10, `--timeout 30m`
349
+ // became 30 seconds — and a fallback is not the same as telling the user.
350
+ parseWholeNumber("--max-iterations", {
351
+ min: 1,
352
+ unit: "iterations",
353
+ unitSingular: "iteration",
354
+ }))
355
+ .option("--budget <tokens>", "Token budget; halt cleanly with a 'needs human' message on exhaustion", parseWholeNumber("--budget", {
356
+ min: 1,
357
+ unit: "tokens",
358
+ unitSingular: "token",
359
+ }))
360
+ .option("--timeout <seconds>", "Timeout per phase in seconds", parsePositiveSeconds("--timeout"))
288
361
  .option("--no-mcp", "Disable MCP server injection in headless mode")
289
362
  .option("--json", "Output as JSON")
290
363
  .option("-v, --verbose", "Enable verbose output")
291
364
  .action((issue, options) => readyCommand(issue, options));
365
+ // #823: internal surface for the /assess skill, deliberately hidden from the
366
+ // top-level help — it takes a JSON payload no human hand-writes. `sequant
367
+ // assess-render --help` still works for debugging.
368
+ program
369
+ .command("assess-render", { hidden: true })
370
+ .description("(internal) Render an /assess AssessResult JSON payload to stdout")
371
+ .argument("<file>", "Path to an AssessResult JSON file")
372
+ .action(assessRenderCommand);
292
373
  program
293
374
  .command("conventions")
294
375
  .description("View and manage codebase conventions")
@@ -300,9 +381,9 @@ program
300
381
  .command("logs")
301
382
  .description("View and analyze workflow run logs")
302
383
  .option("-p, --path <path>", "Custom log directory path")
303
- .option("-n, --last <n>", "Show last N runs", parseInt)
384
+ .option("-n, --last <n>", "Show last N runs", parseWholeNumber("--last", { min: 1 }))
304
385
  .option("--json", "Output as JSON")
305
- .option("-i, --issue <number>", "Filter by issue number", parseInt)
386
+ .option("-i, --issue <number>", "Filter by issue number", parseWholeNumber("--issue", { min: 1 }))
306
387
  .option("--failed", "Show only failed runs")
307
388
  .option("--rotate", "Rotate logs (delete oldest to meet thresholds)")
308
389
  .option("-d, --dry-run", "Show what would be rotated without deleting")
@@ -321,7 +402,7 @@ program
321
402
  program
322
403
  .command("dashboard")
323
404
  .description("Start visual workflow dashboard in browser")
324
- .option("-p, --port <port>", "Port to run server on", parseInt)
405
+ .option("-p, --port <port>", "Port to run server on", parseWholeNumber("--port", { min: 1 }))
325
406
  .option("--no-open", "Don't automatically open browser")
326
407
  .option("-v, --verbose", "Enable verbose logging")
327
408
  .action(dashboardCommand);
@@ -329,7 +410,7 @@ program
329
410
  .command("serve")
330
411
  .description("Start MCP server for workflow orchestration")
331
412
  .option("--transport <type>", "Transport type: stdio (default) or sse", "stdio")
332
- .option("--port <port>", "Port for SSE transport (default: 3100)", parseInt)
413
+ .option("--port <port>", "Port for SSE transport (default: 3100)", parseWholeNumber("--port", { min: 1 }))
333
414
  .action(async (options) => {
334
415
  const mod = await import("../src/commands/serve.js").catch(() => null);
335
416
  if (!mod) {
@@ -363,7 +444,7 @@ stateCmd
363
444
  .option("--json", "Output as JSON")
364
445
  .option("-v, --verbose", "Enable verbose output")
365
446
  .option("-d, --dry-run", "Preview cleanup without changes")
366
- .option("--max-age <days>", "Remove entries older than N days", parseInt)
447
+ .option("--max-age <days>", "Remove entries older than N days", parseWholeNumber("--max-age", { min: 1, unit: "days" }))
367
448
  .option("--all", "Remove all orphaned entries (merged and abandoned)")
368
449
  .action(stateCleanCommand);
369
450
  // Per-issue concurrency locks (#625)
@@ -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.8.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
 
@@ -20,6 +20,8 @@ Or browse in `/plugin > Discover`.
20
20
 
21
21
  After installing, run `/sequant:setup` to configure your project.
22
22
 
23
+ > **Plugins do not auto-update.** Claude Code pins a plugin to the version you installed. To pick up a new release, run `claude plugin update sequant@sequant`, then restart Claude Code.
24
+
23
25
  ### npm (power users / CI)
24
26
 
25
27
  ```bash
@@ -29,7 +31,7 @@ sequant init
29
31
 
30
32
  ## What You Get
31
33
 
32
- ### 17 Workflow Skills
34
+ ### 19 Workflow Skills
33
35
 
34
36
  | Skill | Purpose |
35
37
  |-------|---------|
@@ -49,6 +51,8 @@ sequant init
49
51
  | `/security-review` | Deep security analysis |
50
52
  | `/solve` | Generate the recommended workflow for one or more issues |
51
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 |
52
56
  | `/setup` | Project initialization for plugin users |
53
57
 
54
58
  ### MCP Tools (automatic with plugin)
@@ -81,6 +85,58 @@ sequant ready # Boxed pre-flight: which issues are ready to run?
81
85
 
82
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.
83
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
+
84
140
  ## Documentation
85
141
 
86
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
+ }
@@ -51,33 +51,61 @@ fi
51
51
 
52
52
  _TMPDIR="${TMPDIR:-/tmp}"
53
53
 
54
- # Use CLAUDE_PLUGIN_DATA for persistent logs (survives plugin updates)
55
- if [[ -n "${CLAUDE_PLUGIN_DATA}" ]]; then
54
+ # Log sink (#763 AC-5c: blocked-command history must survive to be a useful
55
+ # regression corpus, so $TMPDIR which macOS purges — is a last resort only).
56
+ #
57
+ # This block MUST stay identical to the one in pre-tool.sh. The two hooks
58
+ # write the *same* claude-timing.log (START there, END here) and the same
59
+ # claude-quality.log, so any divergence silently splits every START/END pair
60
+ # across two files.
61
+ #
62
+ # Every candidate is absolute and lives outside the repo. A repo-local or
63
+ # relative path is wrong three times over: it resolves against the hook's cwd
64
+ # (which Claude Code does not pin), it yields one sink per directory instead
65
+ # of the single corpus AC-5 asks for, and — worst — creating it inside a repo
66
+ # makes `git status --porcelain` non-empty, which silently defeats pre-tool.sh's
67
+ # no-changes guard that reads exactly that output.
68
+ if [[ -n "${CLAUDE_PLUGIN_DATA:-}" ]]; then
56
69
  _LOG_DIR="${CLAUDE_PLUGIN_DATA}/logs"
57
- mkdir -p "$_LOG_DIR"
70
+ elif [[ -n "${HOME:-}" ]]; then
71
+ _LOG_DIR="${HOME}/.sequant/logs"
58
72
  else
59
73
  _LOG_DIR="${_TMPDIR}"
60
74
  fi
75
+ mkdir -p "$_LOG_DIR" 2>/dev/null || _LOG_DIR="${_TMPDIR}"
61
76
 
62
77
  TIMING_LOG="${_LOG_DIR}/claude-timing.log"
63
78
  QUALITY_LOG="${_LOG_DIR}/claude-quality.log"
64
79
  TESTS_LOG="${_LOG_DIR}/claude-tests.log"
65
- 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
66
91
 
67
92
  # === AGENT ID DETECTION ===
68
- # For parallel agents, detect group ID from marker files
69
- # 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.
70
96
  AGENT_ID=""
71
97
  IS_PARALLEL_AGENT="false"
98
+ _MARKER_BASE=$(basename "$PARALLEL_MARKER_PREFIX")
72
99
  # Find marker files using find (works in both bash and zsh)
73
100
  while IFS= read -r marker; do
74
101
  if [[ -n "$marker" && -f "$marker" ]]; then
75
- # Extract group ID from marker filename
76
- 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"}
77
105
  IS_PARALLEL_AGENT="true"
78
106
  break
79
107
  fi
80
- 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)
81
109
 
82
110
  # === TIMING END ===
83
111
  # Include agent ID in log format if available (AC-4)
@@ -238,7 +266,7 @@ if [[ "${CLAUDE_HOOKS_COVERAGE:-}" == "true" ]]; then
238
266
  COVERAGE_LOG="${_LOG_DIR}/claude-coverage.log"
239
267
 
240
268
  # Get changed source files (excluding tests)
241
- 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)
242
270
 
243
271
  if [[ -n "$changed_files" ]]; then
244
272
  echo "$(date +%H:%M:%S) COVERAGE_ANALYSIS: Checking test coverage for changed files" >> "$QUALITY_LOG"