sequant 2.11.0 → 2.13.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 (75) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.md +47 -3
  4. package/dist/dashboard/server.js +4 -0
  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 +9 -0
  8. package/dist/marketplace/external_plugins/sequant/hooks/pre-tool.sh +331 -12
  9. package/dist/marketplace/external_plugins/sequant/skills/_shared/references/subagent-types.md +7 -18
  10. package/dist/marketplace/external_plugins/sequant/skills/assess/SKILL.md +5 -1
  11. package/dist/marketplace/external_plugins/sequant/skills/exec/SKILL.md +62 -8
  12. package/dist/marketplace/external_plugins/sequant/skills/fullsolve/SKILL.md +187 -28
  13. package/dist/marketplace/external_plugins/sequant/skills/loop/SKILL.md +127 -23
  14. package/dist/marketplace/external_plugins/sequant/skills/merger/SKILL.md +130 -13
  15. package/dist/marketplace/external_plugins/sequant/skills/qa/SKILL.md +306 -8
  16. package/dist/marketplace/external_plugins/sequant/skills/release/SKILL.md +79 -0
  17. package/dist/marketplace/external_plugins/sequant/skills/spec/SKILL.md +42 -20
  18. package/dist/marketplace/external_plugins/sequant/skills/spec/references/recommended-workflow.md +14 -1
  19. package/dist/marketplace/external_plugins/sequant/skills/test/SKILL.md +1 -1
  20. package/dist/marketplace/external_plugins/sequant/skills/testgen/SKILL.md +23 -6
  21. package/dist/src/commands/doctor.js +20 -18
  22. package/dist/src/commands/ready.js +7 -1
  23. package/dist/src/commands/status.js +4 -0
  24. package/dist/src/lib/ac-linter.js +26 -0
  25. package/dist/src/lib/ac-parser.d.ts +40 -0
  26. package/dist/src/lib/ac-parser.js +202 -16
  27. package/dist/src/lib/markdown-fence.d.ts +24 -0
  28. package/dist/src/lib/markdown-fence.js +51 -0
  29. package/dist/src/lib/mcp-config.d.ts +24 -0
  30. package/dist/src/lib/mcp-config.js +51 -0
  31. package/dist/src/lib/scope/analyzer.d.ts +4 -0
  32. package/dist/src/lib/scope/analyzer.js +7 -1
  33. package/dist/src/lib/settings.d.ts +73 -14
  34. package/dist/src/lib/settings.js +45 -3
  35. package/dist/src/lib/system.d.ts +7 -3
  36. package/dist/src/lib/system.js +7 -3
  37. package/dist/src/lib/test-tautology-detector.js +50 -3
  38. package/dist/src/lib/workflow/batch-executor.d.ts +20 -1
  39. package/dist/src/lib/workflow/batch-executor.js +81 -6
  40. package/dist/src/lib/workflow/config-resolver.d.ts +30 -2
  41. package/dist/src/lib/workflow/config-resolver.js +59 -2
  42. package/dist/src/lib/workflow/drivers/agent-driver.d.ts +14 -0
  43. package/dist/src/lib/workflow/drivers/claude-code.js +36 -4
  44. package/dist/src/lib/workflow/metrics-schema.d.ts +10 -1
  45. package/dist/src/lib/workflow/metrics-schema.js +13 -1
  46. package/dist/src/lib/workflow/metrics-writer.d.ts +3 -1
  47. package/dist/src/lib/workflow/mutation-marker.d.ts +86 -0
  48. package/dist/src/lib/workflow/mutation-marker.js +97 -0
  49. package/dist/src/lib/workflow/phase-executor.d.ts +17 -0
  50. package/dist/src/lib/workflow/phase-executor.js +60 -6
  51. package/dist/src/lib/workflow/qa-gaps-marker.d.ts +38 -0
  52. package/dist/src/lib/workflow/qa-gaps-marker.js +66 -0
  53. package/dist/src/lib/workflow/ready-gate.d.ts +25 -1
  54. package/dist/src/lib/workflow/ready-gate.js +81 -11
  55. package/dist/src/lib/workflow/reconcile.js +4 -2
  56. package/dist/src/lib/workflow/run-log-schema.d.ts +120 -0
  57. package/dist/src/lib/workflow/run-log-schema.js +40 -0
  58. package/dist/src/lib/workflow/run-orchestrator.d.ts +18 -0
  59. package/dist/src/lib/workflow/run-orchestrator.js +38 -2
  60. package/dist/src/lib/workflow/state-cleanup.d.ts +4 -4
  61. package/dist/src/lib/workflow/state-cleanup.js +9 -5
  62. package/dist/src/lib/workflow/state-schema.d.ts +10 -1
  63. package/dist/src/lib/workflow/state-schema.js +13 -1
  64. package/dist/src/lib/workflow/types.d.ts +20 -0
  65. package/dist/src/mcp/tools/run.js +10 -1
  66. package/package.json +13 -12
  67. package/templates/hooks/pre-tool.sh +108 -17
  68. package/templates/memory/constitution.md +112 -45
  69. package/templates/skills/exec/SKILL.md +1 -1
  70. package/templates/skills/fullsolve/SKILL.md +62 -9
  71. package/templates/skills/loop/SKILL.md +71 -12
  72. package/templates/skills/merger/SKILL.md +32 -3
  73. package/templates/skills/qa/SKILL.md +247 -2
  74. package/templates/skills/spec/SKILL.md +11 -5
  75. package/templates/skills/test/SKILL.md +1 -1
