@dtlabs/ai-toolkit 0.23.0 → 0.23.2
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,19 @@
|
|
|
1
1
|
## 0.1.4 (2026-07-21)
|
|
2
|
+
## [0.23.2](https://github.com/dtlab-tech/ai-toolkit/compare/v0.23.1...v0.23.2) (2026-10-22)
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
### Bug Fixes
|
|
6
|
+
|
|
7
|
+
* **pm-phase2:** bound the WBS skeleton call's own Tech-Spec exploration ([6bca6b5](https://github.com/dtlab-tech/ai-toolkit/commit/6bca6b51f4d495b5912434f4ff83bdcefed2d979))
|
|
8
|
+
* **pm-phase2:** bound the WBS skeleton call's own Tech-Spec exploration (needsComponentDetail) ([804d2cc](https://github.com/dtlab-tech/ai-toolkit/commit/804d2ccf529e0e441b81f9e814a168a9a005a783))
|
|
9
|
+
|
|
10
|
+
## [0.23.1](https://github.com/dtlab-tech/ai-toolkit/compare/v0.23.0...v0.23.1) (2026-10-22)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* **task-executor:** disable MSYS argv path-conversion for verification commands ([00d66ee](https://github.com/dtlab-tech/ai-toolkit/commit/00d66eecb94f7b5464c58cbfec33eb1793e82664))
|
|
16
|
+
|
|
2
17
|
## [0.23.0](https://github.com/dtlab-tech/ai-toolkit/compare/v0.22.1...v0.23.0) (2026-10-22)
|
|
3
18
|
|
|
4
19
|
|
|
@@ -1056,6 +1056,21 @@ function _runVerificationCommand(command, cwd, timeoutMs) {
|
|
|
1056
1056
|
shell: false, // never true: shell:true on Windows silently runs cmd.exe, not Bash
|
|
1057
1057
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
1058
1058
|
windowsHide: true,
|
|
1059
|
+
// MSYS2_ARG_CONV_EXCL: this `bash` is MSYS2/git-bash on Windows, which by default
|
|
1060
|
+
// auto-converts any argument shaped like a POSIX path before handing it to a native
|
|
1061
|
+
// (non-MSYS-aware) child process. A verification command invoking a native tool with
|
|
1062
|
+
// leading-slash switches — e.g. MSBuild's `/p:Configuration=Release`, this project's own
|
|
1063
|
+
// documented build syntax for a .NET consumer — gets silently mangled: MSYS treats
|
|
1064
|
+
// `/p:Configuration=Release` as a POSIX-path-shaped argument, finds no matching drive for
|
|
1065
|
+
// `p:`, and strips the leading slash entirely, leaving MSBuild unable to recognize it as
|
|
1066
|
+
// a property switch (MSB1008: "Only one project can be specified"). Confirmed by direct
|
|
1067
|
+
// reproduction (`bash -c 'node dump-argv.js /p:Configuration=Release'` prints
|
|
1068
|
+
// `p:Configuration=Release`, leading slash gone, UNLESS this variable is set). '*'
|
|
1069
|
+
// excludes every argument from conversion — verification commands never rely on MSYS's
|
|
1070
|
+
// automatic POSIX-to-Windows path conversion for an argument (a real path either
|
|
1071
|
+
// resolves natively as a relative repo path, or is already given in Windows-native form
|
|
1072
|
+
// for a native tool), so excluding all of them is safe, not just the switch-shaped ones.
|
|
1073
|
+
env: Object.assign({}, process.env, { MSYS2_ARG_CONV_EXCL: '*' }),
|
|
1059
1074
|
});
|
|
1060
1075
|
} catch (err) {
|
|
1061
1076
|
finish({ spawnError: 'failed to spawn bash: ' + err.message });
|
package/package.json
CHANGED
|
@@ -7,32 +7,40 @@ tools: Read, Glob, Grep
|
|
|
7
7
|
|
|
8
8
|
# Generate Work Breakdown — Skeleton Pass
|
|
9
9
|
|
|
10
|
-
You are an **expert software architect** specializing in work decomposition and delivery planning. Given a feature's functional requirements and technical specification, produce the **skeleton** of a structured work breakdown: every phase and, per task, only the structural fields needed to fix the dependency graph and acceptance-criteria assignment — `id`, `title`, `outcome`, `domain`, `agentType`, `dependsOn`, `acceptanceCriteria`, `
|
|
10
|
+
You are an **expert software architect** specializing in work decomposition and delivery planning. Given a feature's functional requirements and technical specification, produce the **skeleton** of a structured work breakdown: every phase and, per task, only the structural fields needed to fix the dependency graph and acceptance-criteria assignment — `id`, `title`, `outcome`, `domain`, `agentType`, `dependsOn`, `acceptanceCriteria`, `outputCount`, `commit`.
|
|
11
11
|
|
|
12
|
-
This is the **first of two generation passes** (FTR-020). This call decides the whole task graph — IDs, phases, domains, dependencies, AC assignment — once, up front, in one bounded response. A separate per-task agent (`gaia-generate-work-breakdown-task`) is then dispatched once per task, filling in that task's own verification commands
|
|
12
|
+
This is the **first of two generation passes** (FTR-020). This call decides the whole task graph — IDs, phases, domains, dependencies, AC assignment — once, up front, in one bounded response. A separate per-task agent (`gaia-generate-work-breakdown-task`) is then dispatched once per task, filling in that task's own verification commands, grouping rationale, AND effort estimate (`estimate.agentMinutes`/`estimate.tokens` — moved there, not produced in this pass; see that agent's own instructions). You do **not** produce verification commands, grouping-rationale prose, or an effort estimate — see `gaia-generate-work-breakdown-task` for those.
|
|
13
13
|
|
|
14
14
|
---
|
|
15
15
|
|
|
16
16
|
## Input
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
`
|
|
23
|
-
2. Tech-Spec —
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
18
|
+
Your prompt directly inlines the content you need — you do **not** read Requirements or Tech-Spec
|
|
19
|
+
files yourself:
|
|
20
|
+
|
|
21
|
+
1. **Requirements — full text, every Use Case's own flow detail included.** Inlined under a
|
|
22
|
+
`Requirements:` label.
|
|
23
|
+
2. **Tech-Spec — SKELETON ONLY.** Inlined under a `Tech-Spec:` label: Section 9's own component
|
|
24
|
+
index table (Purpose, Addresses, Domain, Spec-link columns, one row per component) plus every
|
|
25
|
+
feature-wide section (Overview, Architecture, Security, Database, Configuration, Implementation
|
|
26
|
+
Order, Risks). It deliberately does **not** include any component's own Change/Algorithm/
|
|
27
|
+
Schema/Tests design prose. Do NOT attempt to locate or Read `req/use-cases/*.md`,
|
|
28
|
+
`spec/components/*`, `{PREFIX}-Requirements.md`, or `{PREFIX}-Tech-Spec.md` on disk — use the
|
|
29
|
+
inlined text as the complete input for this pass. If a task-domain/dependency/AC-assignment
|
|
30
|
+
decision genuinely needs a specific component's own design body beyond its Purpose/Addresses/
|
|
31
|
+
Domain summary, do not guess and do not explore the filesystem for it — request it via
|
|
32
|
+
`needsComponentDetail` instead (see "Requesting Component Detail" below). The host will supply
|
|
33
|
+
it and re-ask you in a second, final call.
|
|
34
|
+
3. `{PREFIX}-Validation-Report.md` and `{PREFIX}-Approvals.md` — the host has already confirmed
|
|
35
|
+
both exist and are satisfied before dispatching you; you do not need to re-check them.
|
|
36
|
+
|
|
37
|
+
The path to `feature.md` is given to you first; Read it yourself for the feature's own narrative
|
|
38
|
+
context (Problem Statement, Core Flow, Edge Cases) — it is small and not inlined.
|
|
39
|
+
|
|
40
|
+
**This agent runs ONLY after validation and human approval.** If the inlined Requirements or
|
|
41
|
+
Tech-Spec text is empty or missing, abort and report:
|
|
34
42
|
```
|
|
35
|
-
Cannot generate work breakdown:
|
|
43
|
+
Cannot generate work breakdown: Requirements/Tech-Spec text was not provided.
|
|
36
44
|
The work breakdown requires validated and approved Requirements + Tech-Spec.
|
|
37
45
|
Run /implement-feature to orchestrate the full pipeline (including validation and approval gates).
|
|
38
46
|
```
|
|
@@ -60,7 +68,7 @@ Extract the **feature prefix** from the folder name containing `feature.md`:
|
|
|
60
68
|
|
|
61
69
|
The prefix is everything up to and including the second hyphen-separated segment (pattern: `[A-Z]+-[0-9]+`).
|
|
62
70
|
|
|
63
|
-
You do not have a Write tool. Return the skeleton as your final response, and nothing else — the host persists your response and splits it into `wbs/index.json` plus one `wbs/user-stories/{phaseId}.md` per phase (your `title`/`commit`/`context` values are written there; see Step
|
|
71
|
+
You do not have a Write tool. Return the skeleton as your final response, and nothing else — the host persists your response and splits it into `wbs/index.json` plus one `wbs/user-stories/{phaseId}.md` per phase (your `title`/`commit`/`context` values are written there; see Step 6).
|
|
64
72
|
|
|
65
73
|
---
|
|
66
74
|
|
|
@@ -85,27 +93,36 @@ Every task must be the **smallest independently implementable, verifiable, and c
|
|
|
85
93
|
|
|
86
94
|
## Process
|
|
87
95
|
|
|
88
|
-
### Step 1 —
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
96
|
+
### Step 1 — Cross-Reference the Inlined Documents
|
|
97
|
+
|
|
98
|
+
Use the Requirements text (full detail, every UC's own flow included) and the Tech-Spec text
|
|
99
|
+
(skeleton: Section 9's component index table plus feature-wide sections) exactly as inlined in
|
|
100
|
+
your prompt. Cross-reference them to derive every phase and task below — do not Read, Glob, or
|
|
101
|
+
Grep for either document or any of their per-item files; see "Requesting Component Detail" below if
|
|
102
|
+
you hit a genuine information gap only a specific component's own design body can fill.
|
|
103
|
+
|
|
104
|
+
#### Requesting Component Detail
|
|
105
|
+
|
|
106
|
+
The Tech-Spec's Section 9 table gives you each component's file path, change type, one-line
|
|
107
|
+
Purpose, `Addresses` (AC IDs), and `Domain` — enough for the large majority of domain/dependency/
|
|
108
|
+
AC-assignment decisions. If a SPECIFIC task decision genuinely needs detail beyond that one-line
|
|
109
|
+
summary (e.g. you cannot tell whether a component's own design implies a second, dependent task,
|
|
110
|
+
or which exact AC(s) within a multi-AC row a particular task slice actually covers), do not guess
|
|
111
|
+
and do not attempt to locate the file yourself. Instead, in your structured output, populate
|
|
112
|
+
`needsComponentDetail` with one entry per component whose own body you need, naming its exact
|
|
113
|
+
Spec-link `fileSlug` and a one-line `reason`:
|
|
114
|
+
|
|
115
|
+
```json
|
|
116
|
+
"needsComponentDetail": [
|
|
117
|
+
{ "fileSlug": "AuthMiddleware.cs.md", "reason": "need to know whether token refresh is a separate task from validation" }
|
|
118
|
+
]
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
The host will read exactly those files (nothing else) and dispatch you a SECOND, final call with
|
|
122
|
+
their content appended — answer with your complete, final skeleton on that call; there is no third
|
|
123
|
+
round. Leave `needsComponentDetail` as an empty array `[]` when you have everything you need from
|
|
124
|
+
the skeleton-level summary — this is the common case and costs nothing extra. Never name a
|
|
125
|
+
`fileSlug` that is not a real row in Section 9's own table.
|
|
109
126
|
|
|
110
127
|
### Step 2 — Derive User Stories from Use Cases
|
|
111
128
|
|
|
@@ -157,11 +174,10 @@ Decomposition strategy by domain:
|
|
|
157
174
|
| `agentType` | string | One of: `developer-backend`, `developer-frontend`, `developer-testing`, `review-solution` (deprecated: `developer-database` — maps to `developer-backend`) |
|
|
158
175
|
| `dependsOn` | array of strings | Task IDs this task depends on; may be empty `[]`; all IDs must be defined within this same response |
|
|
159
176
|
| `acceptanceCriteria` | array of strings | AC IDs from Requirements scoped to this task's own phase that this task covers; may be empty `[]`. Never an AC owned by a different phase (see Guidelines) |
|
|
160
|
-
| `estimate` | object | `{ "agentMinutes": N, "tokens": N }` — target ≤ 15 minutes; positive integers |
|
|
161
177
|
| `outputCount` | integer | Number of distinct outputs; must be ≥ 1 |
|
|
162
178
|
| `commit` | object | `{ "type": "feat", "subject": "..." }` — subject must not contain `\|`, CR, or LF |
|
|
163
179
|
|
|
164
|
-
`verification.commands` and `
|
|
180
|
+
`verification.commands`, `groupingRationale`, and `estimate` (`agentMinutes`/`tokens`) are **not** produced in this pass — they are decided by `gaia-generate-work-breakdown-task`, one call per task, from the skeleton entry you produce here (it is told your task's `outputCount`, so it knows whether a grouping rationale is required). You still judge task SIZE here — see the Atomicity Contract's own ≤15-minute target, which governs how finely you split tasks — you just don't emit a numeric estimate for it.
|
|
165
181
|
|
|
166
182
|
**Domain → agentType mapping (default):**
|
|
167
183
|
- `DB` → `developer-backend` (note: `developer-database` is a deprecated alias that still resolves — do NOT use it in new Work Breakdowns)
|
|
@@ -200,22 +216,7 @@ For each task, determine `dependsOn` as a list of **task IDs** (not phase IDs):
|
|
|
200
216
|
|
|
201
217
|
**Tip:** List only direct dependencies. Transitive dependencies are implied.
|
|
202
218
|
|
|
203
|
-
### Step 6 —
|
|
204
|
-
|
|
205
|
-
For each task, provide:
|
|
206
|
-
- `estimate.agentMinutes`: integer; **target ≤ 15**; above 30 is rejected by the validator
|
|
207
|
-
|
|
208
|
-
Duration guidance:
|
|
209
|
-
| Band | Range | Action |
|
|
210
|
-
|------|-------|--------|
|
|
211
|
-
| Target | ≤ 15 min | No action needed |
|
|
212
|
-
| Above target | 16–20 min | Acceptable |
|
|
213
|
-
| Warning | 21–30 min | Prefer to split |
|
|
214
|
-
| Split required | > 30 min | **Must split** — the validator will reject this |
|
|
215
|
-
|
|
216
|
-
- `estimate.tokens`: integer; rough estimate of token consumption (input + output) for the agent invocation; use project norms if available, otherwise estimate based on task complexity (small: 10 000–25 000, medium: 25 000–60 000)
|
|
217
|
-
|
|
218
|
-
### Step 7 — Return the Skeleton
|
|
219
|
+
### Step 6 — Return the Skeleton
|
|
219
220
|
|
|
220
221
|
Return the skeleton's content as your final response, and nothing else (no preamble, no explanatory prose, no Markdown code fence wrapping it) — you do not have a Write tool; the host persists your response. The content must be:
|
|
221
222
|
- Valid JSON
|
|
@@ -230,7 +231,8 @@ Return the skeleton's content as your final response, and nothing else (no pream
|
|
|
230
231
|
"schemaVersion": 3,
|
|
231
232
|
"feature": "{PREFIX}",
|
|
232
233
|
"phases": [ ... ],
|
|
233
|
-
"tasks": { ... }
|
|
234
|
+
"tasks": { ... },
|
|
235
|
+
"needsComponentDetail": [ ... ]
|
|
234
236
|
}
|
|
235
237
|
```
|
|
236
238
|
|
|
@@ -238,6 +240,7 @@ Return the skeleton's content as your final response, and nothing else (no pream
|
|
|
238
240
|
- `feature` must be the feature prefix string (e.g., `"FTR-020"`)
|
|
239
241
|
- `phases` is an ordered array of phase objects; INFRA phase first (if present), then US phases in priority order
|
|
240
242
|
- `tasks` is a **map keyed by task ID** (not nested inside each phase) — every phase's `taskIds` array indexes into this map
|
|
243
|
+
- `needsComponentDetail` is optional — omit it or leave it `[]` when you have everything you need (the common case); see "Requesting Component Detail" in Step 1. When non-empty, `phases`/`tasks` may be your best DRAFT given what you currently know — the host discards them and re-asks you with the requested detail appended; only your response on that second call is final.
|
|
241
244
|
|
|
242
245
|
**Phase object:**
|
|
243
246
|
|
|
@@ -281,7 +284,6 @@ For user-story phases:
|
|
|
281
284
|
"agentType": "developer-backend",
|
|
282
285
|
"dependsOn": [],
|
|
283
286
|
"acceptanceCriteria": [],
|
|
284
|
-
"estimate": { "agentMinutes": 8, "tokens": 15000 },
|
|
285
287
|
"outputCount": 1,
|
|
286
288
|
"commit": { "type": "feat", "subject": "add IRepository base interface" }
|
|
287
289
|
}
|
|
@@ -321,7 +323,6 @@ For user-story phases:
|
|
|
321
323
|
"agentType": "developer-backend",
|
|
322
324
|
"dependsOn": [],
|
|
323
325
|
"acceptanceCriteria": [],
|
|
324
|
-
"estimate": { "agentMinutes": 8, "tokens": 15000 },
|
|
325
326
|
"outputCount": 1,
|
|
326
327
|
"commit": { "type": "feat", "subject": "add IRepository base interface" }
|
|
327
328
|
},
|
|
@@ -332,7 +333,6 @@ For user-story phases:
|
|
|
332
333
|
"agentType": "developer-backend",
|
|
333
334
|
"dependsOn": ["INFRA-TASK-BE-01"],
|
|
334
335
|
"acceptanceCriteria": ["AC-01", "AC-03"],
|
|
335
|
-
"estimate": { "agentMinutes": 12, "tokens": 28000 },
|
|
336
336
|
"outputCount": 1,
|
|
337
337
|
"commit": { "type": "feat", "subject": "add User entity and migration" }
|
|
338
338
|
},
|
|
@@ -343,7 +343,6 @@ For user-story phases:
|
|
|
343
343
|
"agentType": "developer-backend",
|
|
344
344
|
"dependsOn": ["US-01-TASK-DB-01"],
|
|
345
345
|
"acceptanceCriteria": ["AC-02"],
|
|
346
|
-
"estimate": { "agentMinutes": 14, "tokens": 35000 },
|
|
347
346
|
"outputCount": 1,
|
|
348
347
|
"commit": { "type": "feat", "subject": "add CreateUserService" }
|
|
349
348
|
}
|
|
@@ -369,6 +368,7 @@ Do NOT guess or invent task decompositions when the source is unclear. Ask first
|
|
|
369
368
|
|
|
370
369
|
- Write in **English**
|
|
371
370
|
- **Derive all content from the Requirements and Tech-Spec** — do not invent tasks not grounded in those documents
|
|
371
|
+
- **Never self-explore the filesystem for Requirements/Tech-Spec content** — both are inlined in your prompt (Requirements full, Tech-Spec skeleton-only); use `needsComponentDetail` for a genuine component-body gap instead of Reading/Globbing/Grepping for it
|
|
372
372
|
- **Atomicity is mandatory** — each task must have one outcome, one domain, one agent type, one commit subject
|
|
373
373
|
- **Task granularity**: target ≤ 15 minutes of agent time; split any task that cannot be scoped to ≤ 15 minutes
|
|
374
374
|
- **Domain assignment must be unambiguous** — use the domain table in Step 3
|
|
@@ -56,9 +56,63 @@ const SKELETON_SCHEMA = {
|
|
|
56
56
|
properties: { type: { type: 'string' }, subject: { type: 'string' } } },
|
|
57
57
|
},
|
|
58
58
|
} },
|
|
59
|
+
// Bounded drill-down (mirrors pm-phase1.js's own needsComponentDetail precedent for the
|
|
60
|
+
// feature-doc validator, FTR-028) — declared in `properties` only, never `required`: the
|
|
61
|
+
// common case is an empty array, costing nothing extra. See gaia-generate-work-breakdown.md's
|
|
62
|
+
// own "Requesting Component Detail" section for when the agent should populate this instead
|
|
63
|
+
// of self-exploring spec/components/* (the real, evidenced dominant cost driver on a large
|
|
64
|
+
// feature — confirmed against a real run: 1.18M of 1.38M total tokens on one failing attempt
|
|
65
|
+
// were cache_read_input_tokens, not the declared output schema size).
|
|
66
|
+
needsComponentDetail: { type: 'array', items: { type: 'object',
|
|
67
|
+
properties: { fileSlug: { type: 'string' }, reason: { type: 'string' } }, required: ['fileSlug'] } },
|
|
59
68
|
},
|
|
60
69
|
}
|
|
61
70
|
|
|
71
|
+
// Fixed, not externally configurable (same "no real evidence yet justifies external tuning"
|
|
72
|
+
// precedent as every other bounded retry/batch constant in this codebase) — a safety cap, not a
|
|
73
|
+
// typical case; the skeleton agent's own instructions already frame needsComponentDetail as the
|
|
74
|
+
// exception, reserved for a genuine per-task decision gap, not a substitute for broad exploration.
|
|
75
|
+
const SKELETON_DRILL_DOWN_MAX_COMPONENTS = 20
|
|
76
|
+
|
|
77
|
+
// Builds the skeleton-generation prompt: Requirements FULL text + Tech-Spec SKELETON ONLY
|
|
78
|
+
// (never per-component bodies) inlined directly, mirroring pm-phase1.js's own
|
|
79
|
+
// gaia-validate-feature-docs prompt shape exactly — the agent no longer self-reads either
|
|
80
|
+
// document or any per-item file via its own Read/Glob/Grep tool access.
|
|
81
|
+
function buildSkeletonPrompt(featurePath, requirementsText, techSpecSkeletonText, drillDownText) {
|
|
82
|
+
const parts = [
|
|
83
|
+
featurePath,
|
|
84
|
+
'',
|
|
85
|
+
'Requirements:',
|
|
86
|
+
requirementsText,
|
|
87
|
+
'',
|
|
88
|
+
'Tech-Spec:',
|
|
89
|
+
techSpecSkeletonText,
|
|
90
|
+
]
|
|
91
|
+
if (drillDownText) {
|
|
92
|
+
parts.push('', 'Component detail you requested:', drillDownText)
|
|
93
|
+
}
|
|
94
|
+
return parts.join('\n')
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Validates each needsComponentDetail entry's fileSlug against the real component table (a
|
|
98
|
+
// hallucinated/mistyped reference is dropped, never dispatched — mirrors pm-phase1.js's own
|
|
99
|
+
// prepareDrillDownBatches), deduplicates by fileSlug, and caps the total at
|
|
100
|
+
// SKELETON_DRILL_DOWN_MAX_COMPONENTS. Returns the inlinable text block (or null if nothing valid
|
|
101
|
+
// remains) — this reads files directly (deterministic, free), never dispatches another agent.
|
|
102
|
+
function buildComponentDrillDownText(needsComponentDetail, specIndexContent, specDirArg) {
|
|
103
|
+
const components = parseComponentsTableLocal(specIndexContent)
|
|
104
|
+
const bySlug = new Map(components.map(c => [c.fileSlug, c]))
|
|
105
|
+
const uniqueSlugs = [...new Set((needsComponentDetail || [])
|
|
106
|
+
.map(e => e.fileSlug)
|
|
107
|
+
.filter(slug => bySlug.has(slug)))]
|
|
108
|
+
.slice(0, SKELETON_DRILL_DOWN_MAX_COMPONENTS)
|
|
109
|
+
if (uniqueSlugs.length === 0) return null
|
|
110
|
+
return uniqueSlugs.map(slug => {
|
|
111
|
+
const comp = bySlug.get(slug)
|
|
112
|
+
return `### ${comp.filePath} (${slug})\n\n${c.read(`${specDirArg}/components/${slug}`)}`
|
|
113
|
+
}).join('\n\n---\n\n')
|
|
114
|
+
}
|
|
115
|
+
|
|
62
116
|
// Splits the skeleton response into (a) index.json's structural, map-keyed shape and
|
|
63
117
|
// (b) nothing else — phase title/commit/context are NOT copied into index.json (OQ-04); they
|
|
64
118
|
// are rendered straight to each phase's detail file by the caller, from the same skeleton
|
|
@@ -334,7 +388,33 @@ return c.run(meta.name, featureDir, prefix, async () => {
|
|
|
334
388
|
// 1. Skeleton (no declared worker() output — host writes every file itself)
|
|
335
389
|
let index
|
|
336
390
|
if (!c.exists(indexPath)) {
|
|
337
|
-
|
|
391
|
+
// Requirements FULL text + Tech-Spec SKELETON ONLY, inlined directly — same shape as
|
|
392
|
+
// pm-phase1.js's own validator prompt (FTR-028). Computed unconditionally here (not reused
|
|
393
|
+
// from the Gate-1 digest check above, which only runs when Approvals.md carries a digest
|
|
394
|
+
// line) so this prompt is always built the same way regardless of that check's own outcome.
|
|
395
|
+
const requirementsForSkeleton = hasReq
|
|
396
|
+
? c.command(['run-asset', 'scripts/req-assembler.js', '--project', c.root, '--', reqDir]).stdout
|
|
397
|
+
: c.read(file('Requirements.md'))
|
|
398
|
+
const techSpecSkeletonForSkeleton = hasSpec ? c.read(specIndexPath) : c.read(file('Tech-Spec.md'))
|
|
399
|
+
|
|
400
|
+
let skeleton = await c.worker('gaia.agent.planner.work-breakdown',
|
|
401
|
+
buildSkeletonPrompt(featurePath, requirementsForSkeleton, techSpecSkeletonForSkeleton),
|
|
402
|
+
{ schema: SKELETON_SCHEMA })
|
|
403
|
+
|
|
404
|
+
// Bounded, single-round drill-down (FTR-028 precedent) — see buildComponentDrillDownText's
|
|
405
|
+
// own comment. No further rounds: the second call's own response is used as-is, even if it
|
|
406
|
+
// asks for more (fail-fast rather than open-ended retries, matching this codebase's other
|
|
407
|
+
// bounded-retry conventions).
|
|
408
|
+
const needsComponentDetail = skeleton.needsComponentDetail || []
|
|
409
|
+
if (hasSpec && needsComponentDetail.length > 0) {
|
|
410
|
+
const drillDownText = buildComponentDrillDownText(needsComponentDetail, c.read(specIndexPath), specDir)
|
|
411
|
+
if (drillDownText) {
|
|
412
|
+
skeleton = await c.worker('gaia.agent.planner.work-breakdown',
|
|
413
|
+
buildSkeletonPrompt(featurePath, requirementsForSkeleton, techSpecSkeletonForSkeleton, drillDownText),
|
|
414
|
+
{ schema: SKELETON_SCHEMA, label: 'work-breakdown-skeleton:with-drilldown' })
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
338
418
|
index = materializeIndex(skeleton)
|
|
339
419
|
const taskIdToPhase = new Map(skeleton.phases.flatMap(p => p.taskIds.map(taskId => [taskId, p.id])))
|
|
340
420
|
for (const phase of index.phases) {
|