sequant 2.9.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 (188) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.md +14 -3
  4. package/dist/bin/cli.js +97 -17
  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.js +2 -0
  36. package/dist/src/commands/logs.js +14 -5
  37. package/dist/src/commands/merge.js +154 -2
  38. package/dist/src/commands/ready-tui-adapter.js +6 -1
  39. package/dist/src/commands/ready.d.ts +25 -3
  40. package/dist/src/commands/ready.js +39 -13
  41. package/dist/src/commands/resume.d.ts +113 -0
  42. package/dist/src/commands/resume.js +193 -0
  43. package/dist/src/commands/run-display.js +37 -4
  44. package/dist/src/commands/run-flags.d.ts +42 -1
  45. package/dist/src/commands/run-flags.js +53 -1
  46. package/dist/src/commands/run-progress.js +26 -1
  47. package/dist/src/commands/run.js +11 -10
  48. package/dist/src/commands/state.js +7 -0
  49. package/dist/src/commands/status.d.ts +9 -0
  50. package/dist/src/commands/status.js +24 -1
  51. package/dist/src/commands/sync.js +90 -19
  52. package/dist/src/commands/update.js +28 -5
  53. package/dist/src/lib/ac-parser.d.ts +14 -0
  54. package/dist/src/lib/ac-parser.js +99 -5
  55. package/dist/src/lib/assess/index.d.ts +10 -0
  56. package/dist/src/lib/assess/index.js +9 -0
  57. package/dist/src/lib/assess/renderer.d.ts +64 -0
  58. package/dist/src/lib/assess/renderer.js +481 -0
  59. package/dist/src/lib/assess/types.d.ts +224 -0
  60. package/dist/src/lib/assess/types.js +241 -0
  61. package/dist/src/lib/ci/outputs.d.ts +15 -2
  62. package/dist/src/lib/ci/outputs.js +17 -5
  63. package/dist/src/lib/cli-flags.d.ts +33 -0
  64. package/dist/src/lib/cli-flags.js +46 -0
  65. package/dist/src/lib/cli-ui/run-renderer-types.d.ts +21 -7
  66. package/dist/src/lib/cli-ui/run-renderer.d.ts +10 -0
  67. package/dist/src/lib/cli-ui/run-renderer.js +62 -0
  68. package/dist/src/lib/errors.d.ts +78 -6
  69. package/dist/src/lib/errors.js +133 -15
  70. package/dist/src/lib/locks/index.d.ts +3 -3
  71. package/dist/src/lib/locks/index.js +2 -2
  72. package/dist/src/lib/locks/lock-manager.d.ts +20 -2
  73. package/dist/src/lib/locks/lock-manager.js +47 -3
  74. package/dist/src/lib/locks/types.d.ts +34 -3
  75. package/dist/src/lib/locks/types.js +16 -0
  76. package/dist/src/lib/mcp-config.d.ts +45 -0
  77. package/dist/src/lib/mcp-config.js +77 -1
  78. package/dist/src/lib/merge-check/combined-branch-test.d.ts +21 -2
  79. package/dist/src/lib/merge-check/combined-branch-test.js +233 -110
  80. package/dist/src/lib/merge-check/command-result.d.ts +36 -0
  81. package/dist/src/lib/merge-check/command-result.js +56 -0
  82. package/dist/src/lib/merge-check/index.d.ts +4 -0
  83. package/dist/src/lib/merge-check/index.js +1 -1
  84. package/dist/src/lib/merge-check/report.js +5 -1
  85. package/dist/src/lib/merge-check/types.d.ts +9 -0
  86. package/dist/src/lib/merge-check/watch.d.ts +121 -0
  87. package/dist/src/lib/merge-check/watch.js +215 -0
  88. package/dist/src/lib/qa/infra-blocked-ci.d.ts +90 -0
  89. package/dist/src/lib/qa/infra-blocked-ci.js +80 -0
  90. package/dist/src/lib/settings.d.ts +14 -0
  91. package/dist/src/lib/settings.js +61 -8
  92. package/dist/src/lib/shutdown.d.ts +42 -2
  93. package/dist/src/lib/shutdown.js +75 -5
  94. package/dist/src/lib/skills-check.d.ts +26 -0
  95. package/dist/src/lib/skills-check.js +31 -0
  96. package/dist/src/lib/stacks.d.ts +110 -1
  97. package/dist/src/lib/stacks.js +244 -5
  98. package/dist/src/lib/templates.d.ts +74 -0
  99. package/dist/src/lib/templates.js +171 -16
  100. package/dist/src/lib/test-tautology-detector.d.ts +9 -2
  101. package/dist/src/lib/test-tautology-detector.js +153 -12
  102. package/dist/src/lib/version-check.d.ts +31 -0
  103. package/dist/src/lib/version-check.js +45 -2
  104. package/dist/src/lib/workflow/batch-executor.d.ts +125 -6
  105. package/dist/src/lib/workflow/batch-executor.js +477 -44
  106. package/dist/src/lib/workflow/chain-resume.d.ts +13 -11
  107. package/dist/src/lib/workflow/chain-resume.js +24 -20
  108. package/dist/src/lib/workflow/completed-status.d.ts +52 -0
  109. package/dist/src/lib/workflow/completed-status.js +57 -0
  110. package/dist/src/lib/workflow/config-resolver.d.ts +26 -0
  111. package/dist/src/lib/workflow/config-resolver.js +54 -2
  112. package/dist/src/lib/workflow/drivers/agent-driver.d.ts +7 -0
  113. package/dist/src/lib/workflow/drivers/aider.d.ts +2 -0
  114. package/dist/src/lib/workflow/drivers/aider.js +2 -0
  115. package/dist/src/lib/workflow/drivers/claude-code.d.ts +14 -0
  116. package/dist/src/lib/workflow/drivers/claude-code.js +20 -2
  117. package/dist/src/lib/workflow/git-diff-utils.d.ts +35 -3
  118. package/dist/src/lib/workflow/git-diff-utils.js +77 -4
  119. package/dist/src/lib/workflow/heartbeat.d.ts +38 -0
  120. package/dist/src/lib/workflow/heartbeat.js +90 -0
  121. package/dist/src/lib/workflow/log-writer.d.ts +44 -1
  122. package/dist/src/lib/workflow/log-writer.js +75 -6
  123. package/dist/src/lib/workflow/metrics-schema.d.ts +3 -3
  124. package/dist/src/lib/workflow/phase-executor.d.ts +203 -14
  125. package/dist/src/lib/workflow/phase-executor.js +457 -52
  126. package/dist/src/lib/workflow/phase-registry.js +2 -1
  127. package/dist/src/lib/workflow/platforms/github.d.ts +64 -0
  128. package/dist/src/lib/workflow/platforms/github.js +152 -0
  129. package/dist/src/lib/workflow/qa-cache.d.ts +6 -1
  130. package/dist/src/lib/workflow/qa-cache.js +22 -9
  131. package/dist/src/lib/workflow/ready-gate.d.ts +17 -4
  132. package/dist/src/lib/workflow/ready-gate.js +25 -9
  133. package/dist/src/lib/workflow/run-log-schema.d.ts +14 -0
  134. package/dist/src/lib/workflow/run-log-schema.js +47 -1
  135. package/dist/src/lib/workflow/run-orchestrator.d.ts +19 -0
  136. package/dist/src/lib/workflow/run-orchestrator.js +141 -20
  137. package/dist/src/lib/workflow/skills-preflight.d.ts +89 -0
  138. package/dist/src/lib/workflow/skills-preflight.js +112 -0
  139. package/dist/src/lib/workflow/state-cleanup.js +9 -2
  140. package/dist/src/lib/workflow/state-manager.d.ts +33 -0
  141. package/dist/src/lib/workflow/state-manager.js +83 -0
  142. package/dist/src/lib/workflow/state-schema.d.ts +26 -0
  143. package/dist/src/lib/workflow/state-schema.js +47 -0
  144. package/dist/src/lib/workflow/types.d.ts +126 -4
  145. package/dist/src/lib/workflow/types.js +1 -0
  146. package/dist/src/lib/workflow/worktree-manager.d.ts +28 -2
  147. package/dist/src/lib/workflow/worktree-manager.js +102 -23
  148. package/dist/src/mcp/tools/run.d.ts +9 -1
  149. package/dist/src/mcp/tools/run.js +20 -1
  150. package/dist/src/mcp/tools/status.js +6 -0
  151. package/dist/src/ui/tui/ElapsedTimer.d.ts +12 -3
  152. package/dist/src/ui/tui/ElapsedTimer.js +12 -10
  153. package/dist/src/ui/tui/IssueBox.js +10 -2
  154. package/dist/src/ui/tui/row-cap.js +2 -1
  155. package/dist/src/ui/tui/theme.d.ts +7 -0
  156. package/dist/src/ui/tui/theme.js +9 -0
  157. package/package.json +8 -7
  158. package/templates/hooks/parallel-marker.sh +50 -0
  159. package/templates/hooks/post-tool.sh +20 -7
  160. package/templates/hooks/pre-tool.sh +75 -10
  161. package/templates/scripts/cleanup-worktree.sh +182 -19
  162. package/templates/scripts/new-feature.sh +264 -16
  163. package/templates/skills/_shared/references/trust-model.md +18 -0
  164. package/templates/skills/assess/SKILL.md +215 -390
  165. package/templates/skills/assess/references/predicted-collision-detection.md +1 -1
  166. package/templates/skills/docs/SKILL.md +4 -4
  167. package/templates/skills/exec/SKILL.md +26 -6
  168. package/templates/skills/fullsolve/SKILL.md +3 -1
  169. package/templates/skills/loop/SKILL.md +38 -2
  170. package/templates/skills/merger/SKILL.md +2 -2
  171. package/templates/skills/qa/SKILL.md +337 -57
  172. package/templates/skills/qa/references/anti-pattern-detection.md +6 -6
  173. package/templates/skills/qa/references/call-site-review.md +2 -2
  174. package/templates/skills/qa/references/code-review-checklist.md +2 -2
  175. package/templates/skills/qa/references/fixtures/injection-issue-body.md +36 -0
  176. package/templates/skills/qa/references/quality-gates.md +21 -7
  177. package/templates/skills/qa/references/testing-requirements.md +1 -1
  178. package/templates/skills/qa/scripts/quality-checks.sh +11 -11
  179. package/templates/skills/release/SKILL.md +1 -1
  180. package/templates/skills/setup/SKILL.md +5 -5
  181. package/templates/skills/spec/SKILL.md +2 -0
  182. package/templates/skills/test/SKILL.md +1 -1
  183. package/dist/src/lib/phase-spinner.d.ts +0 -146
  184. package/dist/src/lib/phase-spinner.js +0 -255
  185. package/dist/src/lib/workflow/pr-operations.d.ts +0 -86
  186. package/dist/src/lib/workflow/pr-operations.js +0 -326
  187. package/dist/src/lib/workflow/run-summary.d.ts +0 -36
  188. package/dist/src/lib/workflow/run-summary.js +0 -142
