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
@@ -48,6 +48,37 @@ export declare const QaVerdictSchema: z.ZodEnum<{
48
48
  NEEDS_VERIFICATION: "NEEDS_VERIFICATION";
49
49
  }>;
50
50
  export type QaVerdict = z.infer<typeof QaVerdictSchema>;
51
+ /**
52
+ * Source that produced the resolved spec→run phase recommendation (#921).
53
+ *
54
+ * Ordered by resolution priority: a durable structured marker in the spec's
55
+ * GitHub comment beats the same comment's prose section, which beats the
56
+ * spec agent's ephemeral chat text, which beats label-based guessing.
57
+ */
58
+ export declare const SpecRecommendationSourceSchema: z.ZodEnum<{
59
+ marker: "marker";
60
+ "comment-prose": "comment-prose";
61
+ chat: "chat";
62
+ "label-fallback": "label-fallback";
63
+ }>;
64
+ export type SpecRecommendationSource = z.infer<typeof SpecRecommendationSourceSchema>;
65
+ /**
66
+ * Resolved spec→run phase recommendation, recorded on the issue log so
67
+ * fallback frequency is auditable (#921 AC-4). Additive/optional — absent on
68
+ * runs that never reached spec resolution (e.g. spec failed) or predate this
69
+ * field, keeping the persisted-log schema stable at `version: 1`.
70
+ */
71
+ export declare const SpecRecommendationSchema: z.ZodObject<{
72
+ source: z.ZodEnum<{
73
+ marker: "marker";
74
+ "comment-prose": "comment-prose";
75
+ chat: "chat";
76
+ "label-fallback": "label-fallback";
77
+ }>;
78
+ phases: z.ZodArray<z.ZodString>;
79
+ qualityLoop: z.ZodBoolean;
80
+ }, z.core.$strip>;
81
+ export type SpecRecommendation = z.infer<typeof SpecRecommendationSchema>;
51
82
  /**
52
83
  * File diff statistics for a single file (AC-3)
53
84
  */
@@ -91,6 +122,7 @@ export declare const ErrorContextSchema: z.ZodObject<{
91
122
  build_error: "build_error";
92
123
  rate_limit: "rate_limit";
93
124
  billing: "billing";
125
+ pr_creation: "pr_creation";
94
126
  }>;
95
127
  errorType: z.ZodOptional<z.ZodString>;
96
128
  errorMetadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
@@ -174,6 +206,7 @@ export declare const PhaseLogSchema: z.ZodObject<{
174
206
  build_error: "build_error";
175
207
  rate_limit: "rate_limit";
176
208
  billing: "billing";
209
+ pr_creation: "pr_creation";
177
210
  }>;
178
211
  errorType: z.ZodOptional<z.ZodString>;
179
212
  errorMetadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
@@ -253,6 +286,7 @@ export declare const IssueLogSchema: z.ZodObject<{
253
286
  build_error: "build_error";
254
287
  rate_limit: "rate_limit";
255
288
  billing: "billing";
289
+ pr_creation: "pr_creation";
256
290
  }>;
257
291
  errorType: z.ZodOptional<z.ZodString>;
258
292
  errorMetadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
@@ -260,8 +294,20 @@ export declare const IssueLogSchema: z.ZodObject<{
260
294
  }, z.core.$strip>>;
261
295
  }, z.core.$strip>>;
262
296
  totalDurationSeconds: z.ZodNumber;
297
+ aborted: z.ZodOptional<z.ZodBoolean>;
298
+ abortReason: z.ZodOptional<z.ZodString>;
263
299
  prNumber: z.ZodOptional<z.ZodNumber>;
264
300
  prUrl: z.ZodOptional<z.ZodString>;
301
+ specRecommendation: z.ZodOptional<z.ZodObject<{
302
+ source: z.ZodEnum<{
303
+ marker: "marker";
304
+ "comment-prose": "comment-prose";
305
+ chat: "chat";
306
+ "label-fallback": "label-fallback";
307
+ }>;
308
+ phases: z.ZodArray<z.ZodString>;
309
+ qualityLoop: z.ZodBoolean;
310
+ }, z.core.$strip>>;
265
311
  }, z.core.$strip>;
266
312
  export type IssueLog = z.infer<typeof IssueLogSchema>;
