sequant 2.10.0 → 2.12.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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +19 -2
- package/dist/bin/cli.js +47 -2
- package/dist/marketplace/external_plugins/sequant/.claude-plugin/plugin.json +1 -1
- package/dist/marketplace/external_plugins/sequant/.mcp.json +1 -1
- package/dist/marketplace/external_plugins/sequant/hooks/pre-tool.sh +331 -12
- package/dist/marketplace/external_plugins/sequant/skills/_shared/references/subagent-types.md +7 -18
- package/dist/marketplace/external_plugins/sequant/skills/assess/SKILL.md +5 -1
- package/dist/marketplace/external_plugins/sequant/skills/exec/SKILL.md +62 -8
- package/dist/marketplace/external_plugins/sequant/skills/fullsolve/SKILL.md +187 -28
- package/dist/marketplace/external_plugins/sequant/skills/loop/SKILL.md +127 -23
- package/dist/marketplace/external_plugins/sequant/skills/merger/SKILL.md +130 -13
- package/dist/marketplace/external_plugins/sequant/skills/qa/SKILL.md +306 -8
- package/dist/marketplace/external_plugins/sequant/skills/release/SKILL.md +79 -0
- package/dist/marketplace/external_plugins/sequant/skills/spec/SKILL.md +40 -20
- package/dist/marketplace/external_plugins/sequant/skills/spec/references/recommended-workflow.md +14 -1
- package/dist/marketplace/external_plugins/sequant/skills/test/SKILL.md +1 -1
- package/dist/marketplace/external_plugins/sequant/skills/testgen/SKILL.md +23 -6
- package/dist/src/commands/doctor.js +20 -18
- package/dist/src/commands/locks.d.ts +20 -1
- package/dist/src/commands/locks.js +206 -4
- package/dist/src/commands/ready.d.ts +6 -0
- package/dist/src/commands/ready.js +19 -1
- package/dist/src/commands/run-display.js +1 -0
- package/dist/src/commands/worktree.d.ts +31 -0
- package/dist/src/commands/worktree.js +95 -0
- package/dist/src/lib/ac-linter.js +26 -0
- package/dist/src/lib/ac-parser.d.ts +40 -0
- package/dist/src/lib/ac-parser.js +202 -16
- package/dist/src/lib/cli-flags.d.ts +23 -0
- package/dist/src/lib/cli-flags.js +43 -0
- package/dist/src/lib/cli-ui/run-renderer-types.d.ts +2 -0
- package/dist/src/lib/cli-ui/run-renderer.js +7 -1
- package/dist/src/lib/locks/checkout-lock.d.ts +193 -0
- package/dist/src/lib/locks/checkout-lock.js +389 -0
- package/dist/src/lib/locks/index.d.ts +6 -3
- package/dist/src/lib/locks/index.js +4 -2
- package/dist/src/lib/locks/lock-manager.d.ts +81 -1
- package/dist/src/lib/locks/lock-manager.js +230 -5
- package/dist/src/lib/locks/types.d.ts +72 -0
- package/dist/src/lib/locks/types.js +28 -0
- package/dist/src/lib/markdown-fence.d.ts +24 -0
- package/dist/src/lib/markdown-fence.js +51 -0
- package/dist/src/lib/mcp-config.d.ts +24 -0
- package/dist/src/lib/mcp-config.js +51 -0
- package/dist/src/lib/scope/analyzer.d.ts +4 -0
- package/dist/src/lib/scope/analyzer.js +7 -1
- package/dist/src/lib/settings.d.ts +111 -1
- package/dist/src/lib/settings.js +59 -0
- package/dist/src/lib/system.d.ts +7 -3
- package/dist/src/lib/system.js +7 -3
- package/dist/src/lib/test-tautology-detector.d.ts +4 -3
- package/dist/src/lib/test-tautology-detector.js +147 -40
- package/dist/src/lib/workflow/batch-executor.d.ts +20 -1
- package/dist/src/lib/workflow/batch-executor.js +154 -23
- package/dist/src/lib/workflow/config-resolver.d.ts +25 -0
- package/dist/src/lib/workflow/config-resolver.js +90 -0
- package/dist/src/lib/workflow/drivers/agent-driver.d.ts +22 -0
- package/dist/src/lib/workflow/drivers/claude-code.js +14 -3
- package/dist/src/lib/workflow/effort-escalation.d.ts +73 -0
- package/dist/src/lib/workflow/effort-escalation.js +82 -0
- package/dist/src/lib/workflow/error-classifier.d.ts +4 -1
- package/dist/src/lib/workflow/error-classifier.js +4 -0
- package/dist/src/lib/workflow/log-writer.d.ts +10 -1
- package/dist/src/lib/workflow/log-writer.js +20 -0
- package/dist/src/lib/workflow/metrics-schema.d.ts +49 -6
- package/dist/src/lib/workflow/metrics-schema.js +33 -0
- package/dist/src/lib/workflow/metrics-writer.d.ts +11 -0
- package/dist/src/lib/workflow/mutation-marker.d.ts +86 -0
- package/dist/src/lib/workflow/mutation-marker.js +97 -0
- package/dist/src/lib/workflow/phase-detection.d.ts +12 -0
- package/dist/src/lib/workflow/phase-detection.js +5 -1
- package/dist/src/lib/workflow/phase-executor.d.ts +17 -0
- package/dist/src/lib/workflow/phase-executor.js +60 -4
- package/dist/src/lib/workflow/qa-gaps-marker.d.ts +38 -0
- package/dist/src/lib/workflow/qa-gaps-marker.js +66 -0
- package/dist/src/lib/workflow/ready-gate.d.ts +53 -1
- package/dist/src/lib/workflow/ready-gate.js +105 -14
- package/dist/src/lib/workflow/run-log-schema.d.ts +175 -0
- package/dist/src/lib/workflow/run-log-schema.js +71 -1
- package/dist/src/lib/workflow/run-orchestrator.js +27 -0
- package/dist/src/lib/workflow/spec-recommendation.d.ts +71 -0
- package/dist/src/lib/workflow/spec-recommendation.js +142 -0
- package/dist/src/lib/workflow/state-schema.d.ts +5 -1
- package/dist/src/lib/workflow/state-schema.js +8 -1
- package/dist/src/lib/workflow/types.d.ts +78 -0
- package/dist/src/lib/workflow/worktree-manager.d.ts +8 -1
- package/dist/src/lib/workflow/worktree-manager.js +9 -1
- package/dist/src/lib/workflow/worktree-resolver.d.ts +73 -0
- package/dist/src/lib/workflow/worktree-resolver.js +126 -0
- package/package.json +4 -3
- package/templates/hooks/pre-tool.sh +331 -12
- package/templates/scripts/cleanup-worktree.sh +36 -15
- package/templates/scripts/new-feature.sh +25 -19
- package/templates/skills/_shared/references/subagent-types.md +7 -18
- package/templates/skills/assess/SKILL.md +5 -1
- package/templates/skills/exec/SKILL.md +62 -8
- package/templates/skills/fullsolve/SKILL.md +187 -28
- package/templates/skills/loop/SKILL.md +127 -23
- package/templates/skills/merger/SKILL.md +130 -13
- package/templates/skills/qa/SKILL.md +306 -8
- package/templates/skills/release/SKILL.md +79 -0
- package/templates/skills/spec/SKILL.md +40 -20
- package/templates/skills/spec/references/recommended-workflow.md +14 -1
- package/templates/skills/test/SKILL.md +1 -1
- package/templates/skills/testgen/SKILL.md +23 -6
- package/templates/agents/sequant-explorer.md +0 -24
|
@@ -57,6 +57,23 @@ export interface AgentSettings {
|
|
|
57
57
|
*/
|
|
58
58
|
isolateParallel: boolean;
|
|
59
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* The Agent SDK's closed reasoning-effort enum (#914). Single source of
|
|
62
|
+
* truth — reused by `PhasePolicySchema`'s zod validation below and by
|
|
63
|
+
* `cli-flags.ts:parsePhaseSpecFlag` for the `--efforts` CLI boundary, so the
|
|
64
|
+
* two validation points cannot drift apart on which values are accepted.
|
|
65
|
+
*/
|
|
66
|
+
export declare const EFFORT_LEVELS: readonly ["low", "medium", "high", "xhigh", "max"];
|
|
67
|
+
/**
|
|
68
|
+
* A single phase's `model`/`effort` override for the claude-code driver
|
|
69
|
+
* (#914). See `RunSettings.phases`.
|
|
70
|
+
*/
|
|
71
|
+
export interface PhasePolicy {
|
|
72
|
+
/** Model alias/ID, passed through unvalidated to the Agent SDK. */
|
|
73
|
+
model?: string;
|
|
74
|
+
/** Reasoning effort — validated against the SDK's closed enum. */
|
|
75
|
+
effort?: (typeof EFFORT_LEVELS)[number];
|
|
76
|
+
}
|
|
60
77
|
/**
|
|
61
78
|
* Aider-specific settings for the aider agent driver.
|
|
62
79
|
*/
|
|
@@ -100,11 +117,35 @@ export interface RunSettings {
|
|
|
100
117
|
defaultBase?: string;
|
|
101
118
|
/**
|
|
102
119
|
* Enable MCP servers in headless mode.
|
|
103
|
-
* When true,
|
|
120
|
+
* When true, injects the sequant MCP server plus any servers declared in
|
|
121
|
+
* the project's own `.mcp.json` — never a passthrough of the user's Claude
|
|
122
|
+
* Desktop config, which is a different trust domain and may carry literal
|
|
123
|
+
* secrets (#936), unless a server is explicitly named in `mcpAllowlist`
|
|
124
|
+
* below.
|
|
104
125
|
* When false or --no-mcp flag is used, MCPs are disabled.
|
|
105
126
|
* Default: true
|
|
106
127
|
*/
|
|
107
128
|
mcp: boolean;
|
|
129
|
+
/**
|
|
130
|
+
* Explicit per-server opt-in to pass specific Claude Desktop MCP servers
|
|
131
|
+
* through to phase execution (#936).
|
|
132
|
+
*
|
|
133
|
+
* `mcp` above never reads Claude Desktop config by default — phase agents
|
|
134
|
+
* get the sequant server plus the project's own `.mcp.json` only. This is
|
|
135
|
+
* the deliberate escape hatch for a server that exists only in Claude
|
|
136
|
+
* Desktop config (e.g. never committed to git): list its exact
|
|
137
|
+
* `mcpServers` key here and phase agents additionally receive it. A name
|
|
138
|
+
* not present in the desktop config is silently ignored — this is a
|
|
139
|
+
* filter, not a requirement. Unset or empty (default): no desktop servers
|
|
140
|
+
* pass through, matching `mcp`'s secure-by-default behavior.
|
|
141
|
+
*
|
|
142
|
+
* ⚠️ Desktop Claude configs cannot use `${VAR}` references, so a server you
|
|
143
|
+
* allowlist here may carry a literal secret that reaches the phase
|
|
144
|
+
* process's argv (the SDK serializes `mcpServers` into `--mcp-config`).
|
|
145
|
+
* Only allowlist a server with no credential, or one whose credential you
|
|
146
|
+
* accept exposing to phase agents and to `ps`.
|
|
147
|
+
*/
|
|
148
|
+
mcpAllowlist?: string[];
|
|
108
149
|
/**
|
|
109
150
|
* Enable automatic retry with MCP fallback.
|
|
110
151
|
* When true (default), failed phases are retried with MCP disabled.
|
|
@@ -166,6 +207,30 @@ export interface RunSettings {
|
|
|
166
207
|
* Default: true.
|
|
167
208
|
*/
|
|
168
209
|
relay?: boolean;
|
|
210
|
+
/**
|
|
211
|
+
* Per-phase `model`/`effort` overrides for the claude-code driver (#914),
|
|
212
|
+
* keyed by phase name. Absent by default — zero behavior change until
|
|
213
|
+
* opted in. Overridable per-invocation with `--models`/`--efforts`
|
|
214
|
+
* (CLI > settings > absent, resolved by `resolvePhasePolicies` in
|
|
215
|
+
* `config-resolver.ts`).
|
|
216
|
+
*/
|
|
217
|
+
phases?: Record<string, PhasePolicy>;
|
|
218
|
+
/**
|
|
219
|
+
* Evidence-based effort escalation on quality-loop retries (#915). Default
|
|
220
|
+
* `false` — raising effort raises token spend, which is the user's call.
|
|
221
|
+
* Overridable per-invocation with `--escalate-effort` (CLI > settings >
|
|
222
|
+
* default). See `effort-escalation.ts` for the resolver.
|
|
223
|
+
*/
|
|
224
|
+
effortEscalation: boolean;
|
|
225
|
+
/**
|
|
226
|
+
* Whether `/fullsolve`'s Phase 5.3 merges the PR automatically once QA
|
|
227
|
+
* passes (#958). Default `false` — the workflow stops at PR creation +
|
|
228
|
+
* final summary, preserving the human merge gate kept by #817–#819.
|
|
229
|
+
* Overridable per-invocation with `--auto-merge`. Read directly by the
|
|
230
|
+
* `/fullsolve` skill prose (not by any runtime code path — `sequant run`
|
|
231
|
+
* never merges regardless of this setting).
|
|
232
|
+
*/
|
|
233
|
+
autoMerge: boolean;
|
|
169
234
|
}
|
|
170
235
|
/**
|
|
171
236
|
* Scope assessment threshold configuration
|
|
@@ -304,6 +369,25 @@ export declare const AgentSettingsSchema: z.ZodObject<{
|
|
|
304
369
|
}>>;
|
|
305
370
|
isolateParallel: z.ZodDefault<z.ZodBoolean>;
|
|
306
371
|
}, z.core.$strip>;
|
|
372
|
+
/**
|
|
373
|
+
* Zod schema for a single phase's model/effort override (#914).
|
|
374
|
+
*
|
|
375
|
+
* Model aliases/IDs pass through unvalidated — they churn independently of
|
|
376
|
+
* sequant releases, and the Agent SDK's `query()` call errors clearly on a
|
|
377
|
+
* bad one. Effort validates against the SDK's closed enum at settings-parse
|
|
378
|
+
* time since that set is stable and a typo here would otherwise silently
|
|
379
|
+
* fall through to the SDK default.
|
|
380
|
+
*/
|
|
381
|
+
export declare const PhasePolicySchema: z.ZodObject<{
|
|
382
|
+
model: z.ZodOptional<z.ZodString>;
|
|
383
|
+
effort: z.ZodOptional<z.ZodEnum<{
|
|
384
|
+
low: "low";
|
|
385
|
+
medium: "medium";
|
|
386
|
+
high: "high";
|
|
387
|
+
xhigh: "xhigh";
|
|
388
|
+
max: "max";
|
|
389
|
+
}>>;
|
|
390
|
+
}, z.core.$strip>;
|
|
307
391
|
/** Zod schema for RunSettings */
|
|
308
392
|
export declare const RunSettingsSchema: z.ZodObject<{
|
|
309
393
|
logJson: z.ZodDefault<z.ZodBoolean>;
|
|
@@ -322,6 +406,7 @@ export declare const RunSettingsSchema: z.ZodObject<{
|
|
|
322
406
|
}, z.core.$strip>>;
|
|
323
407
|
defaultBase: z.ZodOptional<z.ZodString>;
|
|
324
408
|
mcp: z.ZodDefault<z.ZodBoolean>;
|
|
409
|
+
mcpAllowlist: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
325
410
|
retry: z.ZodDefault<z.ZodBoolean>;
|
|
326
411
|
autoWaitMinutes: z.ZodDefault<z.ZodNumber>;
|
|
327
412
|
staleBranchThreshold: z.ZodDefault<z.ZodNumber>;
|
|
@@ -335,6 +420,18 @@ export declare const RunSettingsSchema: z.ZodObject<{
|
|
|
335
420
|
extraArgs: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
336
421
|
}, z.core.$strip>>;
|
|
337
422
|
relay: z.ZodDefault<z.ZodBoolean>;
|
|
423
|
+
phases: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
424
|
+
model: z.ZodOptional<z.ZodString>;
|
|
425
|
+
effort: z.ZodOptional<z.ZodEnum<{
|
|
426
|
+
low: "low";
|
|
427
|
+
medium: "medium";
|
|
428
|
+
high: "high";
|
|
429
|
+
xhigh: "xhigh";
|
|
430
|
+
max: "max";
|
|
431
|
+
}>>;
|
|
432
|
+
}, z.core.$strip>>>;
|
|
433
|
+
effortEscalation: z.ZodDefault<z.ZodBoolean>;
|
|
434
|
+
autoMerge: z.ZodDefault<z.ZodBoolean>;
|
|
338
435
|
}, z.core.$strip>;
|
|
339
436
|
/** Zod schema for ScopeThreshold (base — fields required, no defaults) */
|
|
340
437
|
export declare const ScopeThresholdSchema: z.ZodObject<{
|
|
@@ -415,6 +512,7 @@ export declare const SettingsSchema: z.ZodObject<{
|
|
|
415
512
|
}, z.core.$strip>>;
|
|
416
513
|
defaultBase: z.ZodOptional<z.ZodString>;
|
|
417
514
|
mcp: z.ZodDefault<z.ZodBoolean>;
|
|
515
|
+
mcpAllowlist: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
418
516
|
retry: z.ZodDefault<z.ZodBoolean>;
|
|
419
517
|
autoWaitMinutes: z.ZodDefault<z.ZodNumber>;
|
|
420
518
|
staleBranchThreshold: z.ZodDefault<z.ZodNumber>;
|
|
@@ -428,6 +526,18 @@ export declare const SettingsSchema: z.ZodObject<{
|
|
|
428
526
|
extraArgs: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
429
527
|
}, z.core.$strip>>;
|
|
430
528
|
relay: z.ZodDefault<z.ZodBoolean>;
|
|
529
|
+
phases: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
530
|
+
model: z.ZodOptional<z.ZodString>;
|
|
531
|
+
effort: z.ZodOptional<z.ZodEnum<{
|
|
532
|
+
low: "low";
|
|
533
|
+
medium: "medium";
|
|
534
|
+
high: "high";
|
|
535
|
+
xhigh: "xhigh";
|
|
536
|
+
max: "max";
|
|
537
|
+
}>>;
|
|
538
|
+
}, z.core.$strip>>>;
|
|
539
|
+
effortEscalation: z.ZodDefault<z.ZodBoolean>;
|
|
540
|
+
autoMerge: z.ZodDefault<z.ZodBoolean>;
|
|
431
541
|
}, z.core.$strip>>;
|
|
432
542
|
agents: z.ZodDefault<z.ZodObject<{
|
|
433
543
|
parallel: z.ZodDefault<z.ZodBoolean>;
|
package/dist/src/lib/settings.js
CHANGED
|
@@ -13,10 +13,18 @@
|
|
|
13
13
|
import { readFile, writeFile, fileExists, ensureDir } from "./fs.js";
|
|
14
14
|
import { dirname } from "path";
|
|
15
15
|
import { z } from "zod";
|
|
16
|
+
import { getPhaseNames } from "./workflow/phase-registry.js";
|
|
16
17
|
/** Path to project-level settings file */
|
|
17
18
|
export const SETTINGS_PATH = ".sequant/settings.json";
|
|
18
19
|
/** Current settings schema version */
|
|
19
20
|
export const SETTINGS_VERSION = "1.0";
|
|
21
|
+
/**
|
|
22
|
+
* The Agent SDK's closed reasoning-effort enum (#914). Single source of
|
|
23
|
+
* truth — reused by `PhasePolicySchema`'s zod validation below and by
|
|
24
|
+
* `cli-flags.ts:parsePhaseSpecFlag` for the `--efforts` CLI boundary, so the
|
|
25
|
+
* two validation points cannot drift apart on which values are accepted.
|
|
26
|
+
*/
|
|
27
|
+
export const EFFORT_LEVELS = ["low", "medium", "high", "xhigh", "max"];
|
|
20
28
|
// ─── Zod Schemas (AC-1, AC-5) ────────────────────────────────────────────────
|
|
21
29
|
/** Zod schema for RotationSettings */
|
|
22
30
|
export const RotationSettingsSchema = z.object({
|
|
@@ -36,6 +44,19 @@ export const AgentSettingsSchema = z.object({
|
|
|
36
44
|
model: z.enum(["haiku", "sonnet", "opus"]).default("haiku"),
|
|
37
45
|
isolateParallel: z.boolean().default(false),
|
|
38
46
|
});
|
|
47
|
+
/**
|
|
48
|
+
* Zod schema for a single phase's model/effort override (#914).
|
|
49
|
+
*
|
|
50
|
+
* Model aliases/IDs pass through unvalidated — they churn independently of
|
|
51
|
+
* sequant releases, and the Agent SDK's `query()` call errors clearly on a
|
|
52
|
+
* bad one. Effort validates against the SDK's closed enum at settings-parse
|
|
53
|
+
* time since that set is stable and a typo here would otherwise silently
|
|
54
|
+
* fall through to the SDK default.
|
|
55
|
+
*/
|
|
56
|
+
export const PhasePolicySchema = z.object({
|
|
57
|
+
model: z.string().optional(),
|
|
58
|
+
effort: z.enum(EFFORT_LEVELS).optional(),
|
|
59
|
+
});
|
|
39
60
|
/** Zod schema for RunSettings */
|
|
40
61
|
export const RunSettingsSchema = z.object({
|
|
41
62
|
logJson: z.boolean().default(true),
|
|
@@ -57,6 +78,7 @@ export const RunSettingsSchema = z.object({
|
|
|
57
78
|
rotation: RotationSettingsSchema.default(() => RotationSettingsSchema.parse({})),
|
|
58
79
|
defaultBase: z.string().optional(),
|
|
59
80
|
mcp: z.boolean().default(true),
|
|
81
|
+
mcpAllowlist: z.array(z.string()).optional(),
|
|
60
82
|
retry: z.boolean().default(true),
|
|
61
83
|
autoWaitMinutes: z.number().min(0).default(0),
|
|
62
84
|
staleBranchThreshold: z.number().default(5),
|
|
@@ -66,6 +88,29 @@ export const RunSettingsSchema = z.object({
|
|
|
66
88
|
agent: z.string().optional(),
|
|
67
89
|
aider: AiderSettingsSchema.optional(),
|
|
68
90
|
relay: z.boolean().default(true),
|
|
91
|
+
/**
|
|
92
|
+
* Per-phase `model`/`effort` overrides for the claude-code driver (#914).
|
|
93
|
+
* Absent by default — zero behavior change until opted in. Keyed by phase
|
|
94
|
+
* name (validated against `getPhaseNames()` via `KNOWN_KEYS["run.phases"]`
|
|
95
|
+
* as a non-fatal warning, not a schema-level rejection — a typo'd phase
|
|
96
|
+
* name here should not crash a run the way an invalid `effort` enum does).
|
|
97
|
+
*/
|
|
98
|
+
phases: z.record(z.string(), PhasePolicySchema).optional(),
|
|
99
|
+
/**
|
|
100
|
+
* Evidence-based effort escalation on quality-loop retries (#915). Default
|
|
101
|
+
* `false` — raising effort raises token spend, which is the user's call.
|
|
102
|
+
* When enabled, a retried phase execution (loop iteration ≥ 2, or a
|
|
103
|
+
* `sequant ready` QA-pass loop re-run) resolves one effort tier above its
|
|
104
|
+
* configured/inherited base for that execution only. See
|
|
105
|
+
* `effort-escalation.ts` for the resolver.
|
|
106
|
+
*/
|
|
107
|
+
effortEscalation: z.boolean().default(false),
|
|
108
|
+
/**
|
|
109
|
+
* Whether `/fullsolve`'s Phase 5.3 merges the PR automatically once QA
|
|
110
|
+
* passes (#958). Default `false` — preserves the human merge gate kept by
|
|
111
|
+
* #817–#819. Overridable per-invocation with `--auto-merge`.
|
|
112
|
+
*/
|
|
113
|
+
autoMerge: z.boolean().default(false),
|
|
69
114
|
});
|
|
70
115
|
/** Zod schema for ScopeThreshold (base — fields required, no defaults) */
|
|
71
116
|
export const ScopeThresholdSchema = z.object({
|
|
@@ -178,6 +223,7 @@ const KNOWN_KEYS = {
|
|
|
178
223
|
"rotation",
|
|
179
224
|
"defaultBase",
|
|
180
225
|
"mcp",
|
|
226
|
+
"mcpAllowlist",
|
|
181
227
|
"retry",
|
|
182
228
|
"staleBranchThreshold",
|
|
183
229
|
"resolvedIssueTTL",
|
|
@@ -186,7 +232,13 @@ const KNOWN_KEYS = {
|
|
|
186
232
|
"agent",
|
|
187
233
|
"aider",
|
|
188
234
|
"relay",
|
|
235
|
+
"phases",
|
|
189
236
|
]),
|
|
237
|
+
// #914: keyed by real phase name so a typo (`run.phases.exce`) warns
|
|
238
|
+
// instead of silently resolving to nothing. Computed from the registry
|
|
239
|
+
// rather than hardcoded so a new phase registration doesn't need a
|
|
240
|
+
// matching edit here.
|
|
241
|
+
"run.phases": new Set(getPhaseNames()),
|
|
190
242
|
agents: new Set(["parallel", "model", "isolateParallel"]),
|
|
191
243
|
scopeAssessment: new Set([
|
|
192
244
|
"enabled",
|
|
@@ -406,6 +458,8 @@ export const DEFAULT_SETTINGS = {
|
|
|
406
458
|
staleBranchThreshold: 5, // Block QA/test if feature is >5 commits behind main
|
|
407
459
|
resolvedIssueTTL: 7, // Auto-prune resolved issues after 7 days
|
|
408
460
|
relay: true, // Enable interactive relay (#383) by default
|
|
461
|
+
effortEscalation: false, // #915: off by default — raises token spend
|
|
462
|
+
autoMerge: false, // #958: off by default — preserves the human merge gate
|
|
409
463
|
},
|
|
410
464
|
agents: DEFAULT_AGENT_SETTINGS,
|
|
411
465
|
scopeAssessment: DEFAULT_SCOPE_ASSESSMENT_SETTINGS,
|
|
@@ -536,6 +590,10 @@ export function generateSettingsJsonc(settings) {
|
|
|
536
590
|
lines.push(` "smartTests": ${JSON.stringify(settings.run.smartTests)},`);
|
|
537
591
|
lines.push(` // Enable MCP servers in headless mode`);
|
|
538
592
|
lines.push(` "mcp": ${JSON.stringify(settings.run.mcp)},`);
|
|
593
|
+
if (settings.run.mcpAllowlist !== undefined) {
|
|
594
|
+
lines.push(` // Desktop MCP servers explicitly allowlisted for phases`);
|
|
595
|
+
lines.push(` "mcpAllowlist": ${JSON.stringify(settings.run.mcpAllowlist)},`);
|
|
596
|
+
}
|
|
539
597
|
lines.push(` // Enable automatic retry with MCP fallback`);
|
|
540
598
|
lines.push(` "retry": ${JSON.stringify(settings.run.retry)},`);
|
|
541
599
|
lines.push(` // Commits behind main before warning`);
|
|
@@ -653,6 +711,7 @@ Generated by \`sequant init\`. See defaults below.
|
|
|
653
711
|
| \`smartTests\` | boolean | \`true\` | Enable smart test detection |
|
|
654
712
|
| \`defaultBase\` | string | — | Default base branch for worktree creation |
|
|
655
713
|
| \`mcp\` | boolean | \`true\` | Enable MCP servers in headless mode |
|
|
714
|
+
| \`mcpAllowlist\` | string[] | — | Desktop MCP server names to pass through to phases despite \`mcp\`'s default exclusion (#936) |
|
|
656
715
|
| \`retry\` | boolean | \`true\` | Enable automatic retry with MCP fallback |
|
|
657
716
|
| \`staleBranchThreshold\` | number | \`5\` | Commits behind main before warning |
|
|
658
717
|
| \`resolvedIssueTTL\` | number | \`7\` | Days before resolved issues auto-prune (0=never, -1=immediate) |
|
package/dist/src/lib/system.d.ts
CHANGED
|
@@ -54,9 +54,13 @@ export interface McpServerConfig {
|
|
|
54
54
|
/**
|
|
55
55
|
* Get full MCP server configurations from Claude Desktop config
|
|
56
56
|
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
57
|
+
* For interactive `doctor` diagnostics only — reports what's configured for
|
|
58
|
+
* the human user. Never pass this to phase execution (#936): Claude Desktop
|
|
59
|
+
* is a different trust domain (interactive, human-supervised) than an
|
|
60
|
+
* autonomous phase agent, and desktop configs carry literal secrets since
|
|
61
|
+
* they cannot use `${VAR}` references. Phase agents use
|
|
62
|
+
* `getPhaseMcpServersConfig` from `mcp-config.ts` instead, which allowlists
|
|
63
|
+
* from the project's own `.mcp.json`.
|
|
60
64
|
*
|
|
61
65
|
* @returns MCP server configurations or undefined
|
|
62
66
|
*/
|
package/dist/src/lib/system.js
CHANGED
|
@@ -136,9 +136,13 @@ export function getConfiguredMcpServers() {
|
|
|
136
136
|
/**
|
|
137
137
|
* Get full MCP server configurations from Claude Desktop config
|
|
138
138
|
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
139
|
+
* For interactive `doctor` diagnostics only — reports what's configured for
|
|
140
|
+
* the human user. Never pass this to phase execution (#936): Claude Desktop
|
|
141
|
+
* is a different trust domain (interactive, human-supervised) than an
|
|
142
|
+
* autonomous phase agent, and desktop configs carry literal secrets since
|
|
143
|
+
* they cannot use `${VAR}` references. Phase agents use
|
|
144
|
+
* `getPhaseMcpServersConfig` from `mcp-config.ts` instead, which allowlists
|
|
145
|
+
* from the project's own `.mcp.json`.
|
|
142
146
|
*
|
|
143
147
|
* @returns MCP server configurations or undefined
|
|
144
148
|
*/
|
|
@@ -104,12 +104,13 @@ export declare function extractTestBlocks(content: string): Array<{
|
|
|
104
104
|
* when it references an imported production function, directly spawns the
|
|
105
105
|
* project's build output, or calls a helper that (transitively) does so.
|
|
106
106
|
*
|
|
107
|
-
* @param
|
|
108
|
-
*
|
|
107
|
+
* @param productionHandles Names of describe/module-scope helpers that reach
|
|
108
|
+
* production — by spawning the project's executable code or by calling an
|
|
109
|
+
* imported production function (see {@link collectProductionHandles}).
|
|
109
110
|
* @param buildOutputVars Variable names bound to a build-output path (see
|
|
110
111
|
* {@link collectBuildOutputVars}).
|
|
111
112
|
*/
|
|
112
|
-
export declare function testBlockCallsProductionCode(body: string, importedFunctions: ImportedFunction[],
|
|
113
|
+
export declare function testBlockCallsProductionCode(body: string, importedFunctions: ImportedFunction[], productionHandles?: string[], buildOutputVars?: string[]): boolean;
|
|
113
114
|
/**
|
|
114
115
|
* Check if a file opts out of tautology detection via pragma comment.
|
|
115
116
|
*
|
|
@@ -369,20 +369,85 @@ const SPAWN_PATTERN = /(?:\b(?:execFileSync|spawnSync|execSync|execFile)\s*\(|(?
|
|
|
369
369
|
*/
|
|
370
370
|
const BUILD_OUTPUT_PATTERN = /\bdist\//;
|
|
371
371
|
/**
|
|
372
|
-
*
|
|
372
|
+
* Non-compiled production code this project ships and executes: the hook
|
|
373
|
+
* scripts and the `scripts/` + `templates/scripts/` trees (#906).
|
|
374
|
+
*
|
|
375
|
+
* `dist/` alone was too narrow. `checkout-lock.integration.test.ts` drives the
|
|
376
|
+
* real `.claude/hooks/pre-tool.sh` as a subprocess — that hook IS the
|
|
377
|
+
* enforcement half of the feature under test — yet every block in the file
|
|
378
|
+
* read as tautological because the path is not under `dist/`.
|
|
379
|
+
*/
|
|
380
|
+
const PROJECT_SCRIPT_PATTERN = /\b(?:hooks\/[\w.-]+\.sh|scripts\/[\w./-]+)/;
|
|
381
|
+
/**
|
|
382
|
+
* Collect names of variables bound to a path into the project's own executable
|
|
383
|
+
* code, e.g.
|
|
373
384
|
* const cliPath = resolve(projectRoot, "dist/bin/cli.js");
|
|
374
|
-
*
|
|
375
|
-
*
|
|
385
|
+
* const HOOK = join(REPO_ROOT, ".claude/hooks/pre-tool.sh");
|
|
386
|
+
* captures `cliPath` / `HOOK`. Tests almost always spawn via such a handle
|
|
387
|
+
* rather than an inline string, so these names stand in for the literal path.
|
|
376
388
|
*
|
|
377
389
|
* The right-hand side is statement-bounded (`[^;]`) so a match cannot bleed
|
|
378
|
-
* across declarations, and must
|
|
390
|
+
* across declarations, and must reach one of the two markers. Spawning a
|
|
391
|
+
* *system* binary (`git`, `bash` with a temp fixture) matches neither, which
|
|
392
|
+
* is the intended exclusion — those are not this project's code.
|
|
393
|
+
*
|
|
394
|
+
* An optional type annotation is allowed between the name and `=`, e.g.
|
|
395
|
+
* const HOOK_COPIES: Array<[label: string, path: string]> = [...];
|
|
396
|
+
* The annotation submatch (`[^=;]*`) stops at the first `=`, so an arrow-
|
|
397
|
+
* function-typed annotation (`const f: (x: string) => void = ...`) breaks
|
|
398
|
+
* the match at its `=>` instead of reaching the real assignment — accepted
|
|
399
|
+
* as a narrow miss; typed function-value declarations are not the shape this
|
|
400
|
+
* collector targets (path-bearing table/tuple declarations are).
|
|
379
401
|
*/
|
|
380
402
|
function collectBuildOutputVars(content) {
|
|
381
403
|
const names = new Set();
|
|
382
|
-
const
|
|
404
|
+
const patterns = [
|
|
405
|
+
/(?:const|let|var)\s+(\w+)\s*(?::[^=;]*)?=\s*[^;]*?\bdist\//g,
|
|
406
|
+
/(?:const|let|var)\s+(\w+)\s*(?::[^=;]*)?=\s*[^;]*?\b(?:hooks\/[\w.-]+\.sh|scripts\/[\w./-]+)/g,
|
|
407
|
+
];
|
|
408
|
+
for (const pattern of patterns) {
|
|
409
|
+
let match;
|
|
410
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
411
|
+
names.add(match[1]);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
return [...names];
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* Collect callback parameter names bound to a build-output table's rows via
|
|
418
|
+
* `describe.each(X)("...", (a, b) => {...})`, when `X` is itself a
|
|
419
|
+
* build-output source: a var already collected by collectBuildOutputVars, or
|
|
420
|
+
* an inline array literal containing a build-output token directly.
|
|
421
|
+
*
|
|
422
|
+
* `describe.each` destructures each table row into positional callback
|
|
423
|
+
* params. A helper spawning `hookPath` — the param, not the table var — is
|
|
424
|
+
* exercising production code just as much as one spawning `HOOK_COPIES`
|
|
425
|
+
* directly; static analysis of the table alone misses it entirely. The
|
|
426
|
+
* string-title argument is matched by quote char (not `[^()]*`) because test
|
|
427
|
+
* titles routinely contain literal parens (e.g. `"... (#564) [%s]"`), which
|
|
428
|
+
* a paren-excluding class would truncate on.
|
|
429
|
+
*
|
|
430
|
+
* Bounded like the rest of this file's helper matchers: no nested parens in
|
|
431
|
+
* the table-var/params captures, so a callback with a destructured or
|
|
432
|
+
* default-valued param is skipped rather than mis-parsed.
|
|
433
|
+
*/
|
|
434
|
+
function collectDescribeEachParams(content, buildOutputVars) {
|
|
435
|
+
const names = new Set();
|
|
436
|
+
const pattern = /describe\.each\(\s*([^()]*?)\s*\)\s*\(\s*(['"`])(?:(?!\2)[\s\S])*?\2\s*,\s*(?:async\s+)?\(([^()]*)\)\s*=>/g;
|
|
383
437
|
let match;
|
|
384
438
|
while ((match = pattern.exec(content)) !== null) {
|
|
385
|
-
|
|
439
|
+
const tableArg = match[1].trim();
|
|
440
|
+
const isKnownVar = /^\w+$/.test(tableArg) && buildOutputVars.includes(tableArg);
|
|
441
|
+
const isInlineSource = BUILD_OUTPUT_PATTERN.test(tableArg) ||
|
|
442
|
+
PROJECT_SCRIPT_PATTERN.test(tableArg);
|
|
443
|
+
if (!isKnownVar && !isInlineSource)
|
|
444
|
+
continue;
|
|
445
|
+
for (const param of match[3].split(",")) {
|
|
446
|
+
const name = param.trim().split(":")[0].trim();
|
|
447
|
+
if (/^\w+$/.test(name)) {
|
|
448
|
+
names.add(name);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
386
451
|
}
|
|
387
452
|
return [...names];
|
|
388
453
|
}
|
|
@@ -391,7 +456,7 @@ function collectBuildOutputVars(content) {
|
|
|
391
456
|
* `dist/` marker or one of the collected build-path variable names.
|
|
392
457
|
*/
|
|
393
458
|
function referencesBuildOutput(body, buildOutputVars) {
|
|
394
|
-
if (BUILD_OUTPUT_PATTERN.test(body)) {
|
|
459
|
+
if (BUILD_OUTPUT_PATTERN.test(body) || PROJECT_SCRIPT_PATTERN.test(body)) {
|
|
395
460
|
return true;
|
|
396
461
|
}
|
|
397
462
|
return buildOutputVars.some((name) => referenceMatcher(name).test(body));
|
|
@@ -406,45 +471,84 @@ function spawnsBuildOutput(body, buildOutputVars) {
|
|
|
406
471
|
return (SPAWN_PATTERN.test(body) && referencesBuildOutput(body, buildOutputVars));
|
|
407
472
|
}
|
|
408
473
|
/**
|
|
409
|
-
* Extract module/describe-scope helper definitions
|
|
410
|
-
* consts) as { name, body } pairs.
|
|
474
|
+
* Extract module/describe-scope helper definitions as { name, body } pairs.
|
|
411
475
|
*
|
|
412
|
-
*
|
|
413
|
-
*
|
|
414
|
-
*
|
|
415
|
-
*
|
|
416
|
-
*
|
|
417
|
-
*
|
|
476
|
+
* Two shapes, because both are idiomatic and a detector that saw only one
|
|
477
|
+
* produced large-scale false positives (#906): a test calling a helper the
|
|
478
|
+
* detector cannot see reads as import-less, hence tautological. Measured on
|
|
479
|
+
* `checkout-lock.integration.test.ts` (helpers written as `function`
|
|
480
|
+
* declarations): 17 of 19 blocks flagged, every one of them real.
|
|
481
|
+
*
|
|
482
|
+
* Params are matched with `[^()]*` (no nested parens) to keep the scan from
|
|
483
|
+
* running away across the file. Expression-bodied arrows are skipped — they
|
|
484
|
+
* have no `{` body to extract.
|
|
418
485
|
*/
|
|
419
486
|
function extractHelperDefinitions(content) {
|
|
420
487
|
const helpers = [];
|
|
421
|
-
|
|
488
|
+
// Arrow consts anchor on `=> {`, so the body brace is unambiguous.
|
|
489
|
+
const arrowPattern = /(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s+)?\([^()]*\)\s*(?::[^=]*?)?=>\s*\{/g;
|
|
422
490
|
let match;
|
|
423
|
-
while ((match =
|
|
424
|
-
if (isInsideString(content, match.index))
|
|
491
|
+
while ((match = arrowPattern.exec(content)) !== null) {
|
|
492
|
+
if (isInsideString(content, match.index))
|
|
425
493
|
continue;
|
|
426
|
-
}
|
|
427
|
-
const name = match[1];
|
|
428
|
-
// The final `{` of the match is the function body's opening brace.
|
|
429
494
|
const braceIndex = match.index + match[0].length - 1;
|
|
430
|
-
|
|
431
|
-
|
|
495
|
+
helpers.push({
|
|
496
|
+
name: match[1],
|
|
497
|
+
body: extractBlockBody(content.substring(braceIndex)),
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
// Declarations have no `=>` marker, and the return-type annotation may open
|
|
501
|
+
// a brace group of its own:
|
|
502
|
+
// function runHook(o): { status: number; stderr: string } { ... }
|
|
503
|
+
// so the body is NOT simply the first `{` after the parameters. Do not try
|
|
504
|
+
// to express that in the regex — a greedy annotation subpattern silently ran
|
|
505
|
+
// past the body and anchored on the NEXT declaration's brace, yielding a
|
|
506
|
+
// "helper" whose body was somebody else's code (caught by the object
|
|
507
|
+
// return-type test below). Match only to the closing paren, then walk: take
|
|
508
|
+
// the first brace group; if another `{` follows it, that group was the
|
|
509
|
+
// return type and the body is the next one.
|
|
510
|
+
const declPattern = /(?:async\s+)?function\s*\*?\s*(\w+)\s*\([^()]*\)/g;
|
|
511
|
+
while ((match = declPattern.exec(content)) !== null) {
|
|
512
|
+
if (isInsideString(content, match.index))
|
|
513
|
+
continue;
|
|
514
|
+
const rest = content.substring(match.index + match[0].length);
|
|
515
|
+
const firstBrace = rest.indexOf("{");
|
|
516
|
+
if (firstBrace === -1)
|
|
517
|
+
continue;
|
|
518
|
+
// Anything between `)` and the first `{` must be an annotation, not code.
|
|
519
|
+
if (/[;)=]/.test(rest.substring(0, firstBrace)))
|
|
520
|
+
continue;
|
|
521
|
+
let body = extractBlockBody(rest.substring(firstBrace));
|
|
522
|
+
const after = rest.substring(firstBrace + body.length);
|
|
523
|
+
if (/^\s*\{/.test(after)) {
|
|
524
|
+
body = extractBlockBody(after);
|
|
525
|
+
}
|
|
526
|
+
helpers.push({ name: match[1], body });
|
|
432
527
|
}
|
|
433
528
|
return helpers;
|
|
434
529
|
}
|
|
435
530
|
/**
|
|
436
|
-
* Collect the names of helper functions that
|
|
437
|
-
* indirection transitively: a helper that calls an already-known
|
|
438
|
-
*
|
|
439
|
-
*
|
|
440
|
-
*
|
|
531
|
+
* Collect the names of helper functions that reach production code, resolving
|
|
532
|
+
* indirection transitively: a helper that calls an already-known handle is
|
|
533
|
+
* itself a handle. This lets a test that only calls `expectFlagAccepted(...)`
|
|
534
|
+
* (which calls `runInUninitializedDir`, which spawns the CLI) count as
|
|
535
|
+
* exercising production code.
|
|
536
|
+
*
|
|
537
|
+
* A helper qualifies two ways:
|
|
538
|
+
* - it spawns the project's own executable code (subprocess integration
|
|
539
|
+
* tests), or
|
|
540
|
+
* - it references an imported production function (#906). `makeLock()`
|
|
541
|
+
* returning `new CheckoutLock({...})` is production code by any reading,
|
|
542
|
+
* but seeding on spawns alone missed it, so every test that built its
|
|
543
|
+
* subject through a factory read as tautological.
|
|
441
544
|
*/
|
|
442
|
-
function
|
|
545
|
+
function collectProductionHandles(content, buildOutputVars, importedFunctions = []) {
|
|
443
546
|
const helpers = extractHelperDefinitions(content);
|
|
444
547
|
const handles = new Set();
|
|
445
|
-
// Seed: helpers that directly
|
|
548
|
+
// Seed: helpers that directly reach production.
|
|
446
549
|
for (const helper of helpers) {
|
|
447
|
-
if (spawnsBuildOutput(helper.body, buildOutputVars)
|
|
550
|
+
if (spawnsBuildOutput(helper.body, buildOutputVars) ||
|
|
551
|
+
importedFunctions.some((fn) => referenceMatcher(fn.name).test(helper.body))) {
|
|
448
552
|
handles.add(helper.name);
|
|
449
553
|
}
|
|
450
554
|
}
|
|
@@ -472,12 +576,13 @@ function collectSpawnHandles(content, buildOutputVars) {
|
|
|
472
576
|
* when it references an imported production function, directly spawns the
|
|
473
577
|
* project's build output, or calls a helper that (transitively) does so.
|
|
474
578
|
*
|
|
475
|
-
* @param
|
|
476
|
-
*
|
|
579
|
+
* @param productionHandles Names of describe/module-scope helpers that reach
|
|
580
|
+
* production — by spawning the project's executable code or by calling an
|
|
581
|
+
* imported production function (see {@link collectProductionHandles}).
|
|
477
582
|
* @param buildOutputVars Variable names bound to a build-output path (see
|
|
478
583
|
* {@link collectBuildOutputVars}).
|
|
479
584
|
*/
|
|
480
|
-
export function testBlockCallsProductionCode(body, importedFunctions,
|
|
585
|
+
export function testBlockCallsProductionCode(body, importedFunctions, productionHandles = [], buildOutputVars = []) {
|
|
481
586
|
// 1. References an imported production function.
|
|
482
587
|
for (const fn of importedFunctions) {
|
|
483
588
|
if (referenceMatcher(fn.name).test(body)) {
|
|
@@ -490,9 +595,10 @@ export function testBlockCallsProductionCode(body, importedFunctions, spawnHandl
|
|
|
490
595
|
if (spawnsBuildOutput(body, buildOutputVars)) {
|
|
491
596
|
return true;
|
|
492
597
|
}
|
|
493
|
-
// 3. Calls a describe/module-scope helper that (transitively)
|
|
494
|
-
//
|
|
495
|
-
|
|
598
|
+
// 3. Calls a describe/module-scope helper that (transitively) reaches
|
|
599
|
+
// production — spawns the project's executable code, or calls an imported
|
|
600
|
+
// production function (#906). Covers arrow-const and `function` helpers.
|
|
601
|
+
for (const handle of productionHandles) {
|
|
496
602
|
if (referenceMatcher(handle).test(body)) {
|
|
497
603
|
return true;
|
|
498
604
|
}
|
|
@@ -528,14 +634,15 @@ export function analyzeTestFile(content, filePath) {
|
|
|
528
634
|
}
|
|
529
635
|
try {
|
|
530
636
|
const importedFunctions = extractImports(content);
|
|
531
|
-
const
|
|
532
|
-
const
|
|
637
|
+
const declaredBuildOutputVars = collectBuildOutputVars(content);
|
|
638
|
+
const buildOutputVars = declaredBuildOutputVars.concat(collectDescribeEachParams(content, declaredBuildOutputVars));
|
|
639
|
+
const productionHandles = collectProductionHandles(content, buildOutputVars, importedFunctions);
|
|
533
640
|
const testBlocks = extractTestBlocks(content);
|
|
534
641
|
const analyzedBlocks = testBlocks.map((block) => ({
|
|
535
642
|
description: block.description,
|
|
536
643
|
lineNumber: block.lineNumber,
|
|
537
644
|
style: block.style,
|
|
538
|
-
isTautological: !testBlockCallsProductionCode(block.body, importedFunctions,
|
|
645
|
+
isTautological: !testBlockCallsProductionCode(block.body, importedFunctions, productionHandles, buildOutputVars),
|
|
539
646
|
}));
|
|
540
647
|
const tautologicalCount = analyzedBlocks.filter((b) => b.isTautological).length;
|
|
541
648
|
const totalTests = analyzedBlocks.length;
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* creation).
|
|
9
9
|
*/
|
|
10
10
|
import { LogWriter } from "./log-writer.js";
|
|
11
|
-
import { ExecutionConfig, PhaseResult, IssueResult, type RunOptions, type IssueExecutionContext, type BatchExecutionContext, type ProgressCallback } from "./types.js";
|
|
11
|
+
import { ExecutionConfig, PhaseResult, IssueResult, type RunOptions, type IssueExecutionContext, type BatchExecutionContext, type ProgressCallback, type QaVerdict, type QaSummary } from "./types.js";
|
|
12
12
|
import { type ErrorCategory } from "./error-classifier.js";
|
|
13
13
|
export type { RunOptions, ProgressCallback, IssueExecutionContext, BatchExecutionContext, } from "./types.js";
|
|
14
14
|
/**
|
|
@@ -205,4 +205,23 @@ export declare function billingHaltReason(result: PhaseResult): string;
|
|
|
205
205
|
* @internal Exported for testing
|
|
206
206
|
*/
|
|
207
207
|
export declare function windowHaltResumeAtMs(result: PhaseResult): number | null;
|
|
208
|
+
/**
|
|
209
|
+
* Build the comment body for a standard-qa-phase verdict post (#964).
|
|
210
|
+
* Includes AC coverage and any gaps/suggestions from the parsed `QaSummary`,
|
|
211
|
+
* plus a machine marker so a future dedup pass has an anchor.
|
|
212
|
+
* @internal Exported for testing.
|
|
213
|
+
*/
|
|
214
|
+
export declare function buildQaVerdictComment(verdict: QaVerdict, summary: QaSummary | undefined, commitHash: string | undefined, iteration: number): string;
|
|
215
|
+
/**
|
|
216
|
+
* Post the qa-verdict comment for a standard (non-ready-gate) qa phase under
|
|
217
|
+
* orchestrated `sequant run` (#964). This is the channel `qa/SKILL.md` §9
|
|
218
|
+
* promises ("orchestrator handles aggregated summary") but batch-executor
|
|
219
|
+
* never backed — a re-run producing a fresh, different verdict left a stale,
|
|
220
|
+
* contradicted comment as the only externally-visible one.
|
|
221
|
+
*
|
|
222
|
+
* Best-effort: a post failure is caught and logged, never fails the run —
|
|
223
|
+
* mirrors {@link runReadyGateForIssue}'s `postReport` contract (#937 AC-4).
|
|
224
|
+
* @internal Exported for testing.
|
|
225
|
+
*/
|
|
226
|
+
export declare function postQaVerdictComment(issueNumber: number, verdict: QaVerdict, summary: QaSummary | undefined, commitHash: string | undefined, iteration: number, log: (msg: string) => void, postComment?: (issueNumber: number, body: string) => Promise<void>): Promise<void>;
|
|
208
227
|
export declare function runIssueWithLogging(ctx: IssueExecutionContext): Promise<IssueResult>;
|