@@ -364,6 +364,89 @@ export class StateManager {
364
364
  console.log(`State: PR #${pr.number} linked to issue #${issueNumber}`);
365
365
  }
366
366
  }
367
+ /**
368
+ * Record or clear an in-progress auto-wait (#860). `wakeAtMs` sets the
369
+ * pause marker (and which phase is paused); `null` clears it on wake.
370
+ *
371
+ * Unlike the sibling updaters this NEVER throws on an untracked issue —
372
+ * it is called from a liveness hook during a live wait, and a bookkeeping
373
+ * miss must not interrupt the pause it is describing.
374
+ */
375
+ async updateAutoWait(issueNumber, phase, wakeAtMs) {
376
+ await this.withLock(async () => {
377
+ const state = await this.getState();
378
+ const issueState = state.issues[String(issueNumber)];
379
+ if (!issueState)
380
+ return;
381
+ issueState.autoWait =
382
+ wakeAtMs === null
383
+ ? undefined
384
+ : { wakeAt: new Date(wakeAtMs).toISOString(), phase };
385
+ issueState.lastActivity = new Date().toISOString();
386
+ await this.saveState(state);
387
+ });
388
+ }
389
+ /**
390
+ * Record a durable waitable-window halt (#892 AC-1). Preserves an existing
391
+ * re-entry counter so repeated halts on a still-closed window keep counting
392
+ * toward the re-entry bound instead of resetting it.
393
+ *
394
+ * Like `updateAutoWait`, NEVER throws on an untracked issue — it runs on a
395
+ * halt path where bookkeeping must not mask the real failure.
396
+ */
397
+ async updateWindowHalt(issueNumber, phase, resumeAtMs) {
398
+ await this.withLock(async () => {
399
+ const state = await this.getState();
400
+ const issueState = state.issues[String(issueNumber)];
401
+ if (!issueState)
402
+ return;
403
+ issueState.windowHalt = {
404
+ resumeAt: new Date(resumeAtMs).toISOString(),
405
+ phase,
406
+ reentries: issueState.windowHalt?.reentries ?? 0,
407
+ };
408
+ issueState.lastActivity = new Date().toISOString();
409
+ await this.saveState(state);
410
+ });
411
+ }
412
+ /**
413
+ * Clear a stale window-halt record (#892) — on any phase success (the
414
+ * window demonstrably reopened; progress also resets the re-entry counter)
415
+ * or a non-window failure (the halt cause is no longer waitable, so an
416
+ * unattended re-entry must not fire on it). No-ops (no state write) when
417
+ * nothing is recorded; never throws on an untracked issue.
418
+ */
419
+ async clearWindowHalt(issueNumber) {
420
+ await this.withLock(async () => {
421
+ const state = await this.getState();
422
+ const issueState = state.issues[String(issueNumber)];
423
+ if (!issueState?.windowHalt)
424
+ return;
425
+ issueState.windowHalt = undefined;
426
+ issueState.lastActivity = new Date().toISOString();
427
+ await this.saveState(state);
428
+ });
429
+ }
430
+ /**
431
+ * Consume one re-entry attempt for a halted issue (#892 AC-3). Returns the
432
+ * new count, or `null` when the issue has no window-halt record (nothing to
433
+ * resume). Called by `sequant resume` immediately before re-entry so a
434
+ * re-entry that halts again (window still closed) is already counted.
435
+ */
436
+ async incrementWindowHaltReentries(issueNumber) {
437
+ let newCount = null;
438
+ await this.withLock(async () => {
439
+ const state = await this.getState();
440
+ const issueState = state.issues[String(issueNumber)];
441
+ if (!issueState?.windowHalt)
442
+ return;
443
+ issueState.windowHalt.reentries += 1;
444
+ newCount = issueState.windowHalt.reentries;
445
+ issueState.lastActivity = new Date().toISOString();
446
+ await this.saveState(state);
447
+ });
448
+ return newCount;
449
+ }
367
450
  /**
368
451
  * Update worktree information for an issue
369
452
  */