@@ -129,6 +129,50 @@ export declare const ErrorContextSchema: z.ZodObject<{
129
129
  isRetryable: z.ZodOptional<z.ZodBoolean>;
130
130
  }, z.core.$strip>;
131
131
  export type ErrorContext = z.infer<typeof ErrorContextSchema>;
132
+ /**
133
+ * Finite taxonomy for a single gap finding surfaced by `/qa` (#937).
134
+ *
135
+ * Replaces the lossy prose-scrape gap channel (`parseListSection` against
136
+ * `**Issues:**`/`**Gaps**` headers, which misses AC-table NOT_MET rows, §6d
137
+ * Adversarial Re-Read findings, and §5 Risk Assessment). `evidence` is
138
+ * required so a finding can't be speculative — see #608's 0%-action-rate
139
+ * result for open-ended "what might we be missing" findings.
140
+ */
141
+ export declare const GapCategorySchema: z.ZodEnum<{
142
+ requirement_gap: "requirement_gap";
143
+ dependency_gap: "dependency_gap";
144
+ test_gap: "test_gap";
145
+ repository_gap: "repository_gap";
146
+ risk_gap: "risk_gap";
147
+ execution_gap: "execution_gap";
148
+ }>;
149
+ export type GapCategory = z.infer<typeof GapCategorySchema>;
150
+ export declare const GapActionSchema: z.ZodEnum<{
151
+ fix_now: "fix_now";
152
+ document: "document";
153
+ pause_for_human: "pause_for_human";
154
+ }>;
155
+ export type GapAction = z.infer<typeof GapActionSchema>;
156
+ export declare const GapFindingSchema: z.ZodObject<{
157
+ category: z.ZodEnum<{
158
+ requirement_gap: "requirement_gap";
159
+ dependency_gap: "dependency_gap";
160
+ test_gap: "test_gap";
161
+ repository_gap: "repository_gap";
162
+ risk_gap: "risk_gap";
163
+ execution_gap: "execution_gap";
164
+ }>;
165
+ evidence: z.ZodString;
166
+ description: z.ZodString;
167
+ recommendedAction: z.ZodEnum<{
168
+ fix_now: "fix_now";
169
+ document: "document";
170
+ pause_for_human: "pause_for_human";
171
+ }>;
172
+ affectedAcs: z.ZodOptional<z.ZodArray<z.ZodString>>;
173
+ nonGoal: z.ZodOptional<z.ZodBoolean>;
174
+ }, z.core.$strip>;
175
+ export type GapFinding = z.infer<typeof GapFindingSchema>;
132
176
  /**
133
177
  * Condensed QA verdict summary for structured log output (#434).
134
178
  *
@@ -141,6 +185,25 @@ export declare const QaSummarySchema: z.ZodObject<{
141
185
  acTotal: z.ZodNumber;
142
186
  gaps: z.ZodArray<z.ZodString>;
143
187
  suggestions: z.ZodArray<z.ZodString>;
188
+ findings: z.ZodOptional<z.ZodArray<z.ZodObject<{
189
+ category: z.ZodEnum<{
190
+ requirement_gap: "requirement_gap";
191
+ dependency_gap: "dependency_gap";
192
+ test_gap: "test_gap";
193
+ repository_gap: "repository_gap";
194
+ risk_gap: "risk_gap";
195
+ execution_gap: "execution_gap";
196
+ }>;
197
+ evidence: z.ZodString;
198
+ description: z.ZodString;
199
+ recommendedAction: z.ZodEnum<{
200
+ fix_now: "fix_now";
201
+ document: "document";
202
+ pause_for_human: "pause_for_human";
203
+ }>;
204
+ affectedAcs: z.ZodOptional<z.ZodArray<z.ZodString>>;
205
+ nonGoal: z.ZodOptional<z.ZodBoolean>;
206
+ }, z.core.$strip>>>;
144
207
  }, z.core.$strip>;
145
208
  export type QaSummary = z.infer<typeof QaSummarySchema>;
146
209
  /**
@@ -175,6 +238,25 @@ export declare const PhaseLogSchema: z.ZodObject<{
175
238
  acTotal: z.ZodNumber;
176
239
  gaps: z.ZodArray<z.ZodString>;
177
240
  suggestions: z.ZodArray<z.ZodString>;
241
+ findings: z.ZodOptional<z.ZodArray<z.ZodObject<{
242
+ category: z.ZodEnum<{
243
+ requirement_gap: "requirement_gap";
244
+ dependency_gap: "dependency_gap";
245
+ test_gap: "test_gap";
246
+ repository_gap: "repository_gap";
247
+ risk_gap: "risk_gap";
248
+ execution_gap: "execution_gap";
249
+ }>;
250
+ evidence: z.ZodString;
251
+ description: z.ZodString;
252
+ recommendedAction: z.ZodEnum<{
253
+ fix_now: "fix_now";
254
+ document: "document";
255
+ pause_for_human: "pause_for_human";
256
+ }>;
257
+ affectedAcs: z.ZodOptional<z.ZodArray<z.ZodString>>;
258
+ nonGoal: z.ZodOptional<z.ZodBoolean>;
259
+ }, z.core.$strip>>>;
178
260
  }, z.core.$strip>>;
179
261
  commitHash: z.ZodOptional<z.ZodString>;
180
262
  fileDiffStats: z.ZodOptional<z.ZodArray<z.ZodObject<{
@@ -255,6 +337,25 @@ export declare const IssueLogSchema: z.ZodObject<{
255
337
  acTotal: z.ZodNumber;
256
338
  gaps: z.ZodArray<z.ZodString>;
257
339
  suggestions: z.ZodArray<z.ZodString>;
340
+ findings: z.ZodOptional<z.ZodArray<z.ZodObject<{
341
+ category: z.ZodEnum<{
342
+ requirement_gap: "requirement_gap";
343
+ dependency_gap: "dependency_gap";
344
+ test_gap: "test_gap";
345
+ repository_gap: "repository_gap";
346
+ risk_gap: "risk_gap";
347
+ execution_gap: "execution_gap";
348
+ }>;
349
+ evidence: z.ZodString;
350
+ description: z.ZodString;
351
+ recommendedAction: z.ZodEnum<{
352
+ fix_now: "fix_now";
353
+ document: "document";
354
+ pause_for_human: "pause_for_human";
355
+ }>;
356
+ affectedAcs: z.ZodOptional<z.ZodArray<z.ZodString>>;
357
+ nonGoal: z.ZodOptional<z.ZodBoolean>;
358
+ }, z.core.$strip>>>;
258
359
  }, z.core.$strip>>;
259
360
  commitHash: z.ZodOptional<z.ZodString>;
260
361
  fileDiffStats: z.ZodOptional<z.ZodArray<z.ZodObject<{
@@ -390,6 +491,25 @@ export declare const RunLogSchema: z.ZodObject<{
390
491
  acTotal: z.ZodNumber;
391
492
  gaps: z.ZodArray<z.ZodString>;
392
493
  suggestions: z.ZodArray<z.ZodString>;
494
+ findings: z.ZodOptional<z.ZodArray<z.ZodObject<{
495
+ category: z.ZodEnum<{
496
+ requirement_gap: "requirement_gap";
497
+ dependency_gap: "dependency_gap";
498
+ test_gap: "test_gap";
499
+ repository_gap: "repository_gap";
500
+ risk_gap: "risk_gap";
501
+ execution_gap: "execution_gap";
502
+ }>;
503
+ evidence: z.ZodString;
504
+ description: z.ZodString;
505
+ recommendedAction: z.ZodEnum<{
506
+ fix_now: "fix_now";
507
+ document: "document";
508
+ pause_for_human: "pause_for_human";
509
+ }>;
510
+ affectedAcs: z.ZodOptional<z.ZodArray<z.ZodString>>;
511
+ nonGoal: z.ZodOptional<z.ZodBoolean>;
512
+ }, z.core.$strip>>>;
393
513
  }, z.core.$strip>>;
394
514
  commitHash: z.ZodOptional<z.ZodString>;
395
515
  fileDiffStats: z.ZodOptional<z.ZodArray<z.ZodObject<{
@@ -129,6 +129,39 @@ export const ErrorContextSchema = z.object({
129
129
  /** Whether this error type is retryable (AC-9) */
