@dtlabs/ai-toolkit 0.11.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +65 -0
- package/bin/cli.js +757 -68
- package/lib/agent-registry.js +1018 -0
- package/lib/execution-ledger.js +609 -0
- package/package.json +1 -1
- package/src/claude/agents/{concurrency-safety-assessment.md → gaia-concurrency-safety-assessment.md} +1 -1
- package/src/claude/agents/{define-feature.md → gaia-define-feature.md} +37 -61
- package/src/claude/agents/{dependency-injection-refactoring.md → gaia-dependency-injection-refactoring.md} +1 -1
- package/src/claude/agents/{dependency-supply-chain-security.md → gaia-dependency-supply-chain-security.md} +1 -1
- package/src/claude/agents/{developer-backend.md → gaia-developer-backend.md} +1 -1
- package/src/claude/agents/{developer-frontend.md → gaia-developer-frontend.md} +1 -1
- package/src/claude/agents/{developer-testing.md → gaia-developer-testing.md} +1 -1
- package/src/claude/agents/{domain-model-refactoring.md → gaia-domain-model-refactoring.md} +1 -1
- package/src/claude/agents/{generate-requirements.md → gaia-generate-requirements.md} +1 -1
- package/src/claude/agents/{generate-tech-spec.md → gaia-generate-tech-spec.md} +1 -1
- package/src/claude/agents/{generate-work-breakdown.md → gaia-generate-work-breakdown.md} +5 -5
- package/src/claude/agents/{generic-software-assessment.md → gaia-generic-software-assessment.md} +1 -1
- package/src/claude/agents/{god-class-decomposition.md → gaia-god-class-decomposition.md} +1 -1
- package/src/claude/agents/{init-agents-md.md → gaia-init-agents-md.md} +1 -1
- package/src/claude/agents/{install-toolkit.md → gaia-install-toolkit.md} +1 -1
- package/src/claude/agents/{intervention-documentation-standard.md → gaia-intervention-documentation-standard.md} +1 -1
- package/src/claude/agents/{layered-architecture-assessment.md → gaia-layered-architecture-assessment.md} +1 -1
- package/src/claude/agents/{review-solution.md → gaia-review-solution.md} +1 -1
- package/src/claude/agents/{security-hardening.md → gaia-security-hardening.md} +1 -1
- package/src/claude/agents/{validate-feature-docs.md → gaia-validate-feature-docs.md} +2 -2
- package/src/claude/agents/{validate-work-breakdown-semantic.md → gaia-validate-work-breakdown-semantic.md} +1 -1
- package/src/claude/commands/next-task.md +4 -4
- package/src/claude/scripts/wb-render.js +101 -2
- package/src/claude/scripts/wb-validate.js +32 -2
- package/src/claude/skills/assess-codebase/SKILL.md +1 -1
- package/src/claude/skills/define-feature/SKILL.md +2 -2
- package/src/claude/skills/hi-gaia/SKILL.md +1 -1
- package/src/claude/skills/implement-feature/SKILL.md +190 -10
- package/src/claude/skills/init-agents/SKILL.md +2 -2
- package/src/claude/skills/install-toolkit/SKILL.md +2 -2
- package/src/claude/workflows/pm-phase1.js +94 -73
- package/src/claude/workflows/pm-phase2.js +118 -108
- package/src/claude/workflows/pm-phase3.js +219 -75
|
@@ -14,17 +14,98 @@ with their declared `model:` frontmatter honoured and producing accurate per-age
|
|
|
14
14
|
|
|
15
15
|
---
|
|
16
16
|
|
|
17
|
+
## Step 0 — Agent Preflight (fail-closed, AC-19)
|
|
18
|
+
|
|
19
|
+
Before starting the pipeline, verify that every pipeline agent is installed and verified.
|
|
20
|
+
|
|
21
|
+
1. **Derive the feature prefix** from the `<path-to-feature.md>` argument (e.g. path contains `FTR-017` → prefix = `FTR-017`). Derive the ledger directory as the directory containing the feature.md file.
|
|
22
|
+
|
|
23
|
+
2. **Open the preflight ledger entry** (fail-closed — if this fails, do NOT proceed):
|
|
24
|
+
```bash
|
|
25
|
+
ai-toolkit ledger open \
|
|
26
|
+
--prefix {PREFIX} \
|
|
27
|
+
--agent agent-preflight:implement-feature \
|
|
28
|
+
--phase phase3 \
|
|
29
|
+
--dir {LEDGER_DIR}
|
|
30
|
+
```
|
|
31
|
+
If the command exits non-zero, **STOP IMMEDIATELY** — report the error and do not continue.
|
|
32
|
+
|
|
33
|
+
3. **Run the preflight check:**
|
|
34
|
+
```bash
|
|
35
|
+
ai-toolkit agents preflight --project . --pipeline implement-feature
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
4. **On success** (exit 0): close the ledger entry:
|
|
39
|
+
```bash
|
|
40
|
+
ai-toolkit ledger close \
|
|
41
|
+
--prefix {PREFIX} \
|
|
42
|
+
--agent agent-preflight:implement-feature \
|
|
43
|
+
--dir {LEDGER_DIR}
|
|
44
|
+
```
|
|
45
|
+
Then continue to Step 1.
|
|
46
|
+
|
|
47
|
+
5. **On failure** (exit non-zero): mark the ledger entry failed and STOP:
|
|
48
|
+
```bash
|
|
49
|
+
ai-toolkit ledger fail \
|
|
50
|
+
--prefix {PREFIX} \
|
|
51
|
+
--agent agent-preflight:implement-feature \
|
|
52
|
+
--error "agent preflight failed — one or more pipeline agents unverified" \
|
|
53
|
+
--dir {LEDGER_DIR}
|
|
54
|
+
```
|
|
55
|
+
Report the preflight error to the user. Do NOT continue to Step 1.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
17
59
|
## Step 1 — Invoke pm-phase1 (Documentation Phase)
|
|
18
60
|
|
|
19
|
-
|
|
61
|
+
### Step 1a — Tier 1 dispatch guard (AC-06, AC-07, AC-20)
|
|
20
62
|
|
|
63
|
+
Before dispatching, resolve and verify the pm-phase1 orchestrator:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
ai-toolkit agents resolve \
|
|
67
|
+
--project . \
|
|
68
|
+
--id gaia.orchestrator.feature.phase1 \
|
|
69
|
+
--require-verified
|
|
21
70
|
```
|
|
22
|
-
|
|
71
|
+
|
|
72
|
+
If exit non-zero → **HARD STOP**: report the error, do not dispatch pm-phase1.
|
|
73
|
+
|
|
74
|
+
Store the resolution record fields: `nativeName`, `sha256` (definitionHash), `scope` (resolutionScope), `toolkitVersion`.
|
|
75
|
+
|
|
76
|
+
Open the dispatch ledger entry (fail-closed):
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
ai-toolkit ledger open \
|
|
80
|
+
--prefix {PREFIX} \
|
|
81
|
+
--agent pm-phase1:dispatch \
|
|
82
|
+
--phase phase3 \
|
|
83
|
+
--dir {LEDGER_DIR} \
|
|
84
|
+
--metadata-json '{"agentId":"gaia.orchestrator.feature.phase1","nativeAgentName":"{nativeName}","platform":"claude","toolkitVersion":"{toolkitVersion}","resolutionScope":"{scope}","definitionHash":"{sha256}"}'
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
If exit non-zero → **HARD STOP** (fail-closed): do not dispatch.
|
|
88
|
+
|
|
89
|
+
### Step 1b — Invoke pm-phase1 using verified nativeName
|
|
90
|
+
|
|
91
|
+
Invoke the workflow using ONLY the resolved `nativeName` (never a hardcoded name):
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
subagent_type: {nativeName}
|
|
23
95
|
prompt: <path-to-feature.md>
|
|
24
96
|
```
|
|
25
97
|
|
|
26
98
|
Wait for the workflow to complete. Do NOT proceed until it returns.
|
|
27
99
|
|
|
100
|
+
After the workflow returns, close the dispatch ledger entry:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
ai-toolkit ledger close \
|
|
104
|
+
--prefix {PREFIX} --agent pm-phase1:dispatch --dir {LEDGER_DIR}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
If ledger close fails → **HARD STOP**: report the error and do not continue.
|
|
108
|
+
|
|
28
109
|
Extract from the result:
|
|
29
110
|
- `prefix` — feature prefix (e.g. `FTR-009`)
|
|
30
111
|
- `requirements.summary` — requirements file summary
|
|
@@ -93,10 +174,36 @@ If the file is missing or incomplete, write it again before proceeding.
|
|
|
93
174
|
|
|
94
175
|
## Step 3 — Invoke pm-phase2 (Work Breakdown Phase)
|
|
95
176
|
|
|
96
|
-
|
|
177
|
+
### Step 3a — Tier 1 dispatch guard
|
|
178
|
+
|
|
179
|
+
Resolve and verify pm-phase2:
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
ai-toolkit agents resolve \
|
|
183
|
+
--project . \
|
|
184
|
+
--id gaia.orchestrator.feature.phase2 \
|
|
185
|
+
--require-verified
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
If exit non-zero → **HARD STOP**: do not dispatch pm-phase2.
|
|
189
|
+
|
|
190
|
+
Open the dispatch ledger entry (fail-closed):
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
ai-toolkit ledger open \
|
|
194
|
+
--prefix {PREFIX} \
|
|
195
|
+
--agent pm-phase2:dispatch \
|
|
196
|
+
--phase phase3 \
|
|
197
|
+
--dir {LEDGER_DIR} \
|
|
198
|
+
--metadata-json '{"agentId":"gaia.orchestrator.feature.phase2","nativeAgentName":"{nativeName}","platform":"claude","toolkitVersion":"{toolkitVersion}","resolutionScope":"{scope}","definitionHash":"{sha256}"}'
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
If exit non-zero → **HARD STOP** (fail-closed).
|
|
202
|
+
|
|
203
|
+
### Step 3b — Invoke pm-phase2 using verified nativeName
|
|
97
204
|
|
|
98
205
|
```
|
|
99
|
-
subagent_type:
|
|
206
|
+
subagent_type: {nativeName}
|
|
100
207
|
prompt: <path-to-feature.md>
|
|
101
208
|
```
|
|
102
209
|
|
|
@@ -109,6 +216,15 @@ Wait for the workflow to complete. Extract from the result:
|
|
|
109
216
|
- `agent_estimate` — agent parallel estimate
|
|
110
217
|
- `token_ledger` — per-agent token data from phase 2
|
|
111
218
|
|
|
219
|
+
Close the dispatch ledger entry after the workflow returns:
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
ai-toolkit ledger close \
|
|
223
|
+
--prefix {PREFIX} --agent pm-phase2:dispatch --dir {LEDGER_DIR}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
If ledger close fails → **HARD STOP**: report the error. Do not continue to Gate 2. The error is not swallowed (AC-22).
|
|
227
|
+
|
|
112
228
|
---
|
|
113
229
|
|
|
114
230
|
## Step 4 — Present Gate 2 (HARD STOP — present in main loop)
|
|
@@ -179,10 +295,52 @@ git checkout feature/{PREFIX}-{short-slug}
|
|
|
179
295
|
Pre-condition check: read `{PREFIX}-Approvals.md` and verify BOTH Gate 1 ✅ and Gate 2 ✅.
|
|
180
296
|
If either is missing, return to the missing gate.
|
|
181
297
|
|
|
182
|
-
|
|
298
|
+
### Step 6a — Tier 1 dispatch guard (Gate 2 post-approval, AC-06, AC-07, AC-08)
|
|
299
|
+
|
|
300
|
+
After Gate 2 approval, resolve and verify pm-phase3 with `--require-verified`. Only a `verified` status permits dispatch:
|
|
301
|
+
|
|
302
|
+
```bash
|
|
303
|
+
ai-toolkit agents resolve \
|
|
304
|
+
--project . \
|
|
305
|
+
--id gaia.orchestrator.feature.phase3 \
|
|
306
|
+
--require-verified
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
If exit non-zero → **HARD STOP** (AC-08): pm-phase3 is absent or tampered. Report the error with remediation instructions:
|
|
183
310
|
|
|
184
311
|
```
|
|
185
|
-
|
|
312
|
+
⛔ HARD STOP — pm-phase3 cannot be dispatched.
|
|
313
|
+
|
|
314
|
+
Remediation:
|
|
315
|
+
1. Run: ai-toolkit agents resolve --project . --id gaia.orchestrator.feature.phase3
|
|
316
|
+
2. Check the status field in the output for the failure reason.
|
|
317
|
+
3. Re-install the toolkit if the status is not-installed or hash-mismatch.
|
|
318
|
+
4. Contact the toolkit team if the issue persists.
|
|
319
|
+
|
|
320
|
+
Do NOT use an alternative workflow name or any fallback. This pipeline requires verified agents.
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
Do NOT use any alternative workflow name or fallback.
|
|
324
|
+
|
|
325
|
+
Open the dispatch ledger entry (fail-closed):
|
|
326
|
+
|
|
327
|
+
```bash
|
|
328
|
+
ai-toolkit ledger open \
|
|
329
|
+
--prefix {PREFIX} \
|
|
330
|
+
--agent pm-phase3:dispatch \
|
|
331
|
+
--phase phase3 \
|
|
332
|
+
--dir {LEDGER_DIR} \
|
|
333
|
+
--metadata-json '{"agentId":"gaia.orchestrator.feature.phase3","nativeAgentName":"{nativeName}","platform":"claude","toolkitVersion":"{toolkitVersion}","resolutionScope":"{scope}","definitionHash":"{sha256}"}'
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
If exit non-zero → **HARD STOP** (fail-closed): do not dispatch.
|
|
337
|
+
|
|
338
|
+
### Step 6b — Invoke pm-phase3 using verified nativeName
|
|
339
|
+
|
|
340
|
+
Invoke the workflow using ONLY the resolved `nativeName` (never a hardcoded name):
|
|
341
|
+
|
|
342
|
+
```
|
|
343
|
+
subagent_type: {nativeName}
|
|
186
344
|
prompt: <path-to-feature.md> --branch feature/{PREFIX}-{short-slug}
|
|
187
345
|
```
|
|
188
346
|
|
|
@@ -194,6 +352,26 @@ Wait for the workflow to complete. Capture its full result, including the `<usag
|
|
|
194
352
|
|
|
195
353
|
If `issues_summary.escalations > 0`, report the escalation details to the user.
|
|
196
354
|
|
|
355
|
+
**If pm-phase3 returned an error or escalation:** mark the dispatch ledger entry failed before stopping:
|
|
356
|
+
|
|
357
|
+
```bash
|
|
358
|
+
ai-toolkit ledger fail \
|
|
359
|
+
--prefix {PREFIX} --agent pm-phase3:dispatch \
|
|
360
|
+
--error "pm-phase3 returned an error or escalation" \
|
|
361
|
+
--dir {LEDGER_DIR}
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
If `ledger fail` itself exits non-zero → **HARD STOP** (AC-22): report both the original workflow error and the ledger failure. The error is NOT swallowed.
|
|
365
|
+
|
|
366
|
+
**On successful completion:** close the dispatch ledger entry:
|
|
367
|
+
|
|
368
|
+
```bash
|
|
369
|
+
ai-toolkit ledger close \
|
|
370
|
+
--prefix {PREFIX} --agent pm-phase3:dispatch --dir {LEDGER_DIR}
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
If ledger close fails → **HARD STOP** (AC-22): report the error. The pipeline does not silently swallow ledger failures after dispatch returns.
|
|
374
|
+
|
|
197
375
|
---
|
|
198
376
|
|
|
199
377
|
## Step 7 — Complete Token Estimate file
|
|
@@ -204,8 +382,10 @@ From the `<usage>` block of the pm-phase3 result, read:
|
|
|
204
382
|
|
|
205
383
|
Read `{PREFIX}-Token-Estimate.md`. Append the orchestrator row and grand total:
|
|
206
384
|
|
|
385
|
+
> **Null-compatibility — actuals reading and cost calculation:** Treat a token value of `null`, `0`, or `not_available` as **data unavailable** — never as a real, observable zero consumption. Render unavailable values as `—` in the Actual tokens and Actual cost columns; exclude them from sums, averages, and grand totals. Preventing resume clobber and legacy misinterpretation requires that no unavailable measurement is coerced into a real zero.
|
|
386
|
+
|
|
207
387
|
```markdown
|
|
208
|
-
|
|
|
388
|
+
| pm-phase3 (orchestrator) | — | sonnet | 80,000 (estimated) | {subagent_tokens} (actual) | ±{delta} | {duration} |
|
|
209
389
|
|
|
210
390
|
---
|
|
211
391
|
|
|
@@ -213,14 +393,14 @@ Read `{PREFIX}-Token-Estimate.md`. Append the orchestrator row and grand total:
|
|
|
213
393
|
|
|
214
394
|
| Agent | Task / Scope | Model | Est. tokens | Actual tokens | Delta | Est. cost ($) | Actual cost ($) | Duration |
|
|
215
395
|
|-------|-------------|-------|------------|---------------|-------|--------------|----------------|----------|
|
|
216
|
-
{one row per entry in token_ledger — from phase 1, 2, and 3}
|
|
217
|
-
|
|
|
396
|
+
{one row per entry in token_ledger — from phase 1, 2, and 3; for any entry where actual tokens is null, 0, or not_available show — in Actual tokens and Actual cost and exclude that row from totals}
|
|
397
|
+
| pm-phase3 (orchestrator) | — | sonnet | 80,000 | {subagent_tokens} | ±{delta} | $0.4320 | ${actual_cost} | {duration} |
|
|
218
398
|
|
|
219
399
|
## Estimation accuracy by agent type
|
|
220
400
|
|
|
221
401
|
| Model | Count | Avg est. tokens | Avg actual tokens | Avg delta | Trend |
|
|
222
402
|
|-------|-------|----------------|------------------|-----------|-------|
|
|
223
|
-
{one row per model tier (haiku, sonnet) — exclude
|
|
403
|
+
{one row per model tier (haiku, sonnet) — exclude rows where actual tokens is null, 0, or not_available from averages}
|
|
224
404
|
|
|
225
405
|
## Grand Total
|
|
226
406
|
|
|
@@ -4,10 +4,10 @@ argument-hint: "[path-to-project-root]"
|
|
|
4
4
|
disable-model-invocation: true
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
Spawn the `init-agents-md` agent with the arguments provided by the user:
|
|
7
|
+
Spawn the `gaia-init-agents-md` agent with the arguments provided by the user:
|
|
8
8
|
|
|
9
9
|
```
|
|
10
|
-
subagent_type: init-agents-md
|
|
10
|
+
subagent_type: gaia-init-agents-md
|
|
11
11
|
prompt: [path-to-project-root]
|
|
12
12
|
```
|
|
13
13
|
|
|
@@ -4,10 +4,10 @@ argument-hint: "[path-to-destination] [--force]"
|
|
|
4
4
|
disable-model-invocation: true
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
Spawn the `install-toolkit` agent with the arguments provided by the user:
|
|
7
|
+
Spawn the `gaia-install-toolkit` agent with the arguments provided by the user:
|
|
8
8
|
|
|
9
9
|
```
|
|
10
|
-
subagent_type: install-toolkit
|
|
10
|
+
subagent_type: gaia-install-toolkit
|
|
11
11
|
prompt: <path-to-destination> [--force]
|
|
12
12
|
```
|
|
13
13
|
|
|
@@ -9,36 +9,51 @@ export const meta = {
|
|
|
9
9
|
],
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
// ──
|
|
12
|
+
// ── Tier 2 resolution guard ───────────────────────────────────────────────────
|
|
13
|
+
// All worker agents must be resolved via the CLI before dispatch.
|
|
14
|
+
// No direct require('lib/agent-registry') — resolution goes through the CLI facade.
|
|
15
|
+
const RESOLVE_SCHEMA = {
|
|
16
|
+
type: 'object',
|
|
17
|
+
properties: {
|
|
18
|
+
nativeName: { type: 'string' },
|
|
19
|
+
status: { type: 'string' },
|
|
20
|
+
agentId: { type: 'string' },
|
|
21
|
+
exitNonZero: { type: 'boolean' },
|
|
22
|
+
error: { type: 'string' },
|
|
23
|
+
},
|
|
24
|
+
}
|
|
13
25
|
|
|
14
|
-
async function
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
await agent(
|
|
19
|
-
`Append a JSON object to the ledger array at: ${ledgerPath}\n\n` +
|
|
20
|
-
`1. Run: date -u +"%Y-%m-%dT%H:%M:%SZ" and capture the output as NOW.\n` +
|
|
21
|
-
`2. Read the file. If it does not exist or cannot be parsed as a JSON array, start with [].\n` +
|
|
22
|
-
`3. Push this object onto the array, replacing "__TS__" in started_at with NOW: ${entryWithoutTs}\n` +
|
|
23
|
-
`4. Write the full array back (JSON, 2-space indent). Return no output.`,
|
|
24
|
-
{ label: 'append-ledger', phase: 'Requirements', model: 'haiku' }
|
|
26
|
+
async function resolveAgentTier2(agentId, label, phaseName) {
|
|
27
|
+
const result = await agent(
|
|
28
|
+
`Run this command via Bash:\n\nai-toolkit agents resolve --project . --id ${agentId} --require-verified\n\nIf the command exits 0: parse the JSON from stdout and return it with exitNonZero: false.\nIf the command exits non-zero: return {"exitNonZero": true, "error": "<stderr text>", "agentId": "${agentId}"}.`,
|
|
29
|
+
{ label, phase: phaseName, model: 'haiku', schema: RESOLVE_SCHEMA }
|
|
25
30
|
)
|
|
31
|
+
if (!result || result.exitNonZero || !result.nativeName) {
|
|
32
|
+
throw new Error(`HARD STOP — Tier 2 resolution failed for ${agentId}: ${result && result.error ? result.error : 'no nativeName returned'}`)
|
|
33
|
+
}
|
|
34
|
+
return result.nativeName
|
|
26
35
|
}
|
|
27
36
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
// ── Tier 3 — self-ledger helper ───────────────────────────────────────────────
|
|
38
|
+
const SELF_LEDGER_SCHEMA = {
|
|
39
|
+
type: 'object',
|
|
40
|
+
properties: {
|
|
41
|
+
exitCode: { type: 'number' },
|
|
42
|
+
stdout: { type: 'string' },
|
|
43
|
+
stderr: { type: 'string' },
|
|
44
|
+
},
|
|
45
|
+
required: ['exitCode'],
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function selfLedgerOp(cmd, label, phaseName) {
|
|
49
|
+
const result = await agent(
|
|
50
|
+
`Run this shell command via Bash and return the exit code as structured output.\n\nCommand: ${cmd}\n\nCapture: exitCode (integer), stdout (string), stderr (string). Return all three.`,
|
|
51
|
+
{ label, phase: phaseName, model: 'haiku', schema: SELF_LEDGER_SCHEMA }
|
|
41
52
|
)
|
|
53
|
+
const status = (result && typeof result.exitCode === 'number') ? result.exitCode : 1
|
|
54
|
+
if (status !== 0) {
|
|
55
|
+
throw new Error(`HARD STOP — self-ledger operation failed. Exit code: ${status}. Command: ${cmd}`)
|
|
56
|
+
}
|
|
42
57
|
}
|
|
43
58
|
|
|
44
59
|
// ── Parse args ────────────────────────────────────────────────────────────────
|
|
@@ -46,6 +61,17 @@ async function updateLedgerEntry(featureDir, prefix, agentKey, updates) {
|
|
|
46
61
|
const featurePath = args.split(/\s+/)[0]
|
|
47
62
|
const force = typeof args === 'string' && args.includes('--force')
|
|
48
63
|
|
|
64
|
+
// Derive prefix and feature dir early (before Discovery agent) for self-registration
|
|
65
|
+
const featureDirEarly = featurePath.replace(/[/\\][^/\\]+$/, '')
|
|
66
|
+
const prefixEarlyMatch = featureDirEarly.match(/([A-Z]+-\d+)/)
|
|
67
|
+
const prefixEarly = prefixEarlyMatch ? prefixEarlyMatch[1] : 'FTR-000'
|
|
68
|
+
|
|
69
|
+
// ── Tier 3 self-registration (open BEFORE any main logic, fail-closed) ────────
|
|
70
|
+
await selfLedgerOp(
|
|
71
|
+
`ai-toolkit ledger open --prefix ${prefixEarly} --agent pm-phase1:self --phase phase1 --dir "${featureDirEarly}" --attempt 1`,
|
|
72
|
+
'ledger-open-pm-phase1-self', 'Discovery'
|
|
73
|
+
)
|
|
74
|
+
|
|
49
75
|
// ── Discovery ─────────────────────────────────────────────────────────────────
|
|
50
76
|
phase('Discovery')
|
|
51
77
|
|
|
@@ -104,7 +130,7 @@ log(`needs_requirements=${discoveryResult.needs_requirements} needs_tech_spec=${
|
|
|
104
130
|
|
|
105
131
|
// ── Ensure ledger file exists (US-02-T02) ─────────────────────────────────────
|
|
106
132
|
// If define-feature was not used, the ledger file will not exist yet.
|
|
107
|
-
// Touch it with an empty array so
|
|
133
|
+
// Touch it with an empty array so the ledger facade can always assume a valid base.
|
|
108
134
|
const ledgerFilePath = `${feature_dir}/${prefix}-token-ledger.json`
|
|
109
135
|
await agent(
|
|
110
136
|
`Check whether the file ${ledgerFilePath} exists.\n\n1. Try to read the file using the Read tool.\n2. If the file does NOT exist: write it now using the Write tool with contents: []\n3. If the file already exists and is a valid JSON array: do nothing.\n4. If the file exists but is not valid JSON: overwrite it with: []\n5. Return no output.`,
|
|
@@ -119,24 +145,19 @@ const tokenLedger = []
|
|
|
119
145
|
const errors = []
|
|
120
146
|
|
|
121
147
|
if (discoveryResult.needs_requirements) {
|
|
148
|
+
const reqNativeName = await resolveAgentTier2('gaia.agent.planner.requirements', 'resolve-generate-requirements', 'Requirements')
|
|
122
149
|
const reqKey = 'generate-requirements:phase1'
|
|
123
|
-
await
|
|
124
|
-
agent
|
|
125
|
-
phase: '
|
|
126
|
-
|
|
127
|
-
status: 'running',
|
|
128
|
-
phase_delta_tokens: 0,
|
|
129
|
-
started_at: '__TS__',
|
|
130
|
-
completed_at: null,
|
|
131
|
-
})
|
|
150
|
+
await agent(
|
|
151
|
+
`Run this shell command via Bash. If the --dir path contains spaces, enclose it in double quotes.\n\nai-toolkit ledger open --dir ${feature_dir} --prefix ${prefix} --agent ${reqKey} --phase phase1 --model haiku --attempt 1\n\nReturn no output.`,
|
|
152
|
+
{ label: 'ledger-open-requirements', phase: 'Requirements', model: 'haiku' }
|
|
153
|
+
)
|
|
132
154
|
const beforeReq = budget.spent()
|
|
133
|
-
const reqResult = await agent(featurePath, { agentType:
|
|
155
|
+
const reqResult = await agent(featurePath, { agentType: reqNativeName, label: 'generate-requirements', phase: 'Requirements' })
|
|
134
156
|
const reqTokens = budget.spent() - beforeReq
|
|
135
|
-
await
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
})
|
|
157
|
+
await agent(
|
|
158
|
+
`Run this shell command via Bash. If the --dir path contains spaces, enclose it in double quotes.\n\nai-toolkit ledger close --dir ${feature_dir} --prefix ${prefix} --agent ${reqKey} --tokens ${reqTokens} --attempt 1\n\nReturn no output.`,
|
|
159
|
+
{ label: 'ledger-close-requirements', phase: 'Requirements', model: 'haiku' }
|
|
160
|
+
)
|
|
140
161
|
tokenLedger.push({ agent: 'generate-requirements', model: 'haiku', phase_delta_tokens: reqTokens })
|
|
141
162
|
log(`generate-requirements done — phase delta: ${reqTokens} tokens`)
|
|
142
163
|
} else {
|
|
@@ -147,24 +168,19 @@ if (discoveryResult.needs_requirements) {
|
|
|
147
168
|
phase('Tech-Spec')
|
|
148
169
|
|
|
149
170
|
if (discoveryResult.needs_tech_spec) {
|
|
171
|
+
const specNativeName = await resolveAgentTier2('gaia.agent.planner.tech-spec', 'resolve-generate-tech-spec', 'Tech-Spec')
|
|
150
172
|
const specKey = 'generate-tech-spec:phase1'
|
|
151
|
-
await
|
|
152
|
-
agent
|
|
153
|
-
phase: '
|
|
154
|
-
|
|
155
|
-
status: 'running',
|
|
156
|
-
phase_delta_tokens: 0,
|
|
157
|
-
started_at: '__TS__',
|
|
158
|
-
completed_at: null,
|
|
159
|
-
})
|
|
173
|
+
await agent(
|
|
174
|
+
`Run this shell command via Bash. If the --dir path contains spaces, enclose it in double quotes.\n\nai-toolkit ledger open --dir ${feature_dir} --prefix ${prefix} --agent ${specKey} --phase phase1 --model haiku --attempt 1\n\nReturn no output.`,
|
|
175
|
+
{ label: 'ledger-open-tech-spec', phase: 'Tech-Spec', model: 'haiku' }
|
|
176
|
+
)
|
|
160
177
|
const beforeSpec = budget.spent()
|
|
161
|
-
const specResult = await agent(featurePath, { agentType:
|
|
178
|
+
const specResult = await agent(featurePath, { agentType: specNativeName, label: 'generate-tech-spec', phase: 'Tech-Spec' })
|
|
162
179
|
const specTokens = budget.spent() - beforeSpec
|
|
163
|
-
await
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
})
|
|
180
|
+
await agent(
|
|
181
|
+
`Run this shell command via Bash. If the --dir path contains spaces, enclose it in double quotes.\n\nai-toolkit ledger close --dir ${feature_dir} --prefix ${prefix} --agent ${specKey} --tokens ${specTokens} --attempt 1\n\nReturn no output.`,
|
|
182
|
+
{ label: 'ledger-close-tech-spec', phase: 'Tech-Spec', model: 'haiku' }
|
|
183
|
+
)
|
|
168
184
|
tokenLedger.push({ agent: 'generate-tech-spec', model: 'haiku', phase_delta_tokens: specTokens })
|
|
169
185
|
log(`generate-tech-spec done — phase delta: ${specTokens} tokens`)
|
|
170
186
|
} else {
|
|
@@ -174,6 +190,11 @@ if (discoveryResult.needs_tech_spec) {
|
|
|
174
190
|
// ── validate-feature-docs (revision loop, max 3 cycles) ──────────────────────
|
|
175
191
|
phase('Validation')
|
|
176
192
|
|
|
193
|
+
// Resolve all validation-phase agents once before the loop (Tier 2 guard)
|
|
194
|
+
const valNativeName = await resolveAgentTier2('gaia.agent.planner.validate-feature-docs', 'resolve-validate-feature-docs', 'Validation')
|
|
195
|
+
const reqRevNativeName = await resolveAgentTier2('gaia.agent.planner.requirements', 'resolve-generate-requirements-rev', 'Validation')
|
|
196
|
+
const specRevNativeName = await resolveAgentTier2('gaia.agent.planner.tech-spec', 'resolve-generate-tech-spec-rev', 'Validation')
|
|
197
|
+
|
|
177
198
|
let validationSummary = 'skipped'
|
|
178
199
|
let lastValText = ''
|
|
179
200
|
const MAX_CYCLES = 3
|
|
@@ -181,23 +202,17 @@ const MAX_CYCLES = 3
|
|
|
181
202
|
for (let cycle = 1; cycle <= MAX_CYCLES; cycle++) {
|
|
182
203
|
log(`validate-feature-docs cycle ${cycle}`)
|
|
183
204
|
const valKey = `validate-feature-docs:phase1:cycle${cycle}`
|
|
184
|
-
await
|
|
185
|
-
agent
|
|
186
|
-
phase: '
|
|
187
|
-
|
|
188
|
-
status: 'running',
|
|
189
|
-
phase_delta_tokens: 0,
|
|
190
|
-
started_at: '__TS__',
|
|
191
|
-
completed_at: null,
|
|
192
|
-
})
|
|
205
|
+
await agent(
|
|
206
|
+
`Run this shell command via Bash. If the --dir path contains spaces, enclose it in double quotes.\n\nai-toolkit ledger open --dir ${feature_dir} --prefix ${prefix} --agent ${valKey} --phase phase1 --model haiku --attempt 1\n\nReturn no output.`,
|
|
207
|
+
{ label: `ledger-open-validate-cycle-${cycle}`, phase: 'Validation', model: 'haiku' }
|
|
208
|
+
)
|
|
193
209
|
const beforeVal = budget.spent()
|
|
194
|
-
const valResult = await agent(featurePath, { agentType:
|
|
210
|
+
const valResult = await agent(featurePath, { agentType: valNativeName, label: `validate-feature-docs (cycle ${cycle})`, phase: 'Validation' })
|
|
195
211
|
const valTokens = budget.spent() - beforeVal
|
|
196
|
-
await
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
})
|
|
212
|
+
await agent(
|
|
213
|
+
`Run this shell command via Bash. If the --dir path contains spaces, enclose it in double quotes.\n\nai-toolkit ledger close --dir ${feature_dir} --prefix ${prefix} --agent ${valKey} --tokens ${valTokens} --attempt 1\n\nReturn no output.`,
|
|
214
|
+
{ label: `ledger-close-validate-cycle-${cycle}`, phase: 'Validation', model: 'haiku' }
|
|
215
|
+
)
|
|
201
216
|
tokenLedger.push({ agent: `validate-feature-docs (cycle ${cycle})`, model: 'haiku', phase_delta_tokens: valTokens })
|
|
202
217
|
|
|
203
218
|
// valResult is the agent's text output — check for gap indicators
|
|
@@ -217,10 +232,10 @@ for (let cycle = 1; cycle <= MAX_CYCLES; cycle++) {
|
|
|
217
232
|
if (cycle < MAX_CYCLES) {
|
|
218
233
|
// Re-run failing docs before next validation cycle
|
|
219
234
|
if (resultText.includes('Requirements')) {
|
|
220
|
-
await agent(featurePath, { agentType:
|
|
235
|
+
await agent(featurePath, { agentType: reqRevNativeName, label: `generate-requirements (revision ${cycle})`, phase: 'Validation' })
|
|
221
236
|
}
|
|
222
237
|
if (resultText.includes('Tech-Spec')) {
|
|
223
|
-
await agent(featurePath, { agentType:
|
|
238
|
+
await agent(featurePath, { agentType: specRevNativeName, label: `generate-tech-spec (revision ${cycle})`, phase: 'Validation' })
|
|
224
239
|
}
|
|
225
240
|
} else {
|
|
226
241
|
validationSummary = 'gaps remain after 3 cycles'
|
|
@@ -339,5 +354,11 @@ const gate1Payload = {
|
|
|
339
354
|
errors,
|
|
340
355
|
}
|
|
341
356
|
|
|
357
|
+
// ── Tier 3 self-registration — close on successful completion ─────────────────
|
|
358
|
+
await selfLedgerOp(
|
|
359
|
+
`ai-toolkit ledger close --prefix ${prefix} --agent pm-phase1:self --dir "${feature_dir}" --attempt 1`,
|
|
360
|
+
'ledger-close-pm-phase1-self', 'Validation'
|
|
361
|
+
)
|
|
362
|
+
|
|
342
363
|
log(`pm-phase1 complete — gate1_payload ready`)
|
|
343
364
|
return gate1Payload
|