@@ -213,6 +213,14 @@ export declare const RelayStateSchema: z.ZodObject<{
213
213
  messageCount: z.ZodNumber;
214
214
  }, z.core.$strip>;
215
215
  export type RelayState = z.infer<typeof RelayStateSchema>;
216
+ /**
217
+ * Maximum scheduler re-entries per window-halted issue (#892 AC-3). Mirrors
218
+ * `AUTO_WAIT_MAX_WAITS` (#804): both bound how often sequant returns to the
219
+ * same closed rate-limit window before declaring the halt terminal. Lives
220
+ * here — beside the `windowHalt.reentries` field it bounds — so display
221
+ * surfaces (`sequant status`) can import it without pulling in the run path.
222
+ */
223
+ export declare const MAX_RESUME_REENTRIES = 2;
216
224
  /**
217
225
  * Complete state for a single issue
218
226
  */
@@ -342,6 +350,15 @@ export declare const IssueStateSchema: z.ZodObject<{
342
350
  token: z.ZodString;
343
351
  originCwd: z.ZodString;
344
352
  }, z.core.$strip>>;
353
+ autoWait: z.ZodOptional<z.ZodObject<{
354
+ wakeAt: z.ZodString;
355
+ phase: z.ZodString;
356
+ }, z.core.$strip>>;
357
+ windowHalt: z.ZodOptional<z.ZodObject<{
358
+ resumeAt: z.ZodString;
359
+ phase: z.ZodString;
360
+ reentries: z.ZodNumber;
361
+ }, z.core.$strip>>;
345
362
  resolvedAt: z.ZodOptional<z.ZodString>;