130
130
  isRetryable: z.boolean().optional(),
131
131
  });
132
+ /**
133
+ * Finite taxonomy for a single gap finding surfaced by `/qa` (#937).
134
+ *
135
+ * Replaces the lossy prose-scrape gap channel (`parseListSection` against
136
+ * `**Issues:**`/`**Gaps**` headers, which misses AC-table NOT_MET rows, §6d
137
+ * Adversarial Re-Read findings, and §5 Risk Assessment). `evidence` is
138
+ * required so a finding can't be speculative — see #608's 0%-action-rate
139
+ * result for open-ended "what might we be missing" findings.
140
+ */
141
+ export const GapCategorySchema = z.enum([
142
+ "requirement_gap",
143
+ "dependency_gap",
144
+ "test_gap",
145
+ "repository_gap",
146
+ "risk_gap",
147
+ "execution_gap",
148
+ ]);
149
+ export const GapActionSchema = z.enum([
150
+ "fix_now",
151
+ "document",
152
+ "pause_for_human",
153
+ ]);
154
+ export const GapFindingSchema = z.object({
155
+ category: GapCategorySchema,
156
+ /** Concrete observation grounding the finding — never speculation. */
157
+ evidence: z.string().min(1),
158
+ description: z.string().min(1),
159
+ recommendedAction: GapActionSchema,
160
+ /** ACs this finding relates to, e.g. ["AC-3"]. */
161
+ affectedAcs: z.array(z.string()).optional(),
162
+ /** True when the finding overlaps one of the issue's Non-Goals. */
163
+ nonGoal: z.boolean().optional(),
164
+ });
132
165
  /**
133
166
  * Condensed QA verdict summary for structured log output (#434).
134
167
  *
@@ -145,6 +178,13 @@ export const QaSummarySchema = z.object({
145
178
  gaps: z.array(z.string()),
146
179
  /** List of improvement suggestions from QA */
