sequant 2.7.0 → 2.9.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 +18 -2
- package/dist/bin/cli.d.ts +1 -1
- package/dist/bin/cli.js +12 -2
- package/dist/bin/preflight.d.ts +21 -0
- package/dist/bin/preflight.js +45 -0
- package/dist/marketplace/external_plugins/sequant/.claude-plugin/plugin.json +1 -1
- package/dist/marketplace/external_plugins/sequant/README.md +2 -0
- package/dist/marketplace/external_plugins/sequant/hooks/post-tool.sh +18 -3
- package/dist/marketplace/external_plugins/sequant/hooks/pre-tool.sh +330 -57
- package/dist/marketplace/external_plugins/sequant/skills/_shared/references/force-push.md +34 -0
- package/dist/marketplace/external_plugins/sequant/skills/assess/SKILL.md +117 -19
- package/dist/marketplace/external_plugins/sequant/skills/assess/references/predicted-collision-detection.md +9 -6
- package/dist/marketplace/external_plugins/sequant/skills/exec/SKILL.md +29 -0
- package/dist/marketplace/external_plugins/sequant/skills/fullsolve/SKILL.md +1 -1
- package/dist/marketplace/external_plugins/sequant/skills/loop/SKILL.md +100 -2
- package/dist/marketplace/external_plugins/sequant/skills/qa/SKILL.md +24 -0
- package/dist/marketplace/external_plugins/sequant/skills/qa/references/anti-pattern-detection.md +285 -0
- package/dist/marketplace/external_plugins/sequant/skills/qa/references/call-site-review.md +202 -0
- package/dist/marketplace/external_plugins/sequant/skills/qa/references/quality-gates.md +287 -0
- package/dist/marketplace/external_plugins/sequant/skills/qa/references/test-quality-checklist.md +272 -0
- package/dist/marketplace/external_plugins/sequant/skills/qa/references/testing-requirements.md +40 -0
- package/dist/marketplace/external_plugins/sequant/skills/qa/scripts/quality-checks.sh +95 -11
- package/dist/marketplace/external_plugins/sequant/skills/references/shared/framework-gotchas.md +186 -0
- package/dist/marketplace/external_plugins/sequant/skills/reflect/SKILL.md +27 -13
- package/dist/marketplace/external_plugins/sequant/skills/reflect/references/documentation-tiers.md +80 -68
- package/dist/marketplace/external_plugins/sequant/skills/reflect/references/phase-reflection.md +31 -15
- package/dist/marketplace/external_plugins/sequant/skills/release/SKILL.md +669 -0
- package/dist/marketplace/external_plugins/sequant/skills/spec/references/verification-criteria.md +1 -1
- package/dist/marketplace/external_plugins/sequant/skills/test/references/browser-testing-patterns.md +423 -0
- package/dist/marketplace/external_plugins/sequant/skills/upstream/SKILL.md +419 -0
- package/dist/src/commands/logs.js +6 -1
- package/dist/src/commands/run-display.d.ts +20 -0
- package/dist/src/commands/run-display.js +80 -1
- package/dist/src/commands/stats.js +47 -0
- package/dist/src/lib/assess-collision-detect.d.ts +19 -2
- package/dist/src/lib/assess-collision-detect.js +68 -4
- package/dist/src/lib/cli-ui/run-renderer.js +17 -9
- package/dist/src/lib/errors.d.ts +91 -0
- package/dist/src/lib/errors.js +118 -0
- package/dist/src/lib/manifest.js +1 -17
- package/dist/src/lib/version-check.d.ts +19 -0
- package/dist/src/lib/version-check.js +45 -5
- package/dist/src/lib/workflow/batch-executor.d.ts +13 -0
- package/dist/src/lib/workflow/batch-executor.js +142 -24
- package/dist/src/lib/workflow/chain-preflight.d.ts +89 -0
- package/dist/src/lib/workflow/chain-preflight.js +199 -0
- package/dist/src/lib/workflow/chain-resume.d.ts +116 -0
- package/dist/src/lib/workflow/chain-resume.js +166 -0
- package/dist/src/lib/workflow/dependency-markers.d.ts +29 -0
- package/dist/src/lib/workflow/dependency-markers.js +79 -0
- package/dist/src/lib/workflow/drivers/agent-driver.d.ts +17 -0
- package/dist/src/lib/workflow/drivers/claude-code.d.ts +29 -0
- package/dist/src/lib/workflow/drivers/claude-code.js +136 -8
- package/dist/src/lib/workflow/error-classifier.d.ts +9 -2
- package/dist/src/lib/workflow/error-classifier.js +14 -1
- package/dist/src/lib/workflow/log-writer.d.ts +1 -1
- package/dist/src/lib/workflow/log-writer.js +6 -8
- package/dist/src/lib/workflow/metrics-schema.d.ts +39 -0
- package/dist/src/lib/workflow/metrics-schema.js +16 -0
- package/dist/src/lib/workflow/metrics-writer.d.ts +2 -1
- package/dist/src/lib/workflow/phase-executor.d.ts +50 -0
- package/dist/src/lib/workflow/phase-executor.js +151 -17
- package/dist/src/lib/workflow/run-log-schema.d.ts +26 -0
- package/dist/src/lib/workflow/run-log-schema.js +52 -1
- package/dist/src/lib/workflow/run-orchestrator.d.ts +14 -0
- package/dist/src/lib/workflow/run-orchestrator.js +291 -30
- package/dist/src/lib/workflow/state-manager.d.ts +1 -0
- package/dist/src/lib/workflow/state-manager.js +6 -0
- package/dist/src/lib/workflow/state-schema.d.ts +3 -0
- package/dist/src/lib/workflow/state-schema.js +7 -0
- package/dist/src/lib/workflow/status-derivation.d.ts +30 -0
- package/dist/src/lib/workflow/status-derivation.js +27 -0
- package/dist/src/lib/workflow/types.d.ts +40 -0
- package/dist/src/lib/workflow/worktree-manager.d.ts +43 -1
- package/dist/src/lib/workflow/worktree-manager.js +103 -33
- package/dist/src/mcp/tools/run.d.ts +2 -0
- package/dist/src/mcp/tools/run.js +2 -0
- package/dist/src/ui/tui/theme.d.ts +18 -4
- package/dist/src/ui/tui/theme.js +18 -4
- package/package.json +5 -6
- package/templates/hooks/post-tool.sh +18 -3
- package/templates/hooks/pre-tool.sh +330 -57
- package/templates/scripts/cleanup-worktree.sh +103 -14
- package/templates/skills/_shared/references/force-push.md +34 -0
- package/templates/skills/assess/SKILL.md +117 -19
- package/templates/skills/assess/references/predicted-collision-detection.md +9 -6
- package/templates/skills/exec/SKILL.md +29 -0
- package/templates/skills/fullsolve/SKILL.md +1 -1
- package/templates/skills/loop/SKILL.md +100 -2
- package/templates/skills/qa/SKILL.md +24 -0
- package/templates/skills/qa/references/anti-pattern-detection.md +285 -0
- package/templates/skills/qa/references/call-site-review.md +202 -0
- package/templates/skills/qa/references/quality-gates.md +287 -0
- package/templates/skills/qa/references/test-quality-checklist.md +272 -0
- package/templates/skills/qa/references/testing-requirements.md +40 -0
- package/templates/skills/qa/scripts/quality-checks.sh +95 -11
- package/templates/skills/references/shared/framework-gotchas.md +186 -0
- package/templates/skills/reflect/SKILL.md +27 -13
- package/templates/skills/reflect/references/documentation-tiers.md +80 -68
- package/templates/skills/reflect/references/phase-reflection.md +31 -15
- package/templates/skills/release/SKILL.md +669 -0
- package/templates/skills/spec/references/verification-criteria.md +1 -1
- package/templates/skills/test/references/browser-testing-patterns.md +423 -0
- package/templates/skills/upstream/SKILL.md +419 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chain resume planning (#760).
|
|
3
|
+
*
|
|
4
|
+
* When a `--chain` run fails mid-way, earlier links may already be complete
|
|
5
|
+
* (`ready_for_merge`) with a checkpoint commit on their feature branch
|
|
6
|
+
* (`createCheckpointCommit`, worktree-manager.ts). Re-running the same chain
|
|
7
|
+
* should skip that completed prefix and resume at the first incomplete link,
|
|
8
|
+
* rebased onto the last completed link's committed tip — NOT `main` (which is
|
|
9
|
+
* the #748 wrong-base failure this reuses the #748 rebase path to avoid).
|
|
10
|
+
*
|
|
11
|
+
* The existing pre-flight guard (`run-orchestrator.ts`) already drops
|
|
12
|
+
* `ready_for_merge`/`merged` issues from the run, but it is chain-unaware:
|
|
13
|
+
* dropping the completed prefix leaves the first incomplete link at index 0,
|
|
14
|
+
* where `executeSequential`'s successor-rebase never fires, so it silently
|
|
15
|
+
* builds on `main`. This module computes a *chain-correct* resume plan that
|
|
16
|
+
* preserves the completed prefix as the resume base.
|
|
17
|
+
*
|
|
18
|
+
* The planner is pure over an injected {@link CompletedLinkResolver} so the
|
|
19
|
+
* skip/fail-fast state machine (AC-3) is unit-testable without real git; the
|
|
20
|
+
* real-git rebase is covered by the integration test.
|
|
21
|
+
*/
|
|
22
|
+
/** A completed link that will be skipped (not re-executed) on resume. */
|
|
23
|
+
export interface ChainResumeSkip {
|
|
24
|
+
issueNumber: number;
|
|
25
|
+
/** Why it was skipped — the terminal-ish status that made it complete. */
|
|
26
|
+
status: "ready_for_merge" | "merged";
|
|
27
|
+
/** The link's local feature branch (from state), if known. */
|
|
28
|
+
branch?: string;
|
|
29
|
+
/** The link's worktree path (from state), if known — used for the dirty check. */
|
|
30
|
+
worktree?: string;
|
|
31
|
+
}
|
|
32
|
+
/** The computed plan for resuming a partially-completed chain. */
|
|
33
|
+
export interface ChainResumePlan {
|
|
34
|
+
/** Contiguous completed prefix that will be skipped (not re-executed). */
|
|
35
|
+
skipped: ChainResumeSkip[];
|
|
36
|
+
/** Issue numbers to actually execute (the incomplete tail). */
|
|
37
|
+
active: number[];
|
|
38
|
+
/**
|
|
39
|
+
* Local branch (or base ref) the first active link must be provisioned from
|
|
40
|
+
* and rebased onto. Undefined on a fresh run (no completed prefix).
|
|
41
|
+
*/
|
|
42
|
+
resumeBase?: string;
|
|
43
|
+
/** Commit the resume base resolves to — reported so the user sees the point. */
|
|
44
|
+
resumeBaseCommit?: string;
|
|
45
|
+
/** First incomplete issue number (the resume point), if any. */
|
|
46
|
+
resumeIssue?: number;
|
|
47
|
+
/**
|
|
48
|
+
* Set when resume cannot proceed safely (AC-3): a `ready_for_merge` link's
|
|
49
|
+
* branch/checkpoint is gone and its tip is unreconstructable. The caller must
|
|
50
|
+
* abort rather than silently execute the successor on the wrong base.
|
|
51
|
+
*/
|
|
52
|
+
failFast?: string;
|
|
53
|
+
/** True when every link in the chain is already complete. */
|
|
54
|
+
allComplete: boolean;
|
|
55
|
+
}
|
|
56
|
+
/** Resolves the git refs a resume base depends on. Injected for testability. */
|
|
57
|
+
export interface CompletedLinkResolver {
|
|
58
|
+
/**
|
|
59
|
+
* Resolve a local branch ref to its tip commit SHA, or undefined if the
|
|
60
|
+
* branch does not exist (destroyed worktree/branch — the AC-3 fail-fast case).
|
|
61
|
+
*/
|
|
62
|
+
resolveBranchTip(branch: string): string | undefined;
|
|
63
|
+
/** Resolve the base branch tip (for merged-resume reporting). */
|
|
64
|
+
resolveBaseTip(): string | undefined;
|
|
65
|
+
/**
|
|
66
|
+
* True iff the worktree has uncommitted changes (tracked or non-ignored
|
|
67
|
+
* untracked). Used to detect a resume base whose checkpoint never landed —
|
|
68
|
+
* see the dirty-tip fail-fast in {@link computeChainResumePlan}. Must return
|
|
69
|
+
* false (not throw) when the worktree is gone or unreadable; branch-tip
|
|
70
|
+
* resolution governs that case instead.
|
|
71
|
+
*/
|
|
72
|
+
isWorktreeDirty(worktreePath: string): boolean;
|
|
73
|
+
}
|
|
74
|
+
/** An ordered chain link with its persisted state (status + branch + worktree). */
|
|
75
|
+
export interface ChainLinkState {
|
|
76
|
+
issueNumber: number;
|
|
77
|
+
status?: string;
|
|
78
|
+
branch?: string;
|
|
79
|
+
worktree?: string;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Compute a chain-correct resume plan.
|
|
83
|
+
*
|
|
84
|
+
* Peels the *contiguous* completed prefix off the front of the chain (chain
|
|
85
|
+
* mode breaks on the first failure, so completed links always form a prefix).
|
|
86
|
+
* A completed link that appears *after* an incomplete one is left in `active`
|
|
87
|
+
* and re-executed — the conservative choice, since automatic skipping must not
|
|
88
|
+
* silently drop an issue the user intended to redo.
|
|
89
|
+
*
|
|
90
|
+
* @param orderedLinks Chain links in execution order, with persisted state.
|
|
91
|
+
* @param baseBranch The run's base branch (resume base when the prefix merged).
|
|
92
|
+
* @param resolver Git-ref resolver (injected for tests).
|
|
93
|
+
*/
|
|
94
|
+
export declare function computeChainResumePlan(orderedLinks: ChainLinkState[], baseBranch: string, resolver: CompletedLinkResolver): ChainResumePlan;
|
|
95
|
+
/** The subset of persisted issue state the resume planner reads. */
|
|
96
|
+
export interface PersistedLinkState {
|
|
97
|
+
status?: string;
|
|
98
|
+
branch?: string;
|
|
99
|
+
worktree?: string;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Read each link's persisted state and compute the resume plan.
|
|
103
|
+
*
|
|
104
|
+
* Split out of `run-orchestrator.ts` so the state-reading half of resume — in
|
|
105
|
+
* particular "a state lookup failure must treat the link as *incomplete*", the
|
|
106
|
+
* conservative choice that re-executes rather than skipping on bad data — is
|
|
107
|
+
* testable without standing up an orchestrator.
|
|
108
|
+
*
|
|
109
|
+
* @param issueNumbers Chain issues in execution order.
|
|
110
|
+
* @param baseBranch The run's base branch.
|
|
111
|
+
* @param getIssueState Reads persisted state for one issue (may reject).
|
|
112
|
+
* @param resolver Git-ref resolver.
|
|
113
|
+
* @param onStateError Called when a lookup throws; the link is then treated as
|
|
114
|
+
* incomplete (no status), so it stays in `active`.
|
|
115
|
+
*/
|
|
116
|
+
export declare function planChainResumeFromState(issueNumbers: number[], baseBranch: string, getIssueState: (issueNumber: number) => Promise<PersistedLinkState | null | undefined>, resolver: CompletedLinkResolver, onStateError?: (issueNumber: number, error: unknown) => void): Promise<ChainResumePlan>;
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chain resume planning (#760).
|
|
3
|
+
*
|
|
4
|
+
* When a `--chain` run fails mid-way, earlier links may already be complete
|
|
5
|
+
* (`ready_for_merge`) with a checkpoint commit on their feature branch
|
|
6
|
+
* (`createCheckpointCommit`, worktree-manager.ts). Re-running the same chain
|
|
7
|
+
* should skip that completed prefix and resume at the first incomplete link,
|
|
8
|
+
* rebased onto the last completed link's committed tip — NOT `main` (which is
|
|
9
|
+
* the #748 wrong-base failure this reuses the #748 rebase path to avoid).
|
|
10
|
+
*
|
|
11
|
+
* The existing pre-flight guard (`run-orchestrator.ts`) already drops
|
|
12
|
+
* `ready_for_merge`/`merged` issues from the run, but it is chain-unaware:
|
|
13
|
+
* dropping the completed prefix leaves the first incomplete link at index 0,
|
|
14
|
+
* where `executeSequential`'s successor-rebase never fires, so it silently
|
|
15
|
+
* builds on `main`. This module computes a *chain-correct* resume plan that
|
|
16
|
+
* preserves the completed prefix as the resume base.
|
|
17
|
+
*
|
|
18
|
+
* The planner is pure over an injected {@link CompletedLinkResolver} so the
|
|
19
|
+
* skip/fail-fast state machine (AC-3) is unit-testable without real git; the
|
|
20
|
+
* real-git rebase is covered by the integration test.
|
|
21
|
+
*/
|
|
22
|
+
const COMPLETED_STATUSES = new Set(["ready_for_merge", "merged"]);
|
|
23
|
+
/**
|
|
24
|
+
* Compute a chain-correct resume plan.
|
|
25
|
+
*
|
|
26
|
+
* Peels the *contiguous* completed prefix off the front of the chain (chain
|
|
27
|
+
* mode breaks on the first failure, so completed links always form a prefix).
|
|
28
|
+
* A completed link that appears *after* an incomplete one is left in `active`
|
|
29
|
+
* and re-executed — the conservative choice, since automatic skipping must not
|
|
30
|
+
* silently drop an issue the user intended to redo.
|
|
31
|
+
*
|
|
32
|
+
* @param orderedLinks Chain links in execution order, with persisted state.
|
|
33
|
+
* @param baseBranch The run's base branch (resume base when the prefix merged).
|
|
34
|
+
* @param resolver Git-ref resolver (injected for tests).
|
|
35
|
+
*/
|
|
36
|
+
export function computeChainResumePlan(orderedLinks, baseBranch, resolver) {
|
|
37
|
+
const skipped = [];
|
|
38
|
+
let firstIncomplete = 0;
|
|
39
|
+
for (const link of orderedLinks) {
|
|
40
|
+
if (link.status && COMPLETED_STATUSES.has(link.status)) {
|
|
41
|
+
skipped.push({
|
|
42
|
+
issueNumber: link.issueNumber,
|
|
43
|
+
status: link.status,
|
|
44
|
+
branch: link.branch,
|
|
45
|
+
worktree: link.worktree,
|
|
46
|
+
});
|
|
47
|
+
firstIncomplete++;
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const allNumbers = orderedLinks.map((l) => l.issueNumber);
|
|
54
|
+
// No completed prefix → fresh run, nothing to resume.
|
|
55
|
+
if (skipped.length === 0) {
|
|
56
|
+
return { skipped: [], active: allNumbers, allComplete: false };
|
|
57
|
+
}
|
|
58
|
+
// Every link already complete → nothing to execute.
|
|
59
|
+
if (firstIncomplete >= orderedLinks.length) {
|
|
60
|
+
return { skipped, active: [], allComplete: true };
|
|
61
|
+
}
|
|
62
|
+
const active = allNumbers.slice(firstIncomplete);
|
|
63
|
+
const last = skipped[skipped.length - 1];
|
|
64
|
+
let resumeBase;
|
|
65
|
+
let resumeBaseCommit;
|
|
66
|
+
if (last.status === "merged") {
|
|
67
|
+
// The completed prefix's work is in the merged base (origin/main). Provision
|
|
68
|
+
// the first incomplete link from the base branch — the same base a fresh
|
|
69
|
+
// first link would use — rather than a local tip that squash-merge may have
|
|
70
|
+
// orphaned (never rebase a successor onto a stale post-merge local tip).
|
|
71
|
+
resumeBase = baseBranch;
|
|
72
|
+
resumeBaseCommit = resolver.resolveBaseTip();
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
// ready_for_merge: the checkpoint tip lives only on the local feature
|
|
76
|
+
// branch. If that branch is gone (worktree/branch destroyed mid-way), the
|
|
77
|
+
// tip is unreconstructable — fail fast instead of wrong-basing the
|
|
78
|
+
// successor onto main (which would miss the completed link's work).
|
|
79
|
+
if (!last.branch) {
|
|
80
|
+
return {
|
|
81
|
+
skipped,
|
|
82
|
+
active,
|
|
83
|
+
resumeIssue: active[0],
|
|
84
|
+
allComplete: false,
|
|
85
|
+
failFast: `#${last.issueNumber} is ready_for_merge but no branch is recorded in state — ` +
|
|
86
|
+
`cannot reconstruct the resume base. Re-run with --force to redo the chain from scratch.`,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
const tip = resolver.resolveBranchTip(last.branch);
|
|
90
|
+
if (!tip) {
|
|
91
|
+
return {
|
|
92
|
+
skipped,
|
|
93
|
+
active,
|
|
94
|
+
resumeIssue: active[0],
|
|
95
|
+
allComplete: false,
|
|
96
|
+
failFast: `#${last.issueNumber} is ready_for_merge but its branch "${last.branch}" no longer exists — ` +
|
|
97
|
+
`the resume base is unreconstructable. Re-run with --force to redo the chain from scratch.`,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
// The branch exists, but a tip is only a valid resume base if it actually
|
|
101
|
+
// contains the link's work. `createCheckpointCommit` sweeps trailing
|
|
102
|
+
// uncommitted changes into a checkpoint commit; when it fails (commit hook,
|
|
103
|
+
// staging error, or unrelated dirty files) the status was *already* written
|
|
104
|
+
// as ready_for_merge, so this link still reads as a completed prefix while
|
|
105
|
+
// its tip is missing work. Rebasing the successor onto it would be exactly
|
|
106
|
+
// the silent wrong-base execution AC-3 forbids — fail fast instead.
|
|
107
|
+
if (last.worktree && resolver.isWorktreeDirty(last.worktree)) {
|
|
108
|
+
return {
|
|
109
|
+
skipped,
|
|
110
|
+
active,
|
|
111
|
+
resumeIssue: active[0],
|
|
112
|
+
allComplete: false,
|
|
113
|
+
failFast: `#${last.issueNumber} is ready_for_merge but its worktree has uncommitted changes — ` +
|
|
114
|
+
`its checkpoint commit never landed, so branch "${last.branch}" is missing that work and ` +
|
|
115
|
+
`resuming #${active[0]} here would build on an incomplete base. Commit them in ${last.worktree}, ` +
|
|
116
|
+
`or re-run with --force to redo the chain from scratch.`,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
resumeBase = last.branch;
|
|
120
|
+
resumeBaseCommit = tip;
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
skipped,
|
|
124
|
+
active,
|
|
125
|
+
resumeBase,
|
|
126
|
+
resumeBaseCommit,
|
|
127
|
+
resumeIssue: active[0],
|
|
128
|
+
allComplete: false,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Read each link's persisted state and compute the resume plan.
|
|
133
|
+
*
|
|
134
|
+
* Split out of `run-orchestrator.ts` so the state-reading half of resume — in
|
|
135
|
+
* particular "a state lookup failure must treat the link as *incomplete*", the
|
|
136
|
+
* conservative choice that re-executes rather than skipping on bad data — is
|
|
137
|
+
* testable without standing up an orchestrator.
|
|
138
|
+
*
|
|
139
|
+
* @param issueNumbers Chain issues in execution order.
|
|
140
|
+
* @param baseBranch The run's base branch.
|
|
141
|
+
* @param getIssueState Reads persisted state for one issue (may reject).
|
|
142
|
+
* @param resolver Git-ref resolver.
|
|
143
|
+
* @param onStateError Called when a lookup throws; the link is then treated as
|
|
144
|
+
* incomplete (no status), so it stays in `active`.
|
|
145
|
+
*/
|
|
146
|
+
export async function planChainResumeFromState(issueNumbers, baseBranch, getIssueState, resolver, onStateError) {
|
|
147
|
+
const orderedLinks = [];
|
|
148
|
+
for (const issueNumber of issueNumbers) {
|
|
149
|
+
let status;
|
|
150
|
+
let branch;
|
|
151
|
+
let worktree;
|
|
152
|
+
try {
|
|
153
|
+
const issueState = await getIssueState(issueNumber);
|
|
154
|
+
status = issueState?.status;
|
|
155
|
+
branch = issueState?.branch;
|
|
156
|
+
worktree = issueState?.worktree;
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
// Unknown state → treat as incomplete and re-execute. Skipping on a
|
|
160
|
+
// failed lookup could silently drop an issue the user meant to run.
|
|
161
|
+
onStateError?.(issueNumber, error);
|
|
162
|
+
}
|
|
163
|
+
orderedLinks.push({ issueNumber, status, branch, worktree });
|
|
164
|
+
}
|
|
165
|
+
return computeChainResumePlan(orderedLinks, baseBranch, resolver);
|
|
166
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared parser for line-leading dependency markers in an issue body (#767).
|
|
3
|
+
*
|
|
4
|
+
* Two callers extract issue numbers from `depends on #N` / `blocked by #N`
|
|
5
|
+
* declarations:
|
|
6
|
+
*
|
|
7
|
+
* - `chain-preflight.ts:parseDeclaredBlockers` — warn-only pre-flight, honors
|
|
8
|
+
* BOTH `depends on` and `blocked by`.
|
|
9
|
+
* - `batch-executor.ts:parseDependencies` — feeds `sortByDependencies`, which
|
|
10
|
+
* *silently reorders the run*, so it honors ONLY `depends on`.
|
|
11
|
+
*
|
|
12
|
+
* The hardened mechanics (line anchoring, required `#`, code/comment stripping)
|
|
13
|
+
* were introduced for the pre-flight in #762/PR #764 and lived only in
|
|
14
|
+
* `chain-preflight.ts`. #767 promotes them here so the sorter can adopt the same
|
|
15
|
+
* hardening without the two regexes drifting — while keeping the **marker set
|
|
16
|
+
* per-caller** so sharing the parser does NOT make the sorter start reordering
|
|
17
|
+
* on `blocked by` (a new, unrequested silent-reorder class; #762 Open Q #3).
|
|
18
|
+
*/
|
|
19
|
+
/** A dependency-declaration marker a caller opts into honoring. */
|
|
20
|
+
export type DepMarker = "depends on" | "blocked by";
|
|
21
|
+
/**
|
|
22
|
+
* Parse the issue numbers a body declares itself dependent on / blocked by,
|
|
23
|
+
* honoring only the requested `markers`. Deduped, order-preserving.
|
|
24
|
+
*
|
|
25
|
+
* Only line-leading markers count as declarations — see `buildMarkerRegex` for
|
|
26
|
+
* why mid-sentence prose mentions are deliberately ignored. Code blocks, inline
|
|
27
|
+
* code spans, and HTML comments are stripped first.
|
|
28
|
+
*/
|
|
29
|
+
export declare function parseBodyDependencyMarkers(body: string, markers: DepMarker[]): number[];
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared parser for line-leading dependency markers in an issue body (#767).
|
|
3
|
+
*
|
|
4
|
+
* Two callers extract issue numbers from `depends on #N` / `blocked by #N`
|
|
5
|
+
* declarations:
|
|
6
|
+
*
|
|
7
|
+
* - `chain-preflight.ts:parseDeclaredBlockers` — warn-only pre-flight, honors
|
|
8
|
+
* BOTH `depends on` and `blocked by`.
|
|
9
|
+
* - `batch-executor.ts:parseDependencies` — feeds `sortByDependencies`, which
|
|
10
|
+
* *silently reorders the run*, so it honors ONLY `depends on`.
|
|
11
|
+
*
|
|
12
|
+
* The hardened mechanics (line anchoring, required `#`, code/comment stripping)
|
|
13
|
+
* were introduced for the pre-flight in #762/PR #764 and lived only in
|
|
14
|
+
* `chain-preflight.ts`. #767 promotes them here so the sorter can adopt the same
|
|
15
|
+
* hardening without the two regexes drifting — while keeping the **marker set
|
|
16
|
+
* per-caller** so sharing the parser does NOT make the sorter start reordering
|
|
17
|
+
* on `blocked by` (a new, unrequested silent-reorder class; #762 Open Q #3).
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* Build the anchored marker regex for the requested marker set.
|
|
21
|
+
*
|
|
22
|
+
* Matches a declared marker, optionally bold-wrapped, colon-separated, and/or
|
|
23
|
+
* written as a list item, e.g. `- **Depends on**: #123`.
|
|
24
|
+
*
|
|
25
|
+
* Anchored to line start because a *declaration* is a line about the issue's
|
|
26
|
+
* own dependencies, whereas prose that merely mentions the marker mid-sentence
|
|
27
|
+
* is not. #762's own body is the motivating case: it contains both `...when #39
|
|
28
|
+
* says blocked by #38` and `...real markers like "Blocked by #36"` as examples,
|
|
29
|
+
* and an unanchored match reported #762 as blocked by #38 and #36 — exactly the
|
|
30
|
+
* false inference #604 says is worse than none. That matters most under
|
|
31
|
+
* `--strict-preflight`, where a bogus warning hard-aborts a legitimate chain.
|
|
32
|
+
*
|
|
33
|
+
* The `#` is required (unlike the historic looser `#?` in `parseDependencies`)
|
|
34
|
+
* so a line such as `Blocked by 5 days of review` — or the prose
|
|
35
|
+
* `Issue 14 depends on 12+13` — cannot parse as an issue number.
|
|
36
|
+
*/
|
|
37
|
+
function buildMarkerRegex(markers) {
|
|
38
|
+
const alternation = markers.map((m) => m.split(/\s+/).join("\\s+")).join("|");
|
|
39
|
+
return new RegExp(`^\\s*(?:[-*]\\s*)?\\*?\\*?(?:${alternation})\\*?\\*?:?\\s*#(\\d+)`, "gim");
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Strip fenced code blocks, inline code spans, and HTML comments so markers
|
|
43
|
+
* inside quoted shell snippets, documentation examples, or commented-out drafts
|
|
44
|
+
* don't count as real declarations. Inline spans are matched within a single
|
|
45
|
+
* line so an unbalanced backtick cannot swallow the rest of the body.
|
|
46
|
+
*
|
|
47
|
+
* Deliberately diverges from `assess-collision-detect.ts:stripCodeBlocksAndComments`,
|
|
48
|
+
* which keeps inline spans: its PATH_REGEX only matches backtick-wrapped paths,
|
|
49
|
+
* so stripping them there would find nothing. Same syntax, opposite meaning —
|
|
50
|
+
* a backticked marker here is an example, a backticked path there is the target.
|
|
51
|
+
* Keep the two separate (see PR #770).
|
|
52
|
+
*/
|
|
53
|
+
function stripCodeAndComments(body) {
|
|
54
|
+
return body
|
|
55
|
+
.replace(/```[\s\S]*?```/g, "")
|
|
56
|
+
.replace(/<!--[\s\S]*?-->/g, "")
|
|
57
|
+
.replace(/`[^`\n]*`/g, "");
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Parse the issue numbers a body declares itself dependent on / blocked by,
|
|
61
|
+
* honoring only the requested `markers`. Deduped, order-preserving.
|
|
62
|
+
*
|
|
63
|
+
* Only line-leading markers count as declarations — see `buildMarkerRegex` for
|
|
64
|
+
* why mid-sentence prose mentions are deliberately ignored. Code blocks, inline
|
|
65
|
+
* code spans, and HTML comments are stripped first.
|
|
66
|
+
*/
|
|
67
|
+
export function parseBodyDependencyMarkers(body, markers) {
|
|
68
|
+
if (markers.length === 0)
|
|
69
|
+
return [];
|
|
70
|
+
const cleaned = stripCodeAndComments(body);
|
|
71
|
+
const regex = buildMarkerRegex(markers);
|
|
72
|
+
const found = [];
|
|
73
|
+
for (const m of cleaned.matchAll(regex)) {
|
|
74
|
+
const n = parseInt(m[1], 10);
|
|
75
|
+
if (!isNaN(n) && !found.includes(n))
|
|
76
|
+
found.push(n);
|
|
77
|
+
}
|
|
78
|
+
return found;
|
|
79
|
+
}
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* Continue.dev, Copilot SDK, Cursor API) can be added by implementing this
|
|
6
6
|
* interface without touching orchestration logic.
|
|
7
7
|
*/
|
|
8
|
+
import type { SequantError } from "../../errors.js";
|
|
8
9
|
/**
|
|
9
10
|
* Resume handle for a previous agent session.
|
|
10
11
|
*
|
|
@@ -64,6 +65,22 @@ export interface AgentPhaseResult {
|
|
|
64
65
|
/** Driver-tagged resume handle for cwd-safe cross-phase resume (#674). */
|
|
65
66
|
resumeHandle?: ResumeHandle;
|
|
66
67
|
error?: string;
|
|
68
|
+
/**
|
|
69
|
+
* Set when the agent hit its `maxTurns` ceiling (`error_max_turns`). The
|
|
70
|
+
* `output` is partial-but-usable rather than a hard failure, so consumers
|
|
71
|
+
* can treat it as inconclusive/incomplete instead of discarding the work.
|
|
72
|
+
* See #733.
|
|
73
|
+
*/
|
|
74
|
+
capped?: boolean;
|
|
75
|
+
/**
|
|
76
|
+
* Typed error carrying structured cause data (#732). Set by drivers that can
|
|
77
|
+
* observe structured failure signals (e.g. ClaudeCodeDriver reading the SDK's
|
|
78
|
+
* `rate_limit_event` / assistant `error`). The executor prefers this over
|
|
79
|
+
* stderr-regex classification and uses its type to gate retry behavior (e.g.
|
|
80
|
+
* skipping the MCP fallback for non-retryable billing failures). Left
|
|
81
|
+
* undefined by drivers without structured signals (aider, subprocess paths).
|
|
82
|
+
*/
|
|
83
|
+
structuredError?: SequantError;
|
|
67
84
|
/** Last N lines of stderr captured via RingBuffer (#447) */
|
|
68
85
|
stderrTail?: string[];
|
|
69
86
|
/** Last N lines of stdout captured via RingBuffer (#447) */
|
|
@@ -28,6 +28,35 @@ export declare class ClaudeCodeDriver implements AgentDriver {
|
|
|
28
28
|
*/
|
|
29
29
|
canResume(handle: ResumeHandle, targetCwd: string): boolean;
|
|
30
30
|
executePhase(prompt: string, config: AgentExecutionConfig): Promise<AgentPhaseResult>;
|
|
31
|
+
/**
|
|
32
|
+
* Derive a typed {@link SequantError} from structured SDK failure signals
|
|
33
|
+
* (#732). Precedence: a captured `rate_limit_event` (richest signal) wins;
|
|
34
|
+
* otherwise the assistant-level `error`; otherwise the last `api_retry`
|
|
35
|
+
* error. Returns undefined when no rate-limit/billing signal was seen, so
|
|
36
|
+
* the executor falls back to stderr-regex classification.
|
|
37
|
+
*
|
|
38
|
+
* Exception: a non-retryable billing failure must never be downgraded to a
|
|
39
|
+
* retryable {@link RateLimitError}. If the `rate_limit_event` was only a
|
|
40
|
+
* transient throttle but the assistant separately reported `billing_error`,
|
|
41
|
+
* the billing cause wins — a retry cannot refill credits, and a
|
|
42
|
+
* RateLimitError would wrongly re-enable the retry / MCP-fallback path. When
|
|
43
|
+
* the `rate_limit_event` is itself a billing failure its richer metadata
|
|
44
|
+
* (`canUserPurchaseCredits`, etc.) is preserved.
|
|
45
|
+
*/
|
|
46
|
+
private buildStructuredError;
|
|
47
|
+
/**
|
|
48
|
+
* Map the SDK's assistant/api-retry error enum to a typed error. Only
|
|
49
|
+
* rate-limit / billing variants are mapped; other variants (auth, etc.)
|
|
50
|
+
* return undefined and defer to the existing classification path.
|
|
51
|
+
*
|
|
52
|
+
* The assistant-error channel carries no `resetsAt`/`rateLimitType` (the
|
|
53
|
+
* enum is the whole signal — `SDKAssistantMessageError` is a bare string),
|
|
54
|
+
* so the originating variant is recorded in metadata to keep `rate_limit`
|
|
55
|
+
* and `overloaded` distinguishable downstream (#761 AC-9). With no
|
|
56
|
+
* `resetsAt`, these errors are treated as transient by the executor's
|
|
57
|
+
* window-exhaustion check — the mandatory metadata-absent fallback.
|
|
58
|
+
*/
|
|
59
|
+
private errorFromAssistantError;
|
|
31
60
|
private buildResumeHandle;
|
|
32
61
|
isAvailable(): Promise<boolean>;
|
|
33
62
|
}
|