346
363
  lastActivity: z.ZodString;
347
364
  createdAt: z.ZodString;
@@ -482,6 +499,15 @@ export declare const WorkflowStateSchema: z.ZodObject<{
482
499
  token: z.ZodString;
483
500
  originCwd: z.ZodString;
484
501
  }, z.core.$strip>>;
502
+ autoWait: z.ZodOptional<z.ZodObject<{
503
+ wakeAt: z.ZodString;
504
+ phase: z.ZodString;
505
+ }, z.core.$strip>>;
506
+ windowHalt: z.ZodOptional<z.ZodObject<{
507
+ resumeAt: z.ZodString;
508
+ phase: z.ZodString;
509
+ reentries: z.ZodNumber;
510
+ }, z.core.$strip>>;
485
511
  resolvedAt: z.ZodOptional<z.ZodString>;
486
512
  lastActivity: z.ZodString;
487
513
  createdAt: z.ZodString;
@@ -197,6 +197,14 @@ export const RelayStateSchema = z.object({
197
197
  startedAt: z.string().datetime(),
198
198
  messageCount: z.number().int().nonnegative(),
199
199
  });
200
+ /**
201
+ * Maximum scheduler re-entries per window-halted issue (#892 AC-3). Mirrors
202
+ * `AUTO_WAIT_MAX_WAITS` (#804): both bound how often sequant returns to the
203
+ * same closed rate-limit window before declaring the halt terminal. Lives
204
+ * here — beside the `windowHalt.reentries` field it bounds — so display
205
+ * surfaces (`sequant status`) can import it without pulling in the run path.
206
+ */
207
+ export const MAX_RESUME_REENTRIES = 2;
200
208
  /**
201
209
  * Complete state for a single issue
202
210
  */