147
180
  suggestions: z.array(z.string()),
181
+ /**
182
+ * Structured gap findings parsed from the `SEQUANT_QA_GAPS` marker (#937).
183
+ * Present only when the marker was found and validated; `gaps` above
184
+ * always carries the union of marker + prose descriptions (dedupe'd) so
185
+ * marker-unaware consumers never regress.
186
+ */
187
+ findings: z.array(GapFindingSchema).optional(),
148
188
  });
149
189
  /**
150
190
  * Log entry for a single phase execution
@@ -16,6 +16,7 @@ import type { LockFile } from "../locks/index.js";
16
16
  import { type ChainResumePlan } from "./chain-resume.js";
17
17
  import { WorkflowEventEmitter } from "./event-emitter.js";
18
18
  import type { SequantSettings } from "../settings.js";
19
+ import { type PhasePolicy } from "./config-resolver.js";
19
20
  /**
20
21
  * Build the stack-manifest line emitted into PR bodies under --stacked.
21
22
  *
@@ -172,6 +173,23 @@ export interface RunResult {
172
173
  */
173
174
  wallClockDurationSeconds: number;
174
175
  }
176
+ /**
177
+ * Merge per-phase `resolvedModel` from execution results into the static
178
+ * phasePolicies map before it is written to metrics (#975 AC-4).
179
+ *
180
+ * `config.phasePolicies` carries the model alias resolved at config time (e.g.
181
+ * `"sonnet"`). The concrete model ID only becomes known after execution, from
182
+ * the driver's `modelUsage` map. This function enriches each phase entry with
183
+ * that runtime-observed value so benchmark comparisons across roster changes
184
+ * (#916/#944) see the actual model dispatched, not just the alias.
185
+ *
186
+ * `requestedModel` (the pre-resolution role string, e.g. `"role:fast"`) flows
187
+ * through from `config.phasePolicies` unchanged — it is already set by
188
+ * `resolvePhasePolicies` for role references and absent for raw strings.
189
+ *
190
+ * @internal Exported for testing only.
191
+ */
192
+ export declare function enrichPhasePoliciesFromResults(phasePolicies: Record<string, PhasePolicy> | undefined, results: IssueResult[]): Record<string, PhasePolicy> | undefined;
175
193
  /**
176
194
  * CLI-free workflow execution engine.
177
195
  *
@@ -82,7 +82,7 @@ import { MetricsWriter } from "./metrics-writer.js";
82
82
  import { WorkflowEventEmitter } from "./event-emitter.js";
83
83
  import { determineOutcome } from "./metrics-schema.js";
84
84
  import { getTokenUsageForRun } from "./token-utils.js";
85
- import { resolveRunOptions, buildExecutionConfig } from "./config-resolver.js";
85
+ import { resolveRunOptions, buildExecutionConfig, } from "./config-resolver.js";
86
86
  import { pipelineHasFailed } from "./status-derivation.js";
87
87
  /**
88
88
  * Build the stack-manifest line emitted into PR bodies under --stacked.
@@ -96,6 +96,40 @@ export function buildStackManifest(issueNumbers, currentIndex) {
96
96
  const parts = issueNumbers.map((n, i) => i === currentIndex ? `#${n} (this)` : `#${n}`);
97
97
  return `Part of stack: ${parts.join(" → ")}`;
98
98
  }
99
+ // ── Metrics helpers ─────────────────────────────────────────────────────────
100
+ /**
101
+ * Merge per-phase `resolvedModel` from execution results into the static
102
+ * phasePolicies map before it is written to metrics (#975 AC-4).
103
+ *
104
+ * `config.phasePolicies` carries the model alias resolved at config time (e.g.
105
+ * `"sonnet"`). The concrete model ID only becomes known after execution, from
106
+ * the driver's `modelUsage` map. This function enriches each phase entry with
107
+ * that runtime-observed value so benchmark comparisons across roster changes
108
+ * (#916/#944) see the actual model dispatched, not just the alias.
109
+ *
110
+ * `requestedModel` (the pre-resolution role string, e.g. `"role:fast"`) flows
111
+ * through from `config.phasePolicies` unchanged — it is already set by
112
+ * `resolvePhasePolicies` for role references and absent for raw strings.
113
+ *
114
+ * @internal Exported for testing only.
115
+ */
116
+ export function enrichPhasePoliciesFromResults(phasePolicies, results) {
117
+ const resolvedByPhase = new Map();
118
+ for (const result of results) {
119
+ for (const pr of result.phaseResults) {
120
+ if (pr.resolvedModel) {
121
+ resolvedByPhase.set(pr.phase, pr.resolvedModel);
122
+ }
123
+ }
124
+ }
125
+ if (resolvedByPhase.size === 0)
126
+ return phasePolicies;
127
+ const enriched = { ...(phasePolicies ?? {}) };
128
+ for (const [phase, resolvedModel] of resolvedByPhase.entries()) {
129
+ enriched[phase] = { ...enriched[phase], resolvedModel };
130
+ }
131
+ return enriched;
132
+ }
99
133
  // ── Orchestrator ────────────────────────────────────────────────────────────