267
313
  /**
@@ -284,6 +330,7 @@ export declare const RunSummarySchema: z.ZodObject<{
284
330
  passed: z.ZodNumber;
285
331
  failed: z.ZodNumber;
286
332
  partial: z.ZodDefault<z.ZodNumber>;
333
+ aborted: z.ZodDefault<z.ZodNumber>;
287
334
  totalDurationSeconds: z.ZodNumber;
288
335
  }, z.core.$strip>;
289
336
  export type RunSummary = z.infer<typeof RunSummarySchema>;
@@ -374,6 +421,7 @@ export declare const RunLogSchema: z.ZodObject<{
374
421
  build_error: "build_error";
375
422
  rate_limit: "rate_limit";
376
423
  billing: "billing";
424
+ pr_creation: "pr_creation";
377
425
  }>;
378
426
  errorType: z.ZodOptional<z.ZodString>;
379
427
  errorMetadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
@@ -381,18 +429,32 @@ export declare const RunLogSchema: z.ZodObject<{
381
429
  }, z.core.$strip>>;
382
430
  }, z.core.$strip>>;
383
431
  totalDurationSeconds: z.ZodNumber;
432
+ aborted: z.ZodOptional<z.ZodBoolean>;
433
+ abortReason: z.ZodOptional<z.ZodString>;
384
434
  prNumber: z.ZodOptional<z.ZodNumber>;
385
435
  prUrl: z.ZodOptional<z.ZodString>;
436
+ specRecommendation: z.ZodOptional<z.ZodObject<{
437
+ source: z.ZodEnum<{
438
+ marker: "marker";
439
+ "comment-prose": "comment-prose";
440
+ chat: "chat";
441
+ "label-fallback": "label-fallback";
442
+ }>;
443
+ phases: z.ZodArray<z.ZodString>;
444
+ qualityLoop: z.ZodBoolean;
445
+ }, z.core.$strip>>;
386
446
  }, z.core.$strip>>;
387
447
  summary: z.ZodObject<{
388
448
  totalIssues: z.ZodNumber;
389
449
  passed: z.ZodNumber;
390
450
  failed: z.ZodNumber;
391
451
  partial: z.ZodDefault<z.ZodNumber>;
452
+ aborted: z.ZodDefault<z.ZodNumber>;
392
453
  totalDurationSeconds: z.ZodNumber;
393
454
  }, z.core.$strip>;
394
455
  startCommit: z.ZodOptional<z.ZodString>;
395
456
  endCommit: z.ZodOptional<z.ZodString>;
457
+ abortedBy: z.ZodOptional<z.ZodString>;
396
458
  }, z.core.$strip>;
397
459
  export type RunLog = z.infer<typeof RunLogSchema>;
398
460
  /**
@@ -421,6 +483,7 @@ export declare function generateLogFilename(runId: string, startTime: Date): str
421
483
  */
422
484
  export declare function createEmptyRunLog(config: RunConfig, options?: {
423
485
  startCommit?: string;
486
+ startTime?: Date;
424
487
  }): Omit<RunLog, "endTime">;
425
488
  /**
426
489
  * Create a phase log entry
@@ -448,6 +511,7 @@ export declare function completePhaseLog(phaseLog: Omit<PhaseLog, "endTime" | "d
448
511
  */
449
512
  export declare function finalizeRunLog(runLog: Omit<RunLog, "endTime">, options?: {
450
513
  endCommit?: string;
514
+ abortedBy?: string;
451
515
  }): RunLog;
452
516
  /**
453
517
  * Derive an issue's overall log status from its phase log entries (#766).
@@ -460,5 +524,10 @@ export declare function finalizeRunLog(runLog: Omit<RunLog, "endTime">, options?
460
524
  * verdict (mirrors the live-card rule); an unrecovered failure still leaves a
461
525
  * non-loop phase failed. Priority among latest attempts: failure > timeout >
462
526
  * success.
527
+ *
528
+ * An issue with no phase entries is a `failure`, not a `success` (#856). The
529
+ * only way to reach that state is for `startIssue` to have run while no phase
530
+ * ever completed — i.e. the run was cut short. Returning `success` there is
531
+ * what let a SIGTERM'd run persist as a pass with an empty `phases[]`.
463
532
  */
464
533
  export declare function deriveIssueLogStatus(phases: PhaseLog[]): IssueStatus;
@@ -42,6 +42,33 @@ export const QaVerdictSchema = z.enum([
42
42
  "AC_NOT_MET",
43
43
  "NEEDS_VERIFICATION",
44
44
  ]);