@@ -248,6 +256,45 @@ export const IssueStateSchema = z.object({
248
256
  originCwd: z.string(),
249
257
  })
250
258
  .optional(),
259
+ /**
260
+ * An in-progress auto-wait (#860): the phase is deliberately paused until a
261
+ * rate-limit window reopens at `wakeAt`. Written at wait start and cleared
262
+ * on wake, so `sequant status` can say "waiting until 07:01" instead of
263
+ * showing an hours-stale in-progress phase indistinguishable from a hang
264
+ * (#856). The write itself also refreshes `state.json`'s mtime — the
265
+ * activity proxy several liveness surfaces poll.
266
+ */
267
+ autoWait: z
268
+ .object({
269
+ /** ISO timestamp of the scheduled wake (resetsAt + buffer). */
270
+ wakeAt: z.string().datetime(),
271
+ /** Phase that is paused. */
272
+ phase: z.string(),
273
+ })
274
+ .optional(),
275
+ /**
276
+ * A durable halt on an exhausted rate-limit window (#892): the run exited
277
+ * cleanly (lock released) and can be re-entered by `sequant resume` once
278
+ * the window reopens at `resumeAt`. Unlike `autoWait` (a live in-process
279
+ * pause), this record survives reboots — it is the machine-readable
280
+ * contract between the halt and an unattended scheduler re-entry.
281
+ * Written at a waitable-window halt, cleared on any phase success or a
282
+ * non-window failure (the halt cause is then no longer waitable).
283
+ */
284
+ windowHalt: z
285
+ .object({
286
+ /** ISO timestamp after which re-entry can proceed (resetsAt + buffer). */
287
+ resumeAt: z.string().datetime(),
288
+ /** Phase that halted. */
289
+ phase: z.string(),
290
+ /**
291
+ * Re-entry attempts consumed (#892 AC-3). Incremented by `sequant
292
+ * resume` before each re-entry; a window that never reopens is bounded
293
+ * by MAX_RESUME_REENTRIES instead of ping-ponging a scheduler.
294
+ */
295
+ reentries: z.number().int().min(0),
296
+ })
297
+ .optional(),
251
298
  /** When the issue transitioned to a terminal status (merged/abandoned/closed) */
252
299
  resolvedAt: z.string().datetime().optional(),
253
300
  /** Most recent activity timestamp */
@@ -9,6 +9,7 @@ import type { ShutdownManager } from "../shutdown.js";
9
9
  import type { WorktreeInfo } from "./worktree-manager.js";
10
10
  import type { SequantError } from "../errors.js";
11
11
  import type { ErrorCategory } from "./error-classifier.js";
12
+ import type { ReadyResult } from "./ready-gate.js";
12
13
  export type { WorkflowEventEmitter, WorkflowEvents, WorkflowEventListener, IssueEventStatus, BaseEventPayload, RunEventPayload, PhaseStartedPayload, PhaseCompletedPayload, PhaseFailedPayload, IssueStatusChangedPayload, QaVerdictPayload, ProgressPayload, } from "./event-emitter.js";
13
14
  /**
14
15
  * Canonical Zod schema for all workflow phases.
@@ -131,6 +132,14 @@ export interface ExecutionConfig {
131
132
  * poll budget is preserved.
132
133
  */
133
134
  onActivity?: (text: string) => void;
135
+ /**
136
+ * Runtime callback invoked while an auto-wait (#804) is in progress, once
137
+ * per tick plus once when it ends. Used to keep the renderer and the `-q`
138
+ * heartbeat showing the wait and its wake time, so a multi-hour pause reads
139
+ * as deliberate rather than as the silent stall of #574. Not serialized —
140
+ * set per-call by the orchestrator, like {@link onActivity}.
141
+ */
142
+ onAutoWait?: (notice: AutoWaitNotice) => void;
134
143
  /**
135
144
  * Enable interactive relay (#383). When true, phase-executor sets
136
145
  * `SEQUANT_RELAY=true` in the agent environment so the PostToolUse hook
@@ -147,6 +156,53 @@ export interface ExecutionConfig {
147
156
  * QA pass does NOT skip the checks that catch the #318/#529/#570 class.
148
157
  */
149
158
  fullQa?: boolean;