100
134
  /**
101
135
  * CLI-free workflow execution engine.
@@ -1239,7 +1273,9 @@ export class RunOrchestrator {
1239
1273
  flags: cliFlags,
1240
1274
  failureCategory,
1241
1275
  // #914: resolved per-phase model/effort, when any phase had one.
1242
- phasePolicies: config.phasePolicies,
1276
+ // #975: enriched with resolvedModel from execution (modelUsage) and
1277
+ // requestedModel already flows through from resolvePhasePolicies.
1278
+ phasePolicies: enrichPhasePoliciesFromResults(config.phasePolicies, results),
1243
1279
  // #915: escalated tiers, when any phase execution escalated.
1244
1280
  effortEscalations,
1245
1281
  metrics: {
@@ -58,7 +58,7 @@ export interface ReconcileOptions {
58
58
  export interface ReconcileResult {
59
59
  /** Whether reconciliation was successful */
60
60
  success: boolean;
61
- /** Issues advanced to `merged` (from `ready_for_merge`, `in_progress`, or `waiting_for_qa_gate`) */
61
+ /** Issues advanced to `merged` (from `ready_for_merge`, `in_progress`, `waiting_for_qa_gate`, `waiting_for_human_merge`, or `awaiting_verification`) */
62
62
  advanced: number[];
63
63
  /** Issues checked but not yet merged (status unchanged) */
64
64
  stillPending: number[];