45
+ /**
46
+ * Source that produced the resolved spec→run phase recommendation (#921).
47
+ *
48
+ * Ordered by resolution priority: a durable structured marker in the spec's
49
+ * GitHub comment beats the same comment's prose section, which beats the
50
+ * spec agent's ephemeral chat text, which beats label-based guessing.
51
+ */
52
+ export const SpecRecommendationSourceSchema = z.enum([
53
+ "marker",
54
+ "comment-prose",
55
+ "chat",
56
+ "label-fallback",
57
+ ]);
58
+ /**
59
+ * Resolved spec→run phase recommendation, recorded on the issue log so
60
+ * fallback frequency is auditable (#921 AC-4). Additive/optional — absent on
61
+ * runs that never reached spec resolution (e.g. spec failed) or predate this
62
+ * field, keeping the persisted-log schema stable at `version: 1`.
63
+ */
64
+ export const SpecRecommendationSchema = z.object({
65
+ /** Which step in the resolution chain produced this result */
66
+ source: SpecRecommendationSourceSchema,
67
+ /** Resolved phases, spec excluded (spec already ran) */
68
+ phases: z.array(PhaseSchema),
69
+ /** Whether the quality loop should be enabled */
70
+ qualityLoop: z.boolean(),
71
+ });
45
72
  /**
46
73
  * File diff statistics for a single file (AC-3)
47
74
  */