159
+ /**
160
+ * Run the post-QA ready gate after the standard phases succeed (#817).
161
+ *
162
+ * When true, `runIssueWithLogging` invokes the existing `sequant ready`
163
+ * engine (`runReadyGate`) at the PR seam — before rebase/PR so the gate's
164
+ * auto-fix commits land in the PR — driving the issue to the configured
165
+ * `settings.ready.policy` threshold. It NEVER merges: the run terminates with
166
+ * the issue in `waiting_for_human_merge` (ready) or `blocked` (guard halt).
167
+ * Default off; when unset the run path is byte-identical to pre-#817.
168
+ */
169
+ readyGate?: boolean;
170
+ /**
171
+ * Total wait budget, in minutes, for auto-waiting out an exhausted
172
+ * rate-limit window (#804). `0` (the default) disables auto-wait entirely,
173
+ * preserving the #761/#799 halt behavior byte-for-byte.
174
+ *
175
+ * When a phase fails with a window-exhausted `RateLimitError` whose reset
176
+ * lies within the remaining budget, the executor sleeps until the reset and
177
+ * retries the phase instead of returning the failure. Named for the
178
+ * *behavior* (waiting), not the mechanism, so it survives a future move from
179
+ * in-process sleep to out-of-process rescheduling.
180
+ *
181
+ * This is a TOTAL budget across the issue, not a per-occurrence allowance —
182
+ * see {@link AutoWaitLedger}.
183
+ */
184
+ autoWaitMinutes?: number;
185
+ }
186
+ /**
187
+ * A single liveness notice emitted during an auto-wait (#804 AC-7).
188
+ */
189
+ export interface AutoWaitNotice {
190
+ /** Issue the wait belongs to. */
191
+ issueNumber: number;
192
+ /** Phase whose retry is being waited for. */
193
+ phase: string;
194
+ /** Epoch ms at which the wait ends (reset + buffer). */
195
+ wakeAtMs: number;
196
+ /** Ms remaining at the time of this notice. `0` on the final notice. */
197
+ remainingMs: number;
198
+ /**
199
+ * Display string, already formatted. Built from `formatRateLimitMessage`
200
+ * plus the wake time — never by re-appending `resetsAt` to an
201
+ * already-formatted driver message (the #799 doubled-string trap).
202
+ */
203
+ message: string;
204
+ /** True on the terminal notice, once the wait has ended or been aborted. */
205
+ done: boolean;
150
206
  }
151
207
  /**
152
208
  * Default execution configuration
@@ -205,6 +261,14 @@ export interface IssueResult {
205
261
  prNumber?: number;
206
262
  /** PR URL if created after successful QA */
207
263
  prUrl?: string;
264
+ /**
265
+ * Set when PR creation was attempted after passing QA but failed (#879).
266
+ * A failed `createPR` used to be a non-fatal yellow warning while the issue
267
+ * still reported success — so a run could "pass" while producing no PR. When
268
+ * present, `success` is false and the run summary counts the issue as failed
269
+ * with this string as the reason.
270
+ */
271
+ prCreationError?: string;
208
272
  /**
209
273
  * Set when the issue was skipped because another sequant session holds
210
274
  * the per-issue lock (#625). Surfaced in the summary as
@@ -232,6 +296,25 @@ export interface IssueResult {
232
296
  * privacy contract). Absent on success.
233
297
  */
234
298
  failureCategory?: ErrorCategory;
299
+ /**
300
+ * Outcome of the post-QA ready gate (#817), present only when the run was
301
+ * invoked with `--ready-gate` and the gate actually ran (i.e. the standard
302
+ * phases succeeded). Carries the terminal reason, the persisted issue status
303
+ * (`waiting_for_human_merge` / `blocked` — never merged), and the rendered
304
+ * report the summary and PR body surface. Absent on runs without the flag.
305
+ */
306
+ readyGate?: ReadyResult;
307
+ /**
308
+ * Why the ready gate did NOT run, when `--ready-gate` was requested but the
309
+ * gate threw (#817). Mutually exclusive with {@link IssueResult.readyGate}.
310
+ *
311
+ * A gate crash is deliberately non-fatal — the phase work is already
312
+ * committed and the PR still opens — but it must not be silent. Without this
313
+ * field a run whose gate died is byte-identical in the summary to one that
314
+ * never asked for a gate, so the user believes a second look happened when it
315
+ * did not. The summary renders this as an explicit "gate did not run" line.
316
+ */
317
+ readyGateError?: string;
235
318
  }
236
319
  /**
237
320
  * CLI options for the run command, merged with settings and env config.
@@ -263,8 +346,6 @@ export interface RunOptions {
263
346
  autoDetectPhases?: boolean;
264
347
  /** Enable automatic worktree creation for issue isolation */
265
348
  worktreeIsolation?: boolean;
266
- /** Reuse existing worktrees instead of creating new ones */
267
- reuseWorktrees?: boolean;
268
349
  /** Suppress version warnings and non-essential output */