@@ -68,9 +68,9 @@ export interface ReconcileResult {
68
68
  /**
69
69
  * Lightweight state reconciliation at run start
70
70
  *
71
- * Checks issues in `ready_for_merge`, `in_progress`, or `waiting_for_qa_gate`
72
- * state and advances them to `merged` if their PRs are merged or their branches
73
- * are in main.
71
+ * Checks issues in `ready_for_merge`, `in_progress`, `waiting_for_qa_gate`,
72
+ * `waiting_for_human_merge`, or `awaiting_verification` state and advances
73
+ * them to `merged` if their PRs are merged or their branches are in main.
74
74
  *
75
75
  * Including `in_progress` covers the case where a PR was merged outside
76
76
  * this sequant session (separate process, `gh pr merge`, web UI) — without
@@ -188,9 +188,9 @@ export async function cleanupStaleEntries(options = {}) {
188
188
  /**
189
189
  * Lightweight state reconciliation at run start
190
190
  *
191
- * Checks issues in `ready_for_merge`, `in_progress`, or `waiting_for_qa_gate`
192
- * state and advances them to `merged` if their PRs are merged or their branches
193
- * are in main.
191
+ * Checks issues in `ready_for_merge`, `in_progress`, `waiting_for_qa_gate`,
192
+ * `waiting_for_human_merge`, or `awaiting_verification` state and advances
193
+ * them to `merged` if their PRs are merged or their branches are in main.
194
194
  *
195
195
  * Including `in_progress` covers the case where a PR was merged outside
196
196
  * this sequant session (separate process, `gh pr merge`, web UI) — without
@@ -227,14 +227,18 @@ export async function reconcileStateAtStartup(options = {}) {
227
227
  // waiting_for_human_merge covers #817's `--ready-gate` terminal: a gated
228
228
  // issue never reaches ready_for_merge, so without it a gated issue whose PR
229
229
  // a human then merged stayed here forever and never advanced to merged
230
- // (#837). Note this list is deliberately WIDER than `isCompletedIssueStatus`
230
+ // (#837). awaiting_verification covers the same shape for #972: the issue
231
+ // has an open PR pending human verification, and the human may verify and
232
+ // merge it directly instead of re-running qa first.
233
+ // Note this list is deliberately WIDER than `isCompletedIssueStatus`
231
234
  // — it asks "might this have a merged PR?", not "is this done?", which is
232
235
  // why in_progress belongs here but not there.
233
236
  for (const [issueNumStr, issueState] of Object.entries(state.issues)) {
234
237
  if (issueState.status !== "ready_for_merge" &&
235
238
  issueState.status !== "in_progress" &&
236
239
  issueState.status !== "waiting_for_qa_gate" &&
237
- issueState.status !== "waiting_for_human_merge") {
240
+ issueState.status !== "waiting_for_human_merge" &&
241
+ issueState.status !== "awaiting_verification") {
238
242
  continue;
239
243
  }
240
244
  const issueNum = parseInt(issueNumStr, 10);
@@ -47,6 +47,7 @@ export declare const IssueStatusSchema: z.ZodEnum<{
47
47
  not_started: "not_started";
48
48
  waiting_for_qa_gate: "waiting_for_qa_gate";
49
49
  waiting_for_human_merge: "waiting_for_human_merge";
50
+ awaiting_verification: "awaiting_verification";
50
51
  ready_for_merge: "ready_for_merge";
51
52
  blocked: "blocked";
52
53
  abandoned: "abandoned";
@@ -72,6 +73,8 @@ export declare const PhaseMarkerSchema: z.ZodObject<{
72
73
  pr: z.ZodOptional<z.ZodNumber>;
73
74
  error: z.ZodOptional<z.ZodString>;
74
75
  commitSHA: z.ZodOptional<z.ZodString>;
76
+ requestedModel: z.ZodOptional<z.ZodString>;
77
+ resolvedModel: z.ZodOptional<z.ZodString>;
75
78
  }, z.core.$strip>;
76
79
  export type PhaseMarker = z.infer<typeof PhaseMarkerSchema>;
77
80
  /**
@@ -149,6 +152,7 @@ export declare const AcceptanceCriterionSchema: z.ZodObject<{
149
152
  }>;
150
153
  verifiedAt: z.ZodOptional<z.ZodString>;
151
154
  notes: z.ZodOptional<z.ZodString>;
155
+ evidence: z.ZodOptional<z.ZodString>;
152
156
  }, z.core.$strip>;
153
157
  export type AcceptanceCriterion = z.infer<typeof AcceptanceCriterionSchema>;
154
158
  /**
@@ -172,6 +176,7 @@ export declare const AcceptanceCriteriaSchema: z.ZodObject<{
172
176
  }>;
173
177
  verifiedAt: z.ZodOptional<z.ZodString>;
174
178
  notes: z.ZodOptional<z.ZodString>;
179
+ evidence: z.ZodOptional<z.ZodString>;
175
180
  }, z.core.$strip>>;
176
181
  extractedAt: z.ZodString;
177
182
  summary: z.ZodObject<{
@@ -233,6 +238,7 @@ export declare const IssueStateSchema: z.ZodObject<{
233
238
  not_started: "not_started";
234
239
  waiting_for_qa_gate: "waiting_for_qa_gate";
235
240
  waiting_for_human_merge: "waiting_for_human_merge";
241
+ awaiting_verification: "awaiting_verification";
236
242
  ready_for_merge: "ready_for_merge";
237
243
  blocked: "blocked";
238
244
  abandoned: "abandoned";
@@ -281,6 +287,7 @@ export declare const IssueStateSchema: z.ZodObject<{
281
287
  }>;
282
288
  verifiedAt: z.ZodOptional<z.ZodString>;
283
289
  notes: z.ZodOptional<z.ZodString>;
290
+ evidence: z.ZodOptional<z.ZodString>;
284
291
  }, z.core.$strip>>;
285
292
  extractedAt: z.ZodString;
286
293
  summary: z.ZodObject<{
@@ -382,6 +389,7 @@ export declare const WorkflowStateSchema: z.ZodObject<{
382
389
  not_started: "not_started";
383
390
  waiting_for_qa_gate: "waiting_for_qa_gate";
384
391
  waiting_for_human_merge: "waiting_for_human_merge";
392
+ awaiting_verification: "awaiting_verification";
385
393
  ready_for_merge: "ready_for_merge";
386
394
  blocked: "blocked";
387
395
  abandoned: "abandoned";
@@ -430,6 +438,7 @@ export declare const WorkflowStateSchema: z.ZodObject<{
430
438
  }>;
431
439
  verifiedAt: z.ZodOptional<z.ZodString>;
432
440
  notes: z.ZodOptional<z.ZodString>;
441
+ evidence: z.ZodOptional<z.ZodString>;
433
442
  }, z.core.$strip>>;
434
443
  extractedAt: z.ZodString;
435
444
  summary: z.ZodObject<{
@@ -538,7 +547,7 @@ export declare function createPhaseState(status?: PhaseStatus): PhaseState;
538
547
  /**
539
548
  * Create acceptance criterion
540
549
  */
541
- export declare function createAcceptanceCriterion(id: string, description: string, verificationMethod?: ACVerificationMethod): AcceptanceCriterion;
550
+ export declare function createAcceptanceCriterion(id: string, description: string, verificationMethod?: ACVerificationMethod, evidence?: string): AcceptanceCriterion;
542
551
  /**
543
552
  * Create acceptance criteria tracking structure
544
553
  */
@@ -47,6 +47,7 @@ export const IssueStatusSchema = z.enum([
47
47
  "in_progress", // Actively being worked on
48
48
  "waiting_for_qa_gate", // QA completed, waiting for gate approval in chain mode
49
49
  "waiting_for_human_merge", // `sequant ready` (#683) finished its A+ gate; awaiting human merge decision (never auto-merges)
50
+ "awaiting_verification", // QA returned NEEDS_VERIFICATION; human must execute ACs before QA re-run (#972)
50
51
  "ready_for_merge", // All phases passed, PR ready for review
51
52
  "merged", // PR merged, work complete
52
53
  "blocked", // Waiting on external input or dependency
@@ -72,6 +73,10 @@ export const PhaseMarkerSchema = z.object({
72
73
  error: z.string().optional(),
73
74
  /** Git HEAD SHA at time of phase completion (used for incremental QA) */
74
75
  commitSHA: z.string().optional(),
76
+ /** Role string or raw model string as configured for this phase (#975). */
77
+ requestedModel: z.string().optional(),
78
+ /** Concrete model ID from `modelUsage` after phase execution (#975). */
79
+ resolvedModel: z.string().optional(),
75
80
  });
76
81
  /**
77
82
  * Individual phase state within an issue
@@ -149,6 +154,12 @@ export const AcceptanceCriterionSchema = z.object({
149
154
  verifiedAt: z.string().datetime().optional(),
150
155
  /** Additional notes about verification */
151
156
  notes: z.string().optional(),
157
+ /**
158
+ * Explicit `Evidence:` clause declared on the AC line (#938). When present,
159
+ * this is what verificationMethod was resolved from instead of keyword
160
+ * inference — see `resolveVerificationMethod` in ac-parser.ts.
161
+ */
162
+ evidence: z.string().optional(),
152
163
  });
153
164
  /**
154
165
  * Acceptance criteria tracking for an issue
@@ -369,12 +380,13 @@ export function createPhaseState(status = "pending") {
369
380
  /**
370
381
  * Create acceptance criterion
371
382
  */
372
- export function createAcceptanceCriterion(id, description, verificationMethod = "manual") {
383
+ export function createAcceptanceCriterion(id, description, verificationMethod = "manual", evidence) {
373
384
  return {
374
385
  id,
375
386
  description,
376
387
  verificationMethod,
377
388
  status: "pending",
389
+ ...(evidence !== undefined ? { evidence } : {}),
378
390
  };
379
391
  }
380
392
  /**
@@ -83,6 +83,15 @@ export interface ExecutionConfig {
83
83
  dryRun: boolean;
84
84
  /** Enable MCP servers in headless mode (true by default, false if --no-mcp flag used) */
85
85
  mcp: boolean;
86
+ /**
87
+ * Claude Desktop MCP server names explicitly opted in via
88
+ * `settings.run.mcpAllowlist` (#936). Both `ExecutionConfig` producers
89
+ * (`buildExecutionConfig`, `ready-gate.ts`'s `buildPhaseConfig`) read
90
+ * this from settings the same way, so it cannot drift between them the
91
+ * way #833's `phaseTimeout` once did. `phase-executor.ts` forwards it
92
+ * verbatim onto `AgentExecutionConfig.mcpAllowlist`.
93
+ */
94
+ mcpAllowlist?: string[];
86
95
  /**
87
96
  * Enable automatic retry with MCP fallback.
88
97
  * When true (default), failed phases are retried with MCP disabled.
@@ -278,6 +287,12 @@ export interface PhaseResult {
278
287
  base: string;
279
288
  escalated: string;
280
289
  };
290
+ /**
291
+ * Concrete model ID from the SDK `modelUsage` map for this phase execution
292
+ * (#975). First key of `modelUsage` — records the actual model dispatched,
293
+ * distinct from the configured alias. Used to populate metrics `phasePolicies`.
294
+ */
295
+ resolvedModel?: string;
281
296
  }
282
297
  /**
283
298
  * Result of executing all phases for an issue
@@ -705,4 +720,9 @@ export interface IssueExecutionContext {
705
720
  * around verbose Claude streaming.
706
721
  */
707
722
  phasePauseHandle?: PhasePauseHandle;
723
+ /**
724
+ * @internal Test seam for the #964 qa-verdict comment post. Defaults to a
725
+ * real `GitHubProvider().postComment` when unset.
726
+ */
727
+ postComment?: (issueNumber: number, body: string) => Promise<void>;
708
728
  }