@@ -82,7 +109,7 @@ export const ErrorContextSchema = z.object({
82
109
  /**
83
110
  * Classified error category (legacy, kept for backwards compatibility).
84
111
  * Keep in sync with `ERROR_CATEGORIES` in `error-classifier.ts` —
85
- * `rate_limit` / `billing` added by #761 AC-6.
112
+ * `rate_limit` / `billing` added by #761 AC-6, `pr_creation` by #920.
86
113
  */
87
114
  category: z.enum([
88
115
  "context_overflow",
@@ -92,6 +119,7 @@ export const ErrorContextSchema = z.object({
92
119
  "timeout",
93
120
  "rate_limit",
94
121
  "billing",
122
+ "pr_creation",
95
123
  "unknown",
96
124
  ]),
97
125
  /** Typed error class name (AC-8), e.g. "ApiError", "BuildError" */
@@ -180,10 +208,26 @@ export const IssueLogSchema = z.object({
180
208
  phases: z.array(PhaseLogSchema),
181
209
  /** Total execution time in seconds */
182
210
  totalDurationSeconds: z.number().nonnegative(),
211
+ /**
212
+ * Set when this issue never finished because the run was terminated from
213
+ * outside (#856). Reuses the `"failure"` status — additive boolean rather
214
+ * than a new `IssueStatus` enum value, matching the `capped` precedent
215
+ * above, so the persisted-log schema stays stable at `version: 1`.
216
+ *
217
+ * Without this, a run killed mid-flight persisted as `success`: `startIssue`
218
+ * seeds `status: "success"` and only `logPhase` ever revises it, so an issue
219
+ * whose first phase never completed was written out as a pass with an empty
220
+ * `phases[]`.
221
+ */
222
+ aborted: z.boolean().optional(),
223
+ /** Human-readable cause of the abort (e.g. `terminated by SIGTERM`). */
224
+ abortReason: z.string().optional(),
183
225
  /** PR number if created after successful QA */
184
226
  prNumber: z.number().int().positive().optional(),
185
227
  /** PR URL if created after successful QA */
186
228
  prUrl: z.string().optional(),
229
+ /** How the spec→run phase recommendation was resolved (#921 AC-4) */
230
+ specRecommendation: SpecRecommendationSchema.optional(),
187
231
  });
188
232
  /**
189
233
  * Run configuration
@@ -219,6 +263,14 @@ export const RunSummarySchema = z.object({
219
263
  * `.default(0)` keeps logs written before this field parseable.
220
264
  */
221
265
  partial: z.number().int().nonnegative().default(0),
266
+ /**
267
+ * Number of issues cut short by an external signal (#856). A sub-count of
268
+ * `failed`, not a fourth disjoint bucket — aborted issues carry
269
+ * `status: "failure"` so existing consumers keep working, and this field
270
+ * says whether the failure was the run's own or something killing it.
271
+ * `.default(0)` keeps logs written before this field parseable.
272
+ */
273
+ aborted: z.number().int().nonnegative().default(0),
222
274
  /** Total execution time in seconds */
223
275
  totalDurationSeconds: z.number().nonnegative(),
224
276
  });
@@ -246,6 +298,11 @@ export const RunLogSchema = z.object({
246
298
  startCommit: z.string().optional(),
247
299
  /** Git commit SHA at run end (AC-2) */
248
300
  endCommit: z.string().optional(),
301
+ /**
302
+ * Signal that terminated the run, when it ended by external signal rather
303
+ * than by finishing (#856). Present iff at least one issue is `aborted`.
304
+ */
305
+ abortedBy: z.string().optional(),
249
306
  });
250
307
  /**
251
308
  * Default log directory paths
@@ -276,7 +333,9 @@ export function generateLogFilename(runId, startTime) {
276
333
  */
277
334
  export function createEmptyRunLog(config, options) {
278
335
  const runId = randomUUID();
279
- const startTime = new Date().toISOString();
336
+ // #867: use the caller-supplied run origin when provided so the log's wall
337
+ // clock shares the orchestrator's start; fall back to now for standalone use.
338
+ const startTime = (options?.startTime ?? new Date()).toISOString();
280
339
  return {
281
340
  version: 1,
282
341
  runId,
@@ -288,6 +347,7 @@ export function createEmptyRunLog(config, options) {
288
347
  passed: 0,
289
348
  failed: 0,
290
349
  partial: 0,
350
+ aborted: 0,
291
351
  totalDurationSeconds: 0,
292
352
  },
293
353
  startCommit: options?.startCommit,
@@ -343,6 +403,11 @@ export function finalizeRunLog(runLog, options) {
343
403
  // #766: `partial` gets its own bucket so an all-partial run isn't counted as
344
404
  // `0 passed · 0 failed` — it landed in neither before.
345
405
  const partial = runLog.issues.filter((i) => i.status === "partial").length;
406
+ // #856: aborted issues are also counted in `failed` (they carry
407
+ // `status: "failure"`); this is the sub-count that says *why* they failed,
408
+ // so a run killed from outside is distinguishable from one whose phases
409
+ // genuinely failed.
410
+ const aborted = runLog.issues.filter((i) => i.aborted).length;
346
411
  return {
347
412
  ...runLog,
348
413
  endTime: endTime.toISOString(),
@@ -351,9 +416,13 @@ export function finalizeRunLog(runLog, options) {
351
416
  passed,
352
417
  failed,
353
418
  partial,
419
+ aborted,
354
420
  totalDurationSeconds,
355
421
  },
356
422
  endCommit: options?.endCommit ?? runLog.endCommit,
423
+ ...((options?.abortedBy ?? runLog.abortedBy) && {
424
+ abortedBy: options?.abortedBy ?? runLog.abortedBy,
425
+ }),
357
426
  };
358
427
  }
359
428
  /**
@@ -367,6 +436,11 @@ export function finalizeRunLog(runLog, options) {
367
436
  * verdict (mirrors the live-card rule); an unrecovered failure still leaves a
368
437
  * non-loop phase failed. Priority among latest attempts: failure > timeout >
369
438
  * success.
439
+ *
440
+ * An issue with no phase entries is a `failure`, not a `success` (#856). The
441
+ * only way to reach that state is for `startIssue` to have run while no phase
442
+ * ever completed — i.e. the run was cut short. Returning `success` there is
443
+ * what let a SIGTERM'd run persist as a pass with an empty `phases[]`.
370
444
  */
371
445
  export function deriveIssueLogStatus(phases) {
372
446
  const latest = new Map();
@@ -376,6 +450,8 @@ export function deriveIssueLogStatus(phases) {
376
450
  latest.set(p.phase, p.status);
377
451
  }
378
452
  const statuses = [...latest.values()];
453
+ if (statuses.length === 0)
454
+ return "failure";
379
455
  if (statuses.some((s) => s === "failure"))
380
456
  return "failure";
381
457
  if (statuses.some((s) => s === "timeout"))
@@ -164,6 +164,13 @@ export interface RunResult {
164
164
  mergedOptions: RunOptions;
165
165
  /** Log writer (for reflection access) */
166
166
  logWriter: LogWriter | null;
167
+ /**
168
+ * Run wall-clock duration in seconds, computed once by the orchestrator from
169
+ * the run's start and end (#867). The post-run summary reads THIS value rather
170
+ * than re-summing per-issue durations — under `--parallel` that sum
171
+ * double-counts overlapping issues and over-reports by ~the concurrency factor.
172
+ */
173
+ wallClockDurationSeconds: number;
167
174
  }
168
175
  /**
169
176
  * CLI-free workflow execution engine.
@@ -188,6 +195,18 @@ export declare class RunOrchestrator {
188
195
  * listening (#504, AC-3).
189
196
  */
190
197
  getEmitter(): WorkflowEventEmitter;
198
+ /**
199
+ * The wrapped progress sink handed to the batch executor — every phase
200
+ * lifecycle event enters the orchestrator through here.
201
+ *
202
+ * @internal Exported for testing only. `applyProgressEvent` is private and
203
+ * depends on `issueStates`/`phaseStartTimes`/`emitter`, so there is no way
204
+ * to exercise its branches short of a full `run()`. Without this seam the
205
+ * #804 `waiting` branch would be untestable — and an untested `waiting`
206
+ * branch is not cosmetic: falling through to the complete/failed handler
207
+ * marks a merely-paused phase `failed` and emits `phase_failed`.
208
+ */
209
+ getProgressCallback(): ProgressCallback;
191
210
  /**
192
211
  * Point-in-time view of the entire run.
193
212
  *