269
350
  quiet?: boolean;
270
351
  /** Chain issues: each branches from previous (requires --sequential) */
@@ -299,6 +380,13 @@ export interface RunOptions {
299
380
  * Resolution priority: this CLI flag → settings.run.mcp → default (true)
300
381
  */
301
382
  noMcp?: boolean;
383
+ /**
384
+ * Total minutes willing to wait for an exhausted rate-limit window to reopen
385
+ * before halting (#804). `0` (default) keeps today's immediate halt.
386
+ * Resolution priority: this CLI flag → SEQUANT_AUTO_WAIT_MINUTES →
387
+ * settings.run.autoWaitMinutes → default (0).
388
+ */
389
+ autoWaitMinutes?: number;
302
390
  /**
303
391
  * Resume from last completed phase.
304
392
  * Reads phase markers from GitHub issue comments and skips completed phases.
@@ -324,7 +412,9 @@ export interface RunOptions {
324
412
  noPr?: boolean;
325
413
  /**
326
414
  * Force re-execution of issues even if they have completed status.
327
- * Bypasses the pre-flight state guard that skips ready_for_merge/merged issues.
415
+ * Bypasses the pre-flight state guard that skips completed issues — see
416
+ * `isCompletedIssueStatus` (completed-status.ts) for exactly which statuses
417
+ * that covers, and why `blocked` is not one of them.
328
418
  */
329
419
  force?: boolean;
330
420
  /**
@@ -361,6 +451,17 @@ export interface RunOptions {
361
451
  * #705: now a hidden no-op alias — the boxed Ink TUI is the default, so
362
452
  * `--experimental-tui` only parses for backward compatibility and no longer
363
453
  * gates rendering. Kept so existing scripts/muscle-memory don't break.
454
+ *
455
+ * INTENTIONALLY INERT — do not delete (#810). A dead-surface sweep of
456
+ * `RunOptions` will correctly observe that nothing branches on this field.
457
+ * That is the design, not a defect: the flag's whole job is to parse and do
458
+ * nothing, so scripts written against #705 keep working. Its inertness is
459
+ * asserted, not incidental — `run-flags.test.ts` ("--experimental-tui is a
460
+ * no-op"), `cli.integration.test.ts` (hidden from `--help`, still parses),
461
+ * and `run-tui.integration.test.ts` all pin it. Contrast `reuseWorktrees`,
462
+ * removed in #810: that field had no flag, no consumer, and no test, so it
463
+ * promised behavior nothing delivered. The distinction is a flag deliberately
464
+ * kept parseable versus a type field nobody could ever reach.
364
465
  */
365
466
  experimentalTui?: boolean;
366
467
  /**
@@ -375,6 +476,16 @@ export interface RunOptions {
375
476
  * Resolution priority: this CLI flag → settings.run.relay → default (true).
376
477
  */
377
478
  relay?: boolean;
479
+ /**
480
+ * Run the post-QA ready gate after the standard phases succeed (#817). Set via
481
+ * `--ready-gate`. When true, the run drives the issue to the configured
482
+ * `settings.ready.policy` threshold through the existing `sequant ready`
483
+ * engine before creating the PR, then STOPS at the human merge gate — it never
484
+ * merges. Off by default; opt-in only, so an unset flag leaves the run path
485
+ * (including the #749 break-to-PR behavior) unchanged. Reuses `ready`'s policy
486
+ * and bounds wholesale — no new settings accompany this flag.
487
+ */
488
+ readyGate?: boolean;
378
489
  }
379
490
  /**
380
491
  * CLI arguments for run command
@@ -415,12 +526,23 @@ export interface BatchResult {
415
526
  * `"activity"` (#543): sub-phase activity ping. `extra.text` carries a short
416
527
  * one-line snippet (e.g. last line of agent output) for the dashboard's
417
528
  * `nowLine`. Fires at most ~10 Hz from the phase executor.
529
+ *
530
+ * `"waiting"` (#804): an auto-wait for a rate-limit window is in progress.
531
+ * `extra.text` is the display message and `extra.wakeAtMs` the wake time;
532
+ * a `"waiting"` event WITHOUT `wakeAtMs` is the terminal notice that clears
533
+ * the waiting state. Unlike `"activity"`, this must reach both display paths
534
+ * — a multi-hour pause with no signal is the #574 complaint at 60x scale.
418
535
  */
