@dtlabs/ai-toolkit 0.16.8 → 0.17.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/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,25 @@
|
|
|
1
1
|
## 0.1.4 (2026-07-21)
|
|
2
|
+
## [0.17.0](https://github.com/dtlab-tech/ai-toolkit/compare/v0.16.9...v0.17.0) (2026-10-08)
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
* **task-executor:** consume replan's carry-over record on start ([f4bdbc1](https://github.com/dtlab-tech/ai-toolkit/commit/f4bdbc1da55a89e82196377a09ed66f274136185))
|
|
8
|
+
* **task-executor:** consume replan's carry-over record on start ([ee669fc](https://github.com/dtlab-tech/ai-toolkit/commit/ee669fc83a6eaf248358f4c3bf9087804074192e))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* **tests:** gate replan carry-over test on Windows only ([bd46109](https://github.com/dtlab-tech/ai-toolkit/commit/bd4610949537106734f6a47f40b2925d9a53c621))
|
|
14
|
+
|
|
15
|
+
## [0.16.9](https://github.com/dtlab-tech/ai-toolkit/compare/v0.16.8...v0.16.9) (2026-10-07)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Bug Fixes
|
|
19
|
+
|
|
20
|
+
* **task-executor:** let "skipped" satisfy a downstream dependency ([f00d3c6](https://github.com/dtlab-tech/ai-toolkit/commit/f00d3c68324c0ca7f1d8f965b15288027830fd01))
|
|
21
|
+
* **task-executor:** let "skipped" satisfy a downstream dependency ([6b48a61](https://github.com/dtlab-tech/ai-toolkit/commit/6b48a619e64f497b9b93e4a1a0254105eda1b718))
|
|
22
|
+
|
|
2
23
|
## [0.16.8](https://github.com/dtlab-tech/ai-toolkit/compare/v0.16.7...v0.16.8) (2026-10-07)
|
|
3
24
|
|
|
4
25
|
|
package/bin/cli.js
CHANGED
|
@@ -2642,6 +2642,7 @@ function _executorCurrentBranchRef(projectDir) {
|
|
|
2642
2642
|
// mismatch, not an I/O failure): SUCCESSOR_NOT_APPROVED,
|
|
2643
2643
|
// SUCCESSOR_PLAN_NOT_FOUND, PLAN_NOT_FOUND, PLAN_PARSE_ERROR,
|
|
2644
2644
|
// REPLAN_UNKNOWN_TASK_MAPPING, ATTEMPT_NUMBER_MISMATCH,
|
|
2645
|
+
// REPLAN_SOURCE_NOT_SUPERSEDED, REPLAN_PLAN_DIGEST_MISMATCH,
|
|
2645
2646
|
// STATE_READBACK_MISMATCH, STATE_NOT_FOUND, STATE_TASK_NOT_FOUND,
|
|
2646
2647
|
// STATE_ATTEMPT_NOT_FOUND, STATE_TASK_EXISTS, TREE_MISMATCH,
|
|
2647
2648
|
// PARENT_MISMATCH, COMMIT_REINSPECTION_MISMATCH,
|
|
@@ -2699,6 +2700,7 @@ const EXECUTOR_EXIT_CODE_BY_ERROR_CODE = {
|
|
|
2699
2700
|
// 4 — evidence / plan / approval mismatch
|
|
2700
2701
|
SUCCESSOR_NOT_APPROVED: 4, SUCCESSOR_PLAN_NOT_FOUND: 4, PLAN_NOT_FOUND: 4,
|
|
2701
2702
|
PLAN_PARSE_ERROR: 4, REPLAN_UNKNOWN_TASK_MAPPING: 4, ATTEMPT_NUMBER_MISMATCH: 4,
|
|
2703
|
+
REPLAN_SOURCE_NOT_SUPERSEDED: 4, REPLAN_PLAN_DIGEST_MISMATCH: 4,
|
|
2702
2704
|
STATE_READBACK_MISMATCH: 4, STATE_NOT_FOUND: 4, STATE_TASK_NOT_FOUND: 4,
|
|
2703
2705
|
STATE_ATTEMPT_NOT_FOUND: 4, STATE_TASK_EXISTS: 4, TREE_MISMATCH: 4,
|
|
2704
2706
|
PARENT_MISMATCH: 4, COMMIT_REINSPECTION_MISMATCH: 4, INTEGRATION_PARENT_MISMATCH: 4,
|
|
@@ -2838,6 +2840,7 @@ async function handleExecutorCommand(argv) {
|
|
|
2838
2840
|
claudePath: getFlag('--claude-path'),
|
|
2839
2841
|
taskTimeoutMs: taskTimeoutMsRaw === undefined ? undefined : Number(taskTimeoutMsRaw),
|
|
2840
2842
|
agentBudgetUsd: agentBudgetUsdRaw === undefined ? undefined : Number(agentBudgetUsdRaw),
|
|
2843
|
+
fromReplanRunId: getFlag('--from-replan-run-id'),
|
|
2841
2844
|
});
|
|
2842
2845
|
emitSuccess(result);
|
|
2843
2846
|
|
|
@@ -70,7 +70,69 @@ For the full flag reference, exit-code table, and JSON result schemas of every s
|
|
|
70
70
|
|
|
71
71
|
---
|
|
72
72
|
|
|
73
|
-
## 2.
|
|
73
|
+
## 2. Resolving the Claude CLI executable path
|
|
74
|
+
|
|
75
|
+
`--claude-path` has no documented discovery procedure anywhere in this toolkit —
|
|
76
|
+
`implement-feature`'s SKILL.md only says "ask the user if unknown," and this document, until now,
|
|
77
|
+
only showed `<resolved-claude-cli-path>` as an unexplained placeholder. Found twice independently
|
|
78
|
+
(once resolving it for a real run, once from a fresh install on a different machine reporting no
|
|
79
|
+
standalone `claude` executable anywhere on `PATH`): **try automated discovery first, then
|
|
80
|
+
validate, and only ask the user when discovery is inconclusive** — never silently guess, and
|
|
81
|
+
never trust a path (found or user-supplied) without confirming it actually runs.
|
|
82
|
+
|
|
83
|
+
**Case A — a standalone CLI install.** A bare `claude`/`claude.exe` reachable on `PATH`
|
|
84
|
+
(`where claude` on Windows, `which claude` on macOS/Linux). This is the common case for a
|
|
85
|
+
terminal-only install (e.g. `npm install -g @anthropic-ai/claude-code`).
|
|
86
|
+
|
|
87
|
+
**Case B — Claude Code running as a VS Code (or VS Code-family) extension, no standalone CLI on
|
|
88
|
+
`PATH`.** The extension caches its own Claude Agent SDK binary under the editor's own per-OS
|
|
89
|
+
user-data directory, at:
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
<editor-userdata-root>/agent-host/sdk-cache/claude/<sdk-version>/<platform>/node_modules/@anthropic-ai/claude-agent-sdk-<platform>/claude[.exe]
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
`<editor-userdata-root>` is Electron's standard per-OS application-data directory (not an
|
|
96
|
+
extension-specific `globalStorage` subfolder — `agent-host` sits directly under it, a sibling of
|
|
97
|
+
`User`/`Cache`/`logs`):
|
|
98
|
+
- Windows: `%APPDATA%\Code` — confirmed directly on a real machine:
|
|
99
|
+
`C:\Users\<user>\AppData\Roaming\Code\agent-host\sdk-cache\claude\0.3.220\win32-x64\node_modules\@anthropic-ai\claude-agent-sdk-win32-x64\claude.exe`
|
|
100
|
+
- macOS: `~/Library/Application Support/Code` (same relative layout, by VS Code's own standard
|
|
101
|
+
per-OS data-directory convention — not independently verified in this session)
|
|
102
|
+
- Linux: `~/.config/Code` (same caveat)
|
|
103
|
+
- For VS Code Insiders or another fork, substitute `Code` with that variant's own folder name
|
|
104
|
+
(e.g. `Code - Insiders`)
|
|
105
|
+
|
|
106
|
+
Discovery commands (search, do not assume a fixed version — the cached SDK version drifts with
|
|
107
|
+
editor/extension updates):
|
|
108
|
+
|
|
109
|
+
```powershell
|
|
110
|
+
# Windows
|
|
111
|
+
Get-ChildItem -Path "$env:APPDATA\Code\agent-host\sdk-cache\claude" -Recurse -Filter "claude.exe" -ErrorAction SilentlyContinue |
|
|
112
|
+
Select-Object -ExpandProperty FullName
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
# macOS/Linux
|
|
117
|
+
find "$HOME/Library/Application Support/Code/agent-host/sdk-cache/claude" \
|
|
118
|
+
"$HOME/.config/Code/agent-host/sdk-cache/claude" \
|
|
119
|
+
-iname "claude" -type f 2>/dev/null
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
**Validate before trusting any path, found or user-supplied** — this is exactly the kind of
|
|
123
|
+
unattended, potentially costly action (`executor start` spawns real, budget-consuming agent
|
|
124
|
+
processes) this toolkit's own Gate Protocol treats with caution elsewhere:
|
|
125
|
+
- Confirm it is a real, executable file, then run `<path> --version` to confirm it actually
|
|
126
|
+
responds as a Claude CLI — a matching filename is not evidence it works.
|
|
127
|
+
- If discovery finds **zero or more than one** candidate (e.g. several cached SDK versions from
|
|
128
|
+
past extension updates), do not guess which one — present every candidate found to the user and
|
|
129
|
+
have them confirm or choose, per SKILL.md's own "ask the user if unknown" rule. A single,
|
|
130
|
+
unambiguous candidate that passes the `--version` check may be used directly; anything less
|
|
131
|
+
certain goes back to the user before `executor start` ever runs.
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## 3. Windows-only E-02 qualification
|
|
74
136
|
|
|
75
137
|
The executor's process-supervision guarantees — tree-kill of in-flight attempts, tag-based
|
|
76
138
|
worker-liveness scanning, and immediate-stop confirmation — are proven for **Windows only**. This
|
|
@@ -93,7 +155,7 @@ all rather than degrading silently.
|
|
|
93
155
|
|
|
94
156
|
---
|
|
95
157
|
|
|
96
|
-
##
|
|
158
|
+
## 4. Persistence caveats
|
|
97
159
|
|
|
98
160
|
These are the real, already-confirmed caveats and limitations discovered during this feature's
|
|
99
161
|
own delivery. Each one is cited to its source rather than paraphrased from memory.
|
|
@@ -35,7 +35,7 @@ text-mode output in the current implementation.
|
|
|
35
35
|
### `start`
|
|
36
36
|
|
|
37
37
|
```bash
|
|
38
|
-
ai-toolkit executor start --project <path> --feature <path> [--max-concurrency N] [--claude-path <path>] [--task-timeout-ms <ms>] [--agent-budget-usd <amount>]
|
|
38
|
+
ai-toolkit executor start --project <path> --feature <path> [--max-concurrency N] [--claude-path <path>] [--task-timeout-ms <ms>] [--agent-budget-usd <amount>] [--from-replan-run-id <uuid>]
|
|
39
39
|
```
|
|
40
40
|
|
|
41
41
|
| Flag | Required | Default | Notes |
|
|
@@ -46,6 +46,7 @@ ai-toolkit executor start --project <path> --feature <path> [--max-concurrency N
|
|
|
46
46
|
| `--claude-path` | no | — | Absolute path to the qualified Claude CLI executable |
|
|
47
47
|
| `--task-timeout-ms` | no | — | Forwarded as a Number |
|
|
48
48
|
| `--agent-budget-usd` | no | — | Forwarded as a Number; persisted into run config, not itself enforced |
|
|
49
|
+
| `--from-replan-run-id` | no | — | UUID of a prior run `replan` has already superseded; seeds its carry-over-eligible tasks straight to `checkpointed` instead of `pending` (see "Carrying forward a replan" below) |
|
|
49
50
|
|
|
50
51
|
Backs `lib/task-executor/index.js`'s `execute()`. Validates gates/config, acquires the
|
|
51
52
|
repo-wide execution lease for a brand-new run, builds an immutable plan snapshot from the
|
|
@@ -74,6 +75,22 @@ the task's own `--project` directory — the executor's job is letting a develop
|
|
|
74
75
|
anywhere under that project, so (unlike pm-phase1/2's read-only-text workers) a narrower
|
|
75
76
|
allowlist is not an option here.
|
|
76
77
|
|
|
78
|
+
**Carrying forward a replan.** `replan` (below) computes which of an original run's completed
|
|
79
|
+
tasks are eligible to carry forward into a corrected successor plan (`carriesCompletion`,
|
|
80
|
+
commit-reachability based) and durably records that decision — but, on its own, never acts on
|
|
81
|
+
it: a plain `start` on the successor plan dispatches every task fresh from `pending`, discarding
|
|
82
|
+
the eligibility `replan` just computed. `--from-replan-run-id <uuid>` is the explicit way to
|
|
83
|
+
actually consume that record: given the UUID of a run `replan` has already superseded, `execute()`
|
|
84
|
+
validates that run really is `superseded`, reads its `replan` intent, and confirms that intent's
|
|
85
|
+
`successorPlanDigest` matches the plan this `start` call just parsed (`REPLAN_PLAN_DIGEST_MISMATCH`
|
|
86
|
+
otherwise, exit 4) — a prior run that was never actually `replan`-ed throws
|
|
87
|
+
`REPLAN_SOURCE_NOT_SUPERSEDED` (exit 4). Only then are the mapped `newTaskId`s whose
|
|
88
|
+
`carriesCompletion` is `true` seeded straight to `checkpointed`, with a synthetic attempt carrying
|
|
89
|
+
the original task's already-reachable `originalSha` (never re-verified, never re-dispatched in the
|
|
90
|
+
successor run — `terminalReason` records the exact old run/task/attempt provenance for later
|
|
91
|
+
`diagnose`/audit). Never inferred/auto-detected: omitting the flag (the default) carries nothing
|
|
92
|
+
forward, exactly like today.
|
|
93
|
+
|
|
77
94
|
### `status`
|
|
78
95
|
|
|
79
96
|
```bash
|
|
@@ -218,7 +235,10 @@ way. Validates the caller-supplied old→new task mapping against both the origi
|
|
|
218
235
|
tasks and the successor's plan snapshot, computes carry-completion eligibility per pair
|
|
219
236
|
(read-only), refuses if any original-run attempt's worker liveness is not confirmed dead
|
|
220
237
|
(`LIVE_WORKER_BLOCKS_REPLAN`), then records the mapping as a durable intent and marks the
|
|
221
|
-
original run `superseded`. Never deletes or rewrites any task/attempt evidence.
|
|
238
|
+
original run `superseded`. Never deletes or rewrites any task/attempt evidence. The record this
|
|
239
|
+
writes is not write-only forever: `start`'s own `--from-replan-run-id` (above) is the explicit
|
|
240
|
+
consumer — pass this run's own `runId` there on the successor's `start` call to actually carry the
|
|
241
|
+
eligible tasks forward, instead of the eligibility decision going unused.
|
|
222
242
|
|
|
223
243
|
---
|
|
224
244
|
|
|
@@ -2715,6 +2715,14 @@ function createSlotPool(maxConcurrency) {
|
|
|
2715
2715
|
* @param {string[]} [args.reviewSpawnArgs] - Test-only override of the full
|
|
2716
2716
|
* CLI args array forwarded to every runReview call (see runReview's own
|
|
2717
2717
|
* args.spawnArgs). Omitted in production.
|
|
2718
|
+
* @param {string} [args.fromReplanRunId] - UUID of a prior run that
|
|
2719
|
+
* replan() has already superseded, whose carry-over-eligible tasks
|
|
2720
|
+
* (taskMapping entries with carriesCompletion: true) should be seeded
|
|
2721
|
+
* directly as 'checkpointed' in this new run instead of 'pending' — never
|
|
2722
|
+
* inferred/auto-detected, always an explicit operator decision. The
|
|
2723
|
+
* referenced run's own replan record must have been computed against
|
|
2724
|
+
* EXACTLY this run's plan (successorPlanDigest must match), or this call
|
|
2725
|
+
* fails closed before minting a new run at all.
|
|
2718
2726
|
* @returns {Promise<object>} Versioned result object:
|
|
2719
2727
|
* { protocolVersion, runId, runStatus, tasks: [{ taskId, status }] }.
|
|
2720
2728
|
* @throws {Error} DISPATCH_VALIDATION_ERROR for malformed maxConcurrency;
|
|
@@ -2723,9 +2731,14 @@ function createSlotPool(maxConcurrency) {
|
|
|
2723
2731
|
* detached with no explicit args.taskRef; PLAN_NOT_FOUND when this
|
|
2724
2732
|
* feature's own Work-Breakdown.md/.csv cannot be read; PLAN_PARSE_ERROR
|
|
2725
2733
|
* propagated unmodified from plan.createPlanSnapshot; LEASE_HELD when a
|
|
2726
|
-
* repo-wide execution lease is already held;
|
|
2727
|
-
*
|
|
2728
|
-
*
|
|
2734
|
+
* repo-wide execution lease is already held; REPLAN_SOURCE_NOT_SUPERSEDED
|
|
2735
|
+
* when args.fromReplanRunId's own run is not runStatus 'superseded';
|
|
2736
|
+
* REPLAN_PLAN_DIGEST_MISMATCH when its replan record was computed against
|
|
2737
|
+
* a different plan than the one this call just parsed; STATE_NOT_FOUND/
|
|
2738
|
+
* STATE_CORRUPTED/INTENT_NOT_FOUND/INTENT_CORRUPTED propagated unmodified
|
|
2739
|
+
* while reading args.fromReplanRunId's state/replan record; any error
|
|
2740
|
+
* propagated unmodified from dispatchTaskAttempt/runVerifications/
|
|
2741
|
+
* runReview/git.js/store.js for a systemic (non-task-attempt) failure.
|
|
2729
2742
|
*/
|
|
2730
2743
|
async function execute(args) {
|
|
2731
2744
|
// Platform qualification guard (US-08-TASK-BE-05) — the single most
|
|
@@ -2803,6 +2816,70 @@ async function execute(args) {
|
|
|
2803
2816
|
const commonDir = _resolveCommonGitDir(projectDir);
|
|
2804
2817
|
const executionRoot = path.join(commonDir, 'ai-toolkit', 'execution');
|
|
2805
2818
|
|
|
2819
|
+
// Carry-over from a prior replan() decision (US-06-TASK-BE-03's own
|
|
2820
|
+
// "does not auto-start successor" scope boundary — this is the previously
|
|
2821
|
+
// missing other half: an explicit, never-implicit way for a successor
|
|
2822
|
+
// execute() to actually consume what replan() already computed and
|
|
2823
|
+
// durably recorded, instead of that record being write-only forever).
|
|
2824
|
+
// Validated BEFORE minting a new runId/acquiring the lease — a bad
|
|
2825
|
+
// --from-replan-run-id must fail closed before any new run exists at all,
|
|
2826
|
+
// never after. `args.fromReplanRunId` is never inferred/auto-detected
|
|
2827
|
+
// (e.g. by scanning for a 'superseded' run whose replan record's
|
|
2828
|
+
// successorPlanDigest matches this plan) — same "never guess, always
|
|
2829
|
+
// explicit" discipline replan()'s own --task-mapping-json already
|
|
2830
|
+
// established; the operator names the exact source run.
|
|
2831
|
+
let carryOverByNewTaskId = null;
|
|
2832
|
+
if (args.fromReplanRunId !== undefined) {
|
|
2833
|
+
_requireNonEmptyString(args.fromReplanRunId, 'execute: args.fromReplanRunId');
|
|
2834
|
+
const originalRunId = args.fromReplanRunId;
|
|
2835
|
+
|
|
2836
|
+
// Propagates STATE_NOT_FOUND/STATE_CORRUPTED unmodified — a bad run-id
|
|
2837
|
+
// is never silently treated as "nothing carries forward".
|
|
2838
|
+
const originalState = store.readState(executionRoot, originalRunId);
|
|
2839
|
+
if (originalState.runStatus !== 'superseded') {
|
|
2840
|
+
throw _err(
|
|
2841
|
+
'REPLAN_SOURCE_NOT_SUPERSEDED',
|
|
2842
|
+
'execute: --from-replan-run-id "' + originalRunId + '" has runStatus "' + originalState.runStatus +
|
|
2843
|
+
'", not "superseded" — only a run replan() has already superseded may be used as a carry-over ' +
|
|
2844
|
+
'source (never a still-running, paused, or already-completed run)'
|
|
2845
|
+
);
|
|
2846
|
+
}
|
|
2847
|
+
|
|
2848
|
+
// Propagates INTENT_NOT_FOUND/INTENT_CORRUPTED unmodified — replan()
|
|
2849
|
+
// always writes this record BEFORE superseding (see its own step 7/8
|
|
2850
|
+
// ordering), so a superseded run with no readable record here is treated
|
|
2851
|
+
// as corrupted evidence, never as "nothing carries forward".
|
|
2852
|
+
const replanRecord = store.readIntent(executionRoot, originalRunId, 'replan');
|
|
2853
|
+
|
|
2854
|
+
if (replanRecord.successorPlanDigest !== snapshot.planDigest) {
|
|
2855
|
+
throw _err(
|
|
2856
|
+
'REPLAN_PLAN_DIGEST_MISMATCH',
|
|
2857
|
+
'execute: the Work Breakdown just parsed for this run (planDigest "' + snapshot.planDigest +
|
|
2858
|
+
'") does not match the successor plan replan() recorded for run "' + originalRunId +
|
|
2859
|
+
'" (planDigest "' + replanRecord.successorPlanDigest + '") — refusing to carry forward ' +
|
|
2860
|
+
'completions computed against a plan this run is not actually starting'
|
|
2861
|
+
);
|
|
2862
|
+
}
|
|
2863
|
+
|
|
2864
|
+
// Only entries the human/caller already approved as carrying forward
|
|
2865
|
+
// (replanRecord.taskMapping's own carriesCompletion, computed by
|
|
2866
|
+
// replan() from real commit-reachability — see its own doc comment) are
|
|
2867
|
+
// ever applied; every other mapped or unmapped task starts 'pending' and
|
|
2868
|
+
// is dispatched normally.
|
|
2869
|
+
carryOverByNewTaskId = new Map();
|
|
2870
|
+
replanRecord.taskMapping.forEach(function (m) {
|
|
2871
|
+
if (m.carriesCompletion !== true) return;
|
|
2872
|
+
const oldTask = originalState.tasks[m.oldTaskId];
|
|
2873
|
+
const oldAttempt = oldTask.attempts[oldTask.attempts.length - 1];
|
|
2874
|
+
carryOverByNewTaskId.set(m.newTaskId, {
|
|
2875
|
+
oldTaskId: m.oldTaskId,
|
|
2876
|
+
oldAttemptNumber: oldAttempt.number,
|
|
2877
|
+
originalSha: oldAttempt.originalSha,
|
|
2878
|
+
integratedSha: oldAttempt.integratedSha,
|
|
2879
|
+
});
|
|
2880
|
+
});
|
|
2881
|
+
}
|
|
2882
|
+
|
|
2806
2883
|
const runId = crypto.randomUUID();
|
|
2807
2884
|
// Fail closed if already held — this is a brand-new run; reclaiming a
|
|
2808
2885
|
// stale lease is an explicit reconcile/resume/replan decision, never an
|
|
@@ -2836,6 +2913,34 @@ async function execute(args) {
|
|
|
2836
2913
|
snapshot.tasks.forEach(function (task) {
|
|
2837
2914
|
state = state.addTask(task.id, { dependencies: task.dependsOn });
|
|
2838
2915
|
});
|
|
2916
|
+
|
|
2917
|
+
// Seed carry-over tasks straight to 'checkpointed' with a synthetic
|
|
2918
|
+
// attempt recording the ORIGINAL run's already-reachable commit SHA —
|
|
2919
|
+
// never dispatched, never re-verified in this run, exactly mirroring
|
|
2920
|
+
// what a real successful N=1 completion already looks like
|
|
2921
|
+
// (finalizeTaskCheckpoint also only ever registers originalSha, never
|
|
2922
|
+
// integratedSha, for the N=1 case this executor supports today — copied
|
|
2923
|
+
// here verbatim, usually null, never fabricated). 'checkpointed' (not
|
|
2924
|
+
// 'skipped' — that status means "verified+reviewed in THIS run and
|
|
2925
|
+
// found to need no change", a different, honest outcome) is already
|
|
2926
|
+
// terminal-success everywhere that matters: the nonTerminalIds filter,
|
|
2927
|
+
// allTerminalSuccess, and the readyTaskId dependency-satisfaction check
|
|
2928
|
+
// all already treat it as such, unchanged. terminalReason documents the
|
|
2929
|
+
// provenance for any later diagnose/audit — this task has zero real
|
|
2930
|
+
// attempts in the CURRENT run otherwise.
|
|
2931
|
+
if (carryOverByNewTaskId) {
|
|
2932
|
+
carryOverByNewTaskId.forEach(function (carry, newTaskId) {
|
|
2933
|
+
if (!state.tasks[newTaskId]) return; // replan() already validated newTaskId exists; defensive only
|
|
2934
|
+
state = state.addAttempt(newTaskId, { stage: 'committed' });
|
|
2935
|
+
state = state.updateAttempt(newTaskId, 1, {
|
|
2936
|
+
originalSha: carry.originalSha,
|
|
2937
|
+
integratedSha: carry.integratedSha,
|
|
2938
|
+
terminalReason: 'carried-over-from-replan:' + args.fromReplanRunId + ':' + carry.oldTaskId + '#' + carry.oldAttemptNumber,
|
|
2939
|
+
});
|
|
2940
|
+
state = state.setTaskStatus(newTaskId, 'checkpointed');
|
|
2941
|
+
});
|
|
2942
|
+
}
|
|
2943
|
+
|
|
2839
2944
|
store.writeState(executionRoot, runId, state);
|
|
2840
2945
|
|
|
2841
2946
|
const tasksById = new Map(snapshot.tasks.map(function (t) { return [t.id, t]; }));
|
|
@@ -2877,7 +2982,17 @@ async function execute(args) {
|
|
|
2877
2982
|
if (t.status !== 'pending') return false;
|
|
2878
2983
|
return t.dependencies.every(function (depId) {
|
|
2879
2984
|
const dep = state.tasks[depId];
|
|
2880
|
-
|
|
2985
|
+
// 'skipped' must satisfy a dependency exactly like 'checkpointed'/
|
|
2986
|
+
// 'integrated' do — it is already treated as terminal success by
|
|
2987
|
+
// both the nonTerminalIds filter just above and the allTerminalSuccess
|
|
2988
|
+
// check below (and by reconcile's _classifyTask). Omitting it here
|
|
2989
|
+
// left any task depending on a 'skipped' one stuck 'pending' forever,
|
|
2990
|
+
// even though the rest of this run correctly treated 'skipped' as a
|
|
2991
|
+
// completed, successful outcome — confirmed against a real run: both
|
|
2992
|
+
// tasks that reached 'skipped' (the Bug 4 no-op-checkpoint fix) had
|
|
2993
|
+
// downstream dependents that never got scheduled, blocking the
|
|
2994
|
+
// entire remaining 64-task run.
|
|
2995
|
+
return dep && (dep.status === 'checkpointed' || dep.status === 'integrated' || dep.status === 'skipped');
|
|
2881
2996
|
});
|
|
2882
2997
|
});
|
|
2883
2998
|
|
|
@@ -3782,7 +3897,10 @@ function _hasGate2Approval(approvalsText) {
|
|
|
3782
3897
|
*
|
|
3783
3898
|
* Does NOT dispatch, start, or touch the successor run in any way (per its
|
|
3784
3899
|
* own scope — see the module-level file comment's original stub doc, which
|
|
3785
|
-
* this replaces).
|
|
3900
|
+
* this replaces). The record this writes is NOT write-only forever, though:
|
|
3901
|
+
* execute()'s own args.fromReplanRunId explicitly consumes it, seeding any
|
|
3902
|
+
* carriesCompletion:true mapping straight to 'checkpointed' in the
|
|
3903
|
+
* successor run — see execute()'s own doc comment.
|
|
3786
3904
|
*
|
|
3787
3905
|
* @param {object} args
|
|
3788
3906
|
* @param {string} args.executionRoot - Absolute path to
|
package/package.json
CHANGED
|
@@ -248,7 +248,11 @@ ai-toolkit executor start \
|
|
|
248
248
|
|
|
249
249
|
Placeholders:
|
|
250
250
|
- `<path-to-feature.md>` — the feature.md path passed to this skill
|
|
251
|
-
- `<resolved-claude-cli-path>` — the Claude CLI executable path on this host;
|
|
251
|
+
- `<resolved-claude-cli-path>` — the Claude CLI executable path on this host; try automated
|
|
252
|
+
discovery first (`docs/task-executor-bootstrap.md` section 2 — covers both a standalone CLI on
|
|
253
|
+
`PATH` and the VS Code-extension-cached SDK binary case), validate any candidate with
|
|
254
|
+
`--version` before trusting it, and ask the user only when discovery is inconclusive or finds
|
|
255
|
+
more than one candidate
|
|
252
256
|
- `<task-timeout-ms>` and `<agent-budget-usd>` — must be supplied explicitly; there is no hidden default timeout or spend
|
|
253
257
|
|
|
254
258
|
**If the host's ordinary terminal capability can launch a durable external command**, launch
|