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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +20 -5
- package/dist/bin/cli.js +143 -18
- 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/README.md +56 -2
- package/dist/marketplace/external_plugins/sequant/hooks/parallel-marker.sh +50 -0
- package/dist/marketplace/external_plugins/sequant/hooks/post-tool.sh +20 -7
- package/dist/marketplace/external_plugins/sequant/hooks/pre-tool.sh +75 -10
- package/dist/marketplace/external_plugins/sequant/skills/_shared/references/trust-model.md +18 -0
- package/dist/marketplace/external_plugins/sequant/skills/assess/SKILL.md +215 -390
- package/dist/marketplace/external_plugins/sequant/skills/assess/references/predicted-collision-detection.md +1 -1
- package/dist/marketplace/external_plugins/sequant/skills/docs/SKILL.md +4 -4
- package/dist/marketplace/external_plugins/sequant/skills/exec/SKILL.md +26 -6
- package/dist/marketplace/external_plugins/sequant/skills/fullsolve/SKILL.md +3 -1
- package/dist/marketplace/external_plugins/sequant/skills/loop/SKILL.md +38 -2
- package/dist/marketplace/external_plugins/sequant/skills/merger/SKILL.md +2 -2
- package/dist/marketplace/external_plugins/sequant/skills/qa/SKILL.md +337 -57
- package/dist/marketplace/external_plugins/sequant/skills/qa/references/anti-pattern-detection.md +6 -6
- package/dist/marketplace/external_plugins/sequant/skills/qa/references/call-site-review.md +2 -2
- package/dist/marketplace/external_plugins/sequant/skills/qa/references/code-review-checklist.md +2 -2
- package/dist/marketplace/external_plugins/sequant/skills/qa/references/fixtures/injection-issue-body.md +36 -0
- package/dist/marketplace/external_plugins/sequant/skills/qa/references/quality-gates.md +21 -7
- package/dist/marketplace/external_plugins/sequant/skills/qa/references/testing-requirements.md +1 -1
- package/dist/marketplace/external_plugins/sequant/skills/qa/scripts/quality-checks.sh +11 -11
- package/dist/marketplace/external_plugins/sequant/skills/release/SKILL.md +1 -1
- package/dist/marketplace/external_plugins/sequant/skills/setup/SKILL.md +5 -5
- package/dist/marketplace/external_plugins/sequant/skills/spec/SKILL.md +2 -0
- package/dist/marketplace/external_plugins/sequant/skills/test/SKILL.md +1 -1
- package/dist/src/commands/assess-render.d.ts +23 -0
- package/dist/src/commands/assess-render.js +60 -0
- package/dist/src/commands/doctor.js +8 -13
- package/dist/src/commands/init.js +13 -1
- package/dist/src/commands/locks.d.ts +20 -1
- package/dist/src/commands/locks.js +208 -4
- package/dist/src/commands/logs.js +14 -5
- package/dist/src/commands/merge.js +154 -2
- package/dist/src/commands/ready-tui-adapter.js +6 -1
- package/dist/src/commands/ready.d.ts +31 -3
- package/dist/src/commands/ready.js +53 -13
- package/dist/src/commands/resume.d.ts +113 -0
- package/dist/src/commands/resume.js +193 -0
- package/dist/src/commands/run-display.js +38 -4
- package/dist/src/commands/run-flags.d.ts +42 -1
- package/dist/src/commands/run-flags.js +53 -1
- package/dist/src/commands/run-progress.js +26 -1
- package/dist/src/commands/run.js +11 -10
- package/dist/src/commands/state.js +7 -0
- package/dist/src/commands/status.d.ts +9 -0
- package/dist/src/commands/status.js +24 -1
- package/dist/src/commands/sync.js +90 -19
- package/dist/src/commands/update.js +28 -5
- package/dist/src/commands/worktree.d.ts +31 -0
- package/dist/src/commands/worktree.js +95 -0
- package/dist/src/lib/ac-parser.d.ts +14 -0
- package/dist/src/lib/ac-parser.js +99 -5
- package/dist/src/lib/assess/index.d.ts +10 -0
- package/dist/src/lib/assess/index.js +9 -0
- package/dist/src/lib/assess/renderer.d.ts +64 -0
- package/dist/src/lib/assess/renderer.js +481 -0
- package/dist/src/lib/assess/types.d.ts +224 -0
- package/dist/src/lib/assess/types.js +241 -0
- package/dist/src/lib/ci/outputs.d.ts +15 -2
- package/dist/src/lib/ci/outputs.js +17 -5
- package/dist/src/lib/cli-flags.d.ts +56 -0
- package/dist/src/lib/cli-flags.js +89 -0
- package/dist/src/lib/cli-ui/run-renderer-types.d.ts +23 -7
- package/dist/src/lib/cli-ui/run-renderer.d.ts +10 -0
- package/dist/src/lib/cli-ui/run-renderer.js +69 -1
- package/dist/src/lib/errors.d.ts +78 -6
- package/dist/src/lib/errors.js +133 -15
- 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 +7 -4
- package/dist/src/lib/locks/index.js +5 -3
- package/dist/src/lib/locks/lock-manager.d.ts +101 -3
- package/dist/src/lib/locks/lock-manager.js +277 -8
- package/dist/src/lib/locks/types.d.ts +106 -3
- package/dist/src/lib/locks/types.js +44 -0
- package/dist/src/lib/mcp-config.d.ts +45 -0
- package/dist/src/lib/mcp-config.js +77 -1
- package/dist/src/lib/merge-check/combined-branch-test.d.ts +21 -2
- package/dist/src/lib/merge-check/combined-branch-test.js +233 -110
- package/dist/src/lib/merge-check/command-result.d.ts +36 -0
- package/dist/src/lib/merge-check/command-result.js +56 -0
- package/dist/src/lib/merge-check/index.d.ts +4 -0
- package/dist/src/lib/merge-check/index.js +1 -1
- package/dist/src/lib/merge-check/report.js +5 -1
- package/dist/src/lib/merge-check/types.d.ts +9 -0
- package/dist/src/lib/merge-check/watch.d.ts +121 -0
- package/dist/src/lib/merge-check/watch.js +215 -0
- package/dist/src/lib/qa/infra-blocked-ci.d.ts +90 -0
- package/dist/src/lib/qa/infra-blocked-ci.js +80 -0
- package/dist/src/lib/settings.d.ts +87 -0
- package/dist/src/lib/settings.js +106 -8
- package/dist/src/lib/shutdown.d.ts +42 -2
- package/dist/src/lib/shutdown.js +75 -5
- package/dist/src/lib/skills-check.d.ts +26 -0
- package/dist/src/lib/skills-check.js +31 -0
- package/dist/src/lib/stacks.d.ts +110 -1
- package/dist/src/lib/stacks.js +244 -5
- package/dist/src/lib/templates.d.ts +74 -0
- package/dist/src/lib/templates.js +171 -16
- package/dist/src/lib/test-tautology-detector.d.ts +10 -2
- package/dist/src/lib/test-tautology-detector.js +213 -12
- package/dist/src/lib/version-check.d.ts +31 -0
- package/dist/src/lib/version-check.js +45 -2
- package/dist/src/lib/workflow/batch-executor.d.ts +125 -6
- package/dist/src/lib/workflow/batch-executor.js +552 -60
- package/dist/src/lib/workflow/chain-resume.d.ts +13 -11
- package/dist/src/lib/workflow/chain-resume.js +24 -20
- package/dist/src/lib/workflow/completed-status.d.ts +52 -0
- package/dist/src/lib/workflow/completed-status.js +57 -0
- package/dist/src/lib/workflow/config-resolver.d.ts +51 -0
- package/dist/src/lib/workflow/config-resolver.js +143 -2
- package/dist/src/lib/workflow/drivers/agent-driver.d.ts +22 -0
- package/dist/src/lib/workflow/drivers/aider.d.ts +2 -0
- package/dist/src/lib/workflow/drivers/aider.js +2 -0
- package/dist/src/lib/workflow/drivers/claude-code.d.ts +14 -0
- package/dist/src/lib/workflow/drivers/claude-code.js +25 -2
- 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/git-diff-utils.d.ts +35 -3
- package/dist/src/lib/workflow/git-diff-utils.js +77 -4
- package/dist/src/lib/workflow/heartbeat.d.ts +38 -0
- package/dist/src/lib/workflow/heartbeat.js +90 -0
- package/dist/src/lib/workflow/log-writer.d.ts +54 -2
- package/dist/src/lib/workflow/log-writer.js +95 -6
- package/dist/src/lib/workflow/metrics-schema.d.ts +52 -9
- 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/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 +203 -14
- package/dist/src/lib/workflow/phase-executor.js +467 -52
- package/dist/src/lib/workflow/phase-registry.js +2 -1
- package/dist/src/lib/workflow/platforms/github.d.ts +64 -0
- package/dist/src/lib/workflow/platforms/github.js +152 -0
- package/dist/src/lib/workflow/qa-cache.d.ts +6 -1
- package/dist/src/lib/workflow/qa-cache.js +22 -9
- package/dist/src/lib/workflow/ready-gate.d.ts +45 -4
- package/dist/src/lib/workflow/ready-gate.js +49 -12
- package/dist/src/lib/workflow/run-log-schema.d.ts +69 -0
- package/dist/src/lib/workflow/run-log-schema.js +78 -2
- package/dist/src/lib/workflow/run-orchestrator.d.ts +19 -0
- package/dist/src/lib/workflow/run-orchestrator.js +168 -20
- package/dist/src/lib/workflow/skills-preflight.d.ts +89 -0
- package/dist/src/lib/workflow/skills-preflight.js +112 -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-cleanup.js +9 -2
- package/dist/src/lib/workflow/state-manager.d.ts +33 -0
- package/dist/src/lib/workflow/state-manager.js +83 -0
- package/dist/src/lib/workflow/state-schema.d.ts +26 -0
- package/dist/src/lib/workflow/state-schema.js +47 -0
- package/dist/src/lib/workflow/types.d.ts +190 -4
- package/dist/src/lib/workflow/types.js +1 -0
- package/dist/src/lib/workflow/worktree-manager.d.ts +36 -3
- package/dist/src/lib/workflow/worktree-manager.js +111 -24
- package/dist/src/lib/workflow/worktree-resolver.d.ts +73 -0
- package/dist/src/lib/workflow/worktree-resolver.js +126 -0
- package/dist/src/mcp/tools/run.d.ts +9 -1
- package/dist/src/mcp/tools/run.js +20 -1
- package/dist/src/mcp/tools/status.js +6 -0
- package/dist/src/ui/tui/ElapsedTimer.d.ts +12 -3
- package/dist/src/ui/tui/ElapsedTimer.js +12 -10
- package/dist/src/ui/tui/IssueBox.js +10 -2
- package/dist/src/ui/tui/row-cap.js +2 -1
- package/dist/src/ui/tui/theme.d.ts +7 -0
- package/dist/src/ui/tui/theme.js +9 -0
- package/package.json +10 -8
- package/templates/hooks/parallel-marker.sh +50 -0
- package/templates/hooks/post-tool.sh +20 -7
- package/templates/hooks/pre-tool.sh +303 -10
- package/templates/scripts/cleanup-worktree.sh +217 -33
- package/templates/scripts/new-feature.sh +289 -35
- package/templates/skills/_shared/references/subagent-types.md +7 -18
- package/templates/skills/_shared/references/trust-model.md +18 -0
- package/templates/skills/assess/SKILL.md +220 -391
- package/templates/skills/assess/references/predicted-collision-detection.md +1 -1
- package/templates/skills/docs/SKILL.md +4 -4
- package/templates/skills/exec/SKILL.md +87 -13
- package/templates/skills/fullsolve/SKILL.md +130 -22
- package/templates/skills/loop/SKILL.md +94 -13
- package/templates/skills/merger/SKILL.md +100 -12
- package/templates/skills/qa/SKILL.md +396 -63
- package/templates/skills/qa/references/anti-pattern-detection.md +6 -6
- package/templates/skills/qa/references/call-site-review.md +2 -2
- package/templates/skills/qa/references/code-review-checklist.md +2 -2
- package/templates/skills/qa/references/fixtures/injection-issue-body.md +36 -0
- package/templates/skills/qa/references/quality-gates.md +21 -7
- package/templates/skills/qa/references/testing-requirements.md +1 -1
- package/templates/skills/qa/scripts/quality-checks.sh +11 -11
- package/templates/skills/release/SKILL.md +80 -1
- package/templates/skills/setup/SKILL.md +5 -5
- package/templates/skills/spec/SKILL.md +33 -15
- 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/dist/src/lib/phase-spinner.d.ts +0 -146
- package/dist/src/lib/phase-spinner.js +0 -255
- package/dist/src/lib/workflow/pr-operations.d.ts +0 -86
- package/dist/src/lib/workflow/pr-operations.js +0 -326
- package/dist/src/lib/workflow/run-summary.d.ts +0 -36
- package/dist/src/lib/workflow/run-summary.js +0 -142
- package/templates/agents/sequant-explorer.md +0 -24
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured resolution of the spec→run phase recommendation (#921).
|
|
3
|
+
*
|
|
4
|
+
* `parseRecommendedWorkflow` (phase-mapper.ts) regexes the spec agent's
|
|
5
|
+
* *ephemeral chat text* for a `## Recommended Workflow` section. When the
|
|
6
|
+
* spec agent posts its plan via a body file instead of restating it in
|
|
7
|
+
* chat, that regex has nothing to match and the run silently falls back to
|
|
8
|
+
* label-based phase detection — dropping any recommended phase the label
|
|
9
|
+
* fallback can never produce (e.g. `testgen`). See #814.
|
|
10
|
+
*
|
|
11
|
+
* This module resolves the recommendation through an ordered chain, each
|
|
12
|
+
* step falling through loudly to the next on failure:
|
|
13
|
+
*
|
|
14
|
+
* 1. `marker` — the durable `<!-- SEQUANT_SPEC: {json} --> `
|
|
15
|
+
* comment marker (this issue's fix)
|
|
16
|
+
* 2. `comment-prose` — the same regex as `chat`, applied to the spec
|
|
17
|
+
* plan's GitHub comment body instead of chat text
|
|
18
|
+
* 3. `chat` — `parseRecommendedWorkflow` over the agent's
|
|
19
|
+
* captured chat output (existing behavior)
|
|
20
|
+
* 4. `label-fallback` — `detectPhasesFromLabels` (existing behavior)
|
|
21
|
+
*
|
|
22
|
+
* The durable comment marker is the system's existing idiom — see
|
|
23
|
+
* `SEQUANT_PHASE` in `phase-detection.ts` and the `/assess` HTML markers in
|
|
24
|
+
* `assess-comment-parser.ts`.
|
|
25
|
+
*/
|
|
26
|
+
import chalk from "chalk";
|
|
27
|
+
import { z } from "zod";
|
|
28
|
+
import { phaseRegistry } from "./phase-registry.js";
|
|
29
|
+
import { GitHubProvider } from "./platforms/github.js";
|
|
30
|
+
import { stripMarkdownCode } from "./phase-detection.js";
|
|
31
|
+
import { parseRecommendedWorkflow, detectPhasesFromLabels, } from "./phase-mapper.js";
|
|
32
|
+
/** Regex to extract the SEQUANT_SPEC marker JSON from an HTML comment. */
|
|
33
|
+
const SPEC_MARKER_REGEX = /<!-- SEQUANT_SPEC: (\{[^}]+\}) -->/g;
|
|
34
|
+
/**
|
|
35
|
+
* Structural shape of the marker JSON, before phase names are checked
|
|
36
|
+
* against the phase registry. `qualityLoop` defaults to `false` when
|
|
37
|
+
* omitted, matching `parseRecommendedWorkflow`'s prose-parsing default.
|
|
38
|
+
*/
|
|
39
|
+
const SpecMarkerJsonSchema = z.object({
|
|
40
|
+
phases: z.array(z.string()).min(1),
|
|
41
|
+
qualityLoop: z.boolean().optional().default(false),
|
|
42
|
+
});
|
|
43
|
+
/**
|
|
44
|
+
* Extract and validate the latest `SEQUANT_SPEC` marker across a set of
|
|
45
|
+
* comment bodies (oldest-to-newest order, matching `gh`'s natural order).
|
|
46
|
+
*
|
|
47
|
+
* Returns `null` when no marker is present at all. Returns `null` and logs a
|
|
48
|
+
* visible warning when the latest marker is malformed JSON, fails schema
|
|
49
|
+
* validation, or names a phase the registry doesn't recognize — per AC-2,
|
|
50
|
+
* an invalid marker falls through to the next resolution step rather than
|
|
51
|
+
* silently dropping the recommendation or silently ignoring the bad phase.
|
|
52
|
+
*
|
|
53
|
+
* Only the *latest* marker is considered (last comment, last match within
|
|
54
|
+
* it) — a re-spec's marker supersedes an earlier one, matching how a human
|
|
55
|
+
* reader would interpret the thread.
|
|
56
|
+
*/
|
|
57
|
+
export function parseSpecMarker(commentBodies) {
|
|
58
|
+
let latestRaw = null;
|
|
59
|
+
for (const body of commentBodies) {
|
|
60
|
+
const stripped = stripMarkdownCode(body);
|
|
61
|
+
SPEC_MARKER_REGEX.lastIndex = 0;
|
|
62
|
+
let match;
|
|
63
|
+
while ((match = SPEC_MARKER_REGEX.exec(stripped)) !== null) {
|
|
64
|
+
latestRaw = match[1];
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (latestRaw === null) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
let parsedJson;
|
|
71
|
+
try {
|
|
72
|
+
parsedJson = JSON.parse(latestRaw);
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
console.log(chalk.yellow(` ⚠ SEQUANT_SPEC marker is not valid JSON — falling through to comment prose`));
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
const result = SpecMarkerJsonSchema.safeParse(parsedJson);
|
|
79
|
+
if (!result.success) {
|
|
80
|
+
console.log(chalk.yellow(` ⚠ SEQUANT_SPEC marker failed schema validation — falling through to comment prose`));
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
const unknownPhases = result.data.phases.filter((name) => !phaseRegistry.has(name));
|
|
84
|
+
if (unknownPhases.length > 0) {
|
|
85
|
+
console.log(chalk.yellow(` ⚠ SEQUANT_SPEC marker names unknown phase(s): ${unknownPhases.join(", ")} — falling through to comment prose`));
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
return { phases: result.data.phases, qualityLoop: result.data.qualityLoop };
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Find the most recently posted comment containing a `## Recommended
|
|
92
|
+
* Workflow` section and parse it with the existing prose regex.
|
|
93
|
+
*
|
|
94
|
+
* Scans newest-to-oldest so a later spec re-run's comment takes precedence
|
|
95
|
+
* over an earlier one — same intuition as the marker step.
|
|
96
|
+
*/
|
|
97
|
+
function parseCommentProse(commentBodies) {
|
|
98
|
+
for (let i = commentBodies.length - 1; i >= 0; i--) {
|
|
99
|
+
const parsed = parseRecommendedWorkflow(commentBodies[i]);
|
|
100
|
+
if (parsed) {
|
|
101
|
+
return parsed;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Resolve the spec→run phase recommendation through the ordered chain:
|
|
108
|
+
* comment-marker → comment-prose → chat-text → label-fallback.
|
|
109
|
+
*/
|
|
110
|
+
export function resolveSpecRecommendation(input) {
|
|
111
|
+
const github = input.githubProvider ?? new GitHubProvider();
|
|
112
|
+
const commentBodies = github.fetchIssueCommentBodiesSync(String(input.issueNumber));
|
|
113
|
+
let phases;
|
|
114
|
+
let qualityLoop;
|
|
115
|
+
let source;
|
|
116
|
+
const marker = parseSpecMarker(commentBodies);
|
|
117
|
+
const prose = marker ? null : parseCommentProse(commentBodies);
|
|
118
|
+
const chat = marker || prose || !input.chatOutput
|
|
119
|
+
? null
|
|
120
|
+
: parseRecommendedWorkflow(input.chatOutput);
|
|
121
|
+
if (marker) {
|
|
122
|
+
({ phases, qualityLoop } = marker);
|
|
123
|
+
source = "marker";
|
|
124
|
+
}
|
|
125
|
+
else if (prose) {
|
|
126
|
+
({ phases, qualityLoop } = prose);
|
|
127
|
+
source = "comment-prose";
|
|
128
|
+
}
|
|
129
|
+
else if (chat) {
|
|
130
|
+
({ phases, qualityLoop } = chat);
|
|
131
|
+
source = "chat";
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
const fallback = detectPhasesFromLabels(input.labels);
|
|
135
|
+
phases = fallback.phases;
|
|
136
|
+
qualityLoop = fallback.qualityLoop;
|
|
137
|
+
source = "label-fallback";
|
|
138
|
+
}
|
|
139
|
+
// Spec already ran by the time this resolves — never re-include it,
|
|
140
|
+
// regardless of which step in the chain produced the result.
|
|
141
|
+
return { phases: phases.filter((p) => p !== "spec"), qualityLoop, source };
|
|
142
|
+
}
|
|
@@ -221,13 +221,20 @@ export async function reconcileStateAtStartup(options = {}) {
|
|
|
221
221
|
const state = await manager.getState();
|
|
222
222
|
const advanced = [];
|
|
223
223
|
const stillPending = [];
|
|
224
|
-
// Find issues
|
|
224
|
+
// Find issues whose PR may have merged since their status was written.
|
|
225
225
|
// in_progress covers PRs merged outside this session (#592).
|
|
226
226
|
// waiting_for_qa_gate covers PRs merged before the next QA-gate run (#606).
|
|
227
|
+
// waiting_for_human_merge covers #817's `--ready-gate` terminal: a gated
|
|
228
|
+
// issue never reaches ready_for_merge, so without it a gated issue whose PR
|
|
229
|
+
// a human then merged stayed here forever and never advanced to merged
|
|
230
|
+
// (#837). Note this list is deliberately WIDER than `isCompletedIssueStatus`
|
|
231
|
+
// — it asks "might this have a merged PR?", not "is this done?", which is
|
|
232
|
+
// why in_progress belongs here but not there.
|
|
227
233
|
for (const [issueNumStr, issueState] of Object.entries(state.issues)) {
|
|
228
234
|
if (issueState.status !== "ready_for_merge" &&
|
|
229
235
|
issueState.status !== "in_progress" &&
|
|
230
|
-
issueState.status !== "waiting_for_qa_gate"
|
|
236
|
+
issueState.status !== "waiting_for_qa_gate" &&
|
|
237
|
+
issueState.status !== "waiting_for_human_merge") {
|
|
231
238
|
continue;
|
|
232
239
|
}
|
|
233
240
|
const issueNum = parseInt(issueNumStr, 10);
|
|
@@ -126,6 +126,39 @@ export declare class StateManager {
|
|
|
126
126
|
* Update PR information for an issue
|
|
127
127
|
*/
|
|
128
128
|
updatePRInfo(issueNumber: number, pr: PRInfo): Promise<void>;
|
|
129
|
+
/**
|
|
130
|
+
* Record or clear an in-progress auto-wait (#860). `wakeAtMs` sets the
|
|
131
|
+
* pause marker (and which phase is paused); `null` clears it on wake.
|
|
132
|
+
*
|
|
133
|
+
* Unlike the sibling updaters this NEVER throws on an untracked issue —
|
|
134
|
+
* it is called from a liveness hook during a live wait, and a bookkeeping
|
|
135
|
+
* miss must not interrupt the pause it is describing.
|
|
136
|
+
*/
|
|
137
|
+
updateAutoWait(issueNumber: number, phase: string, wakeAtMs: number | null): Promise<void>;
|
|
138
|
+
/**
|
|
139
|
+
* Record a durable waitable-window halt (#892 AC-1). Preserves an existing
|
|
140
|
+
* re-entry counter so repeated halts on a still-closed window keep counting
|
|
141
|
+
* toward the re-entry bound instead of resetting it.
|
|
142
|
+
*
|
|
143
|
+
* Like `updateAutoWait`, NEVER throws on an untracked issue — it runs on a
|
|
144
|
+
* halt path where bookkeeping must not mask the real failure.
|
|
145
|
+
*/
|
|
146
|
+
updateWindowHalt(issueNumber: number, phase: string, resumeAtMs: number): Promise<void>;
|
|
147
|
+
/**
|
|
148
|
+
* Clear a stale window-halt record (#892) — on any phase success (the
|
|
149
|
+
* window demonstrably reopened; progress also resets the re-entry counter)
|
|
150
|
+
* or a non-window failure (the halt cause is no longer waitable, so an
|
|
151
|
+
* unattended re-entry must not fire on it). No-ops (no state write) when
|
|
152
|
+
* nothing is recorded; never throws on an untracked issue.
|
|
153
|
+
*/
|
|
154
|
+
clearWindowHalt(issueNumber: number): Promise<void>;
|
|
155
|
+
/**
|
|
156
|
+
* Consume one re-entry attempt for a halted issue (#892 AC-3). Returns the
|
|
157
|
+
* new count, or `null` when the issue has no window-halt record (nothing to
|
|
158
|
+
* resume). Called by `sequant resume` immediately before re-entry so a
|
|
159
|
+
* re-entry that halts again (window still closed) is already counted.
|
|
160
|
+
*/
|
|
161
|
+
incrementWindowHaltReentries(issueNumber: number): Promise<number | null>;
|
|
129
162
|
/**
|
|
130
163
|
* Update worktree information for an issue
|
|
131
164
|
*/
|
|
@@ -364,6 +364,89 @@ export class StateManager {
|
|
|
364
364
|
console.log(`State: PR #${pr.number} linked to issue #${issueNumber}`);
|
|
365
365
|
}
|
|
366
366
|
}
|
|
367
|
+
/**
|
|
368
|
+
* Record or clear an in-progress auto-wait (#860). `wakeAtMs` sets the
|
|
369
|
+
* pause marker (and which phase is paused); `null` clears it on wake.
|
|
370
|
+
*
|
|
371
|
+
* Unlike the sibling updaters this NEVER throws on an untracked issue —
|
|
372
|
+
* it is called from a liveness hook during a live wait, and a bookkeeping
|
|
373
|
+
* miss must not interrupt the pause it is describing.
|
|
374
|
+
*/
|
|
375
|
+
async updateAutoWait(issueNumber, phase, wakeAtMs) {
|
|
376
|
+
await this.withLock(async () => {
|
|
377
|
+
const state = await this.getState();
|
|
378
|
+
const issueState = state.issues[String(issueNumber)];
|
|
379
|
+
if (!issueState)
|
|
380
|
+
return;
|
|
381
|
+
issueState.autoWait =
|
|
382
|
+
wakeAtMs === null
|
|
383
|
+
? undefined
|
|
384
|
+
: { wakeAt: new Date(wakeAtMs).toISOString(), phase };
|
|
385
|
+
issueState.lastActivity = new Date().toISOString();
|
|
386
|
+
await this.saveState(state);
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Record a durable waitable-window halt (#892 AC-1). Preserves an existing
|
|
391
|
+
* re-entry counter so repeated halts on a still-closed window keep counting
|
|
392
|
+
* toward the re-entry bound instead of resetting it.
|
|
393
|
+
*
|
|
394
|
+
* Like `updateAutoWait`, NEVER throws on an untracked issue — it runs on a
|
|
395
|
+
* halt path where bookkeeping must not mask the real failure.
|
|
396
|
+
*/
|
|
397
|
+
async updateWindowHalt(issueNumber, phase, resumeAtMs) {
|
|
398
|
+
await this.withLock(async () => {
|
|
399
|
+
const state = await this.getState();
|
|
400
|
+
const issueState = state.issues[String(issueNumber)];
|
|
401
|
+
if (!issueState)
|
|
402
|
+
return;
|
|
403
|
+
issueState.windowHalt = {
|
|
404
|
+
resumeAt: new Date(resumeAtMs).toISOString(),
|
|
405
|
+
phase,
|
|
406
|
+
reentries: issueState.windowHalt?.reentries ?? 0,
|
|
407
|
+
};
|
|
408
|
+
issueState.lastActivity = new Date().toISOString();
|
|
409
|
+
await this.saveState(state);
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Clear a stale window-halt record (#892) — on any phase success (the
|
|
414
|
+
* window demonstrably reopened; progress also resets the re-entry counter)
|
|
415
|
+
* or a non-window failure (the halt cause is no longer waitable, so an
|
|
416
|
+
* unattended re-entry must not fire on it). No-ops (no state write) when
|
|
417
|
+
* nothing is recorded; never throws on an untracked issue.
|
|
418
|
+
*/
|
|
419
|
+
async clearWindowHalt(issueNumber) {
|
|
420
|
+
await this.withLock(async () => {
|
|
421
|
+
const state = await this.getState();
|
|
422
|
+
const issueState = state.issues[String(issueNumber)];
|
|
423
|
+
if (!issueState?.windowHalt)
|
|
424
|
+
return;
|
|
425
|
+
issueState.windowHalt = undefined;
|
|
426
|
+
issueState.lastActivity = new Date().toISOString();
|
|
427
|
+
await this.saveState(state);
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Consume one re-entry attempt for a halted issue (#892 AC-3). Returns the
|
|
432
|
+
* new count, or `null` when the issue has no window-halt record (nothing to
|
|
433
|
+
* resume). Called by `sequant resume` immediately before re-entry so a
|
|
434
|
+
* re-entry that halts again (window still closed) is already counted.
|
|
435
|
+
*/
|
|
436
|
+
async incrementWindowHaltReentries(issueNumber) {
|
|
437
|
+
let newCount = null;
|
|
438
|
+
await this.withLock(async () => {
|
|
439
|
+
const state = await this.getState();
|
|
440
|
+
const issueState = state.issues[String(issueNumber)];
|
|
441
|
+
if (!issueState?.windowHalt)
|
|
442
|
+
return;
|
|
443
|
+
issueState.windowHalt.reentries += 1;
|
|
444
|
+
newCount = issueState.windowHalt.reentries;
|
|
445
|
+
issueState.lastActivity = new Date().toISOString();
|
|
446
|
+
await this.saveState(state);
|
|
447
|
+
});
|
|
448
|
+
return newCount;
|
|
449
|
+
}
|
|
367
450
|
/**
|
|
368
451
|
* Update worktree information for an issue
|
|
369
452
|
*/
|
|
@@ -213,6 +213,14 @@ export declare const RelayStateSchema: z.ZodObject<{
|
|
|
213
213
|
messageCount: z.ZodNumber;
|
|
214
214
|
}, z.core.$strip>;
|
|
215
215
|
export type RelayState = z.infer<typeof RelayStateSchema>;
|
|
216
|
+
/**
|
|
217
|
+
* Maximum scheduler re-entries per window-halted issue (#892 AC-3). Mirrors
|
|
218
|
+
* `AUTO_WAIT_MAX_WAITS` (#804): both bound how often sequant returns to the
|
|
219
|
+
* same closed rate-limit window before declaring the halt terminal. Lives
|
|
220
|
+
* here — beside the `windowHalt.reentries` field it bounds — so display
|
|
221
|
+
* surfaces (`sequant status`) can import it without pulling in the run path.
|
|
222
|
+
*/
|
|
223
|
+
export declare const MAX_RESUME_REENTRIES = 2;
|
|
216
224
|
/**
|
|
217
225
|
* Complete state for a single issue
|
|
218
226
|
*/
|
|
@@ -342,6 +350,15 @@ export declare const IssueStateSchema: z.ZodObject<{
|
|
|
342
350
|
token: z.ZodString;
|
|
343
351
|
originCwd: z.ZodString;
|
|
344
352
|
}, z.core.$strip>>;
|
|
353
|
+
autoWait: z.ZodOptional<z.ZodObject<{
|
|
354
|
+
wakeAt: z.ZodString;
|
|
355
|
+
phase: z.ZodString;
|
|
356
|
+
}, z.core.$strip>>;
|
|
357
|
+
windowHalt: z.ZodOptional<z.ZodObject<{
|
|
358
|
+
resumeAt: z.ZodString;
|
|
359
|
+
phase: z.ZodString;
|
|
360
|
+
reentries: z.ZodNumber;
|
|
361
|
+
}, z.core.$strip>>;
|
|
345
362
|
resolvedAt: z.ZodOptional<z.ZodString>;
|
|
346
363
|
lastActivity: z.ZodString;
|
|
347
364
|
createdAt: z.ZodString;
|
|
@@ -482,6 +499,15 @@ export declare const WorkflowStateSchema: z.ZodObject<{
|
|
|
482
499
|
token: z.ZodString;
|
|
483
500
|
originCwd: z.ZodString;
|
|
484
501
|
}, z.core.$strip>>;
|
|
502
|
+
autoWait: z.ZodOptional<z.ZodObject<{
|
|
503
|
+
wakeAt: z.ZodString;
|
|
504
|
+
phase: z.ZodString;
|
|
505
|
+
}, z.core.$strip>>;
|
|
506
|
+
windowHalt: z.ZodOptional<z.ZodObject<{
|
|
507
|
+
resumeAt: z.ZodString;
|
|
508
|
+
phase: z.ZodString;
|
|
509
|
+
reentries: z.ZodNumber;
|
|
510
|
+
}, z.core.$strip>>;
|
|
485
511
|
resolvedAt: z.ZodOptional<z.ZodString>;
|
|
486
512
|
lastActivity: z.ZodString;
|
|
487
513
|
createdAt: z.ZodString;
|
|
@@ -197,6 +197,14 @@ export const RelayStateSchema = z.object({
|
|
|
197
197
|
startedAt: z.string().datetime(),
|
|
198
198
|
messageCount: z.number().int().nonnegative(),
|
|
199
199
|
});
|
|
200
|
+
/**
|
|
201
|
+
* Maximum scheduler re-entries per window-halted issue (#892 AC-3). Mirrors
|
|
202
|
+
* `AUTO_WAIT_MAX_WAITS` (#804): both bound how often sequant returns to the
|
|
203
|
+
* same closed rate-limit window before declaring the halt terminal. Lives
|
|
204
|
+
* here — beside the `windowHalt.reentries` field it bounds — so display
|
|
205
|
+
* surfaces (`sequant status`) can import it without pulling in the run path.
|
|
206
|
+
*/
|
|
207
|
+
export const MAX_RESUME_REENTRIES = 2;
|
|
200
208
|
/**
|
|
201
209
|
* Complete state for a single issue
|
|
202
210
|
*/
|
|
@@ -248,6 +256,45 @@ export const IssueStateSchema = z.object({
|
|
|
248
256
|
originCwd: z.string(),
|
|
249
257
|
})
|
|
250
258
|
.optional(),
|
|
259
|
+
/**
|
|
260
|
+
* An in-progress auto-wait (#860): the phase is deliberately paused until a
|
|
261
|
+
* rate-limit window reopens at `wakeAt`. Written at wait start and cleared
|
|
262
|
+
* on wake, so `sequant status` can say "waiting until 07:01" instead of
|
|
263
|
+
* showing an hours-stale in-progress phase indistinguishable from a hang
|
|
264
|
+
* (#856). The write itself also refreshes `state.json`'s mtime — the
|
|
265
|
+
* activity proxy several liveness surfaces poll.
|
|
266
|
+
*/
|
|
267
|
+
autoWait: z
|
|
268
|
+
.object({
|
|
269
|
+
/** ISO timestamp of the scheduled wake (resetsAt + buffer). */
|
|
270
|
+
wakeAt: z.string().datetime(),
|
|
271
|
+
/** Phase that is paused. */
|
|
272
|
+
phase: z.string(),
|
|
273
|
+
})
|
|
274
|
+
.optional(),
|
|
275
|
+
/**
|
|
276
|
+
* A durable halt on an exhausted rate-limit window (#892): the run exited
|
|
277
|
+
* cleanly (lock released) and can be re-entered by `sequant resume` once
|
|
278
|
+
* the window reopens at `resumeAt`. Unlike `autoWait` (a live in-process
|
|
279
|
+
* pause), this record survives reboots — it is the machine-readable
|
|
280
|
+
* contract between the halt and an unattended scheduler re-entry.
|
|
281
|
+
* Written at a waitable-window halt, cleared on any phase success or a
|
|
282
|
+
* non-window failure (the halt cause is then no longer waitable).
|
|
283
|
+
*/
|
|
284
|
+
windowHalt: z
|
|
285
|
+
.object({
|
|
286
|
+
/** ISO timestamp after which re-entry can proceed (resetsAt + buffer). */
|
|
287
|
+
resumeAt: z.string().datetime(),
|
|
288
|
+
/** Phase that halted. */
|
|
289
|
+
phase: z.string(),
|
|
290
|
+
/**
|
|
291
|
+
* Re-entry attempts consumed (#892 AC-3). Incremented by `sequant
|
|
292
|
+
* resume` before each re-entry; a window that never reopens is bounded
|
|
293
|
+
* by MAX_RESUME_REENTRIES instead of ping-ponging a scheduler.
|
|
294
|
+
*/
|
|
295
|
+
reentries: z.number().int().min(0),
|
|
296
|
+
})
|
|
297
|
+
.optional(),
|
|
251
298
|
/** When the issue transitioned to a terminal status (merged/abandoned/closed) */
|
|
252
299
|
resolvedAt: z.string().datetime().optional(),
|
|
253
300
|
/** Most recent activity timestamp */
|