419
- export type ProgressCallback = (issue: number, phase: string, event: "start" | "complete" | "failed" | "activity", extra?: {
536
+ export type ProgressCallback = (issue: number, phase: string, event: "start" | "complete" | "failed" | "activity" | "waiting", extra?: {
420
537
  durationSeconds?: number;
421
538
  error?: string;
422
539
  iteration?: number;
423
540
  text?: string;
541
+ /**
542
+ * `"waiting"` (#804): epoch ms at which an auto-wait ends. Absent on the
543
+ * terminal notice, which clears the waiting state.
544
+ */
545
+ wakeAtMs?: number;
424
546
  }) => void;
425
547
  /**
426
548
  * #672 AC-2: fired once per issue after the executor has resolved the final
@@ -45,4 +45,5 @@ export const DEFAULT_CONFIG = {
45
45
  dryRun: false,
46
46
  mcp: true,
47
47
  retry: true,
48
+ autoWaitMinutes: 0,
48
49
  };
@@ -145,6 +145,21 @@ export declare function filterResumedPhases(issueNumber: number, phases: Phase[]
145
145
  phases: Phase[];
146
146
  skipped: Phase[];
147
147
  };
148
+ /**
149
+ * Run the frozen dependency install for a freshly provisioned worktree and
150
+ * surface (rather than swallow) a failed install.
151
+ *
152
+ * Mirrors the status check in `reinstallIfLockfileChanged` (#846): a plain
153
+ * `npm install` self-heals a stale/absent lockfile, but the frozen `npm ci`
154
+ * hard-fails (exit 1, no node_modules). Without this check a failing install
155
+ * left a silently dependency-less worktree whose breakage only surfaced later
156
+ * as a confusing phase error. The warning names the resolved command so the
157
+ * user can rerun it by hand.
158
+ *
159
+ * @returns true if the install succeeded, false if it failed (warn-and-continue)
160
+ * @internal Exported for testing
161
+ */
162
+ export declare function installWorktreeDeps(worktreePath: string, packageManager: string | undefined, verbose: boolean): boolean;
148
163
  /**
149
164
  * Create or reuse a worktree for an issue
150
165
  * @param baseBranch - Optional branch to use as base instead of origin/main (for chain mode)
@@ -225,17 +240,25 @@ export declare function rebaseBeforePR(worktreePath: string, issueNumber: number
225
240
  * @param opts.qaVerdict QA verdict for the run; a non-A+ stopping state
226
241
  * (anything other than `READY_FOR_MERGE`) surfaces a note so a human reviewer
227
242
  * sees why the run broke to PR without reaching A+ (#749).
243
+ * @param opts.readyGateReport Rendered `sequant ready` gap report (#817). Set
244
+ * only when the run used `--ready-gate`; surfaces the gate outcome (threshold
245
+ * reached vs guard halt) in the PR body the same way `sequant ready` does.
228
246
  * @internal Exported for testing
229
247
  */
230
248
  export declare function buildAutomatedPRBody(issueNumber: number, opts?: {
231
249
  stackManifest?: string;
232
250
  qaVerdict?: string;
251
+ readyGateReport?: string;
233
252
  }): string;
234
253
  /**
235
254
  * Push branch and create a PR after successful QA.
236
255
  *
237
256
  * Handles both fresh PR creation and detection of existing PRs.
238
- * Failures are warnings — they don't fail the run.
257
+ *
258
+ * Returns `{ success: false, error }` on failure. As of #879 the caller
259
+ * (`runIssueWithLogging`) treats an attempted-but-failed PR as a run failure:
260
+ * a passing run that produced no PR is not a success. This function itself
261
+ * still only reports — it never throws — so relay teardown runs regardless.
239
262
  *
240
263
  * @param worktreePath Path to the worktree
241
264
  * @param issueNumber Issue number
@@ -246,10 +269,13 @@ export declare function buildAutomatedPRBody(issueNumber: number, opts?: {
246
269
  * @param stackOptions When set under --stacked, `prBase` overrides the default
247
270
  * PR target (otherwise gh defaults to the repo's default branch) and
248
271
  * `stackManifest` is appended to the PR body. (#605)
272
+ * @param qaVerdict QA verdict surfaced in the PR body when non-A+ (#749).
273
+ * @param readyGateReport Rendered `sequant ready` gap report, set only when the
274
+ * run used `--ready-gate` (#817).
249
275
  * @returns PRCreationResult with PR info or error
250
276
  * @internal Exported for testing
251
277
  */
252
278
  export declare function createPR(worktreePath: string, issueNumber: number, issueTitle: string, branch: string, verbose: boolean, labels?: string[], stackOptions?: {
253
279
  prBase?: string;
254
280
  stackManifest?: string;
255
- }, qaVerdict?: string): PRCreationResult;
281
+ }, qaVerdict?: string, readyGateReport?: string): PRCreationResult;