@@ -390,6 +390,12 @@ const runToolInputSchema = {
390
390
  .boolean()
391
391
  .optional()
392
392
  .describe("Enable auto-retry on QA failure"),
393
+ force: z
394
+ .boolean()
395
+ .optional()
396
+ .describe("Re-run even if the issue is already in a completed state such as " +
397
+ "ready_for_merge (mirrors CLI --force). Not needed after a " +
398
+ "NEEDS_VERIFICATION verdict: awaiting_verification re-runs freely."),
393
399
  agent: z
394
400
  .string()
395
401
  .optional()
@@ -410,7 +416,7 @@ export function registerRunTool(server) {
410
416
  openWorldHint: true,
411
417
  },
412
418
  inputSchema: runToolInputSchema,
413
- }, (async ({ issues, phases, qualityLoop, agent, }, extra) => {
419
+ }, (async ({ issues, phases, qualityLoop, force, agent, }, extra) => {
414
420
  if (!issues || issues.length === 0) {
415
421
  return {
416
422
  content: [
@@ -435,6 +441,9 @@ export function registerRunTool(server) {
435
441
  if (qualityLoop) {
436
442
  args.push("--quality-loop");
437
443
  }
444
+ if (force) {
445
+ args.push("--force");
446
+ }
438
447
  if (agent) {
439
448
  args.push("--agent", agent);
440
449
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "sequant",
3
- "version": "2.11.0",
4
- "description": "AI coding agent orchestrator — resolve GitHub issues end-to-end with isolated git worktrees, quality gates, and an MCP server. Works with Claude Code or Aider.",
3
+ "version": "2.13.0",
4
+ "description": "AI coding agent orchestrator that proves its work — GitHub issues to merge-ready PRs with every acceptance criterion verified, isolated git worktrees, quality gates, an MCP server, and a human merge gate. Works with Claude Code or Aider.",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "sequant": "dist/bin/cli.js"
@@ -31,6 +31,7 @@
31
31
  "typecheck:scripts": "tsc --noEmit -p tsconfig.scripts.json",
32
32
  "lint:skill-calls": "npx tsx scripts/lint-skill-calls.ts",
33
33
  "lint:skill-gates": "npx tsx scripts/lint-skill-gates.ts",
34
+ "lint:constitution-dod": "npx tsx scripts/check-constitution-dod.ts",
34
35
  "lint:skill-sync": "npx tsx scripts/check-skill-sync.ts",
35
36
  "prepare:marketplace": "npx tsx scripts/prepare-marketplace.ts",
36
37
  "validate:marketplace": "npx tsx scripts/prepare-marketplace.ts --validate-only",
@@ -91,17 +92,17 @@
91
92
  }
92
93
  },
93
94
  "dependencies": {
94
- "@anthropic-ai/claude-agent-sdk": "^0.3.142",
95
+ "@anthropic-ai/claude-agent-sdk": "^0.3.242",
95
96
  "@hono/node-server": "^2.0.0",
96
97
  "boxen": "^8.0.1",
97
- "chalk": "^5.3.0",
98
+ "chalk": "^6.0.0",
98
99
  "chokidar": "^5.0.0",
99
100
  "cli-table3": "^0.6.5",
100
101
  "commander": "^14.0.3",
101
102
  "diff": "^9.0.0",
102
- "hono": "^4.12.1",
103
+ "hono": "^4.13.4",
103
104
  "ink": "^7.0.1",
104
- "inquirer": "^14.0.1",
105
+ "inquirer": "^14.1.0",
105
106
  "log-update": "^7.0.1",
106
107
  "open": "^11.0.0",
107
108
  "ora": "^9.3.0",
@@ -114,16 +115,16 @@
114
115
  "devDependencies": {
115
116
  "@eslint/js": "^10.0.1",
116
117
  "@types/inquirer": "^9.0.7",
117
- "@types/node": "^26.0.1",
118
+ "@types/node": "^26.3.0",
118
119
  "@types/react": "^19.2.14",
119
- "@typescript-eslint/eslint-plugin": "^8.58.0",
120
- "@typescript-eslint/parser": "^8.58.0",
121
- "eslint": "^10.1.0",
120
+ "@typescript-eslint/eslint-plugin": "^8.68.0",
121
+ "@typescript-eslint/parser": "^8.68.0",
122
+ "eslint": "^10.9.1",
122
123
  "globals": "^17.0.0",
123
124
  "ink-testing-library": "^4.0.0",
124
125
  "tsx": "^4.19.2",
125
126
  "typescript": "^6.0.2",
126
- "typescript-eslint": "^8.58.0",
127
- "vitest": "^4.1.0"
127
+ "typescript-eslint": "^8.68.0",
128
+ "vitest": "^4.1.11"
128
129
  }
129
130
  }