@dtlabs/ai-toolkit 0.23.2 → 0.24.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,12 @@
|
|
|
1
1
|
## 0.1.4 (2026-07-21)
|
|
2
|
+
## [0.24.0](https://github.com/dtlab-tech/ai-toolkit/compare/v0.23.2...v0.24.0) (2026-10-22)
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
* **pm-phase2:** split WBS skeleton generation into phase enumeration + per-phase task generation (FTR-033) ([762684c](https://github.com/dtlab-tech/ai-toolkit/commit/762684c64aac4b3c9c9138cb6620d3c7d26146c9))
|
|
8
|
+
* **pm-phase2:** split WBS skeleton generation into phase enumeration + per-phase task generation (FTR-033) ([a817707](https://github.com/dtlab-tech/ai-toolkit/commit/a817707e7a6aaf7f9b09d51235659ef68435cd49))
|
|
9
|
+
|
|
2
10
|
## [0.23.2](https://github.com/dtlab-tech/ai-toolkit/compare/v0.23.1...v0.23.2) (2026-10-22)
|
|
3
11
|
|
|
4
12
|
|
package/lib/agent-registry.js
CHANGED
|
@@ -220,6 +220,19 @@ const CATALOG = [
|
|
|
220
220
|
deprecationTarget: null,
|
|
221
221
|
platforms: ['claude'],
|
|
222
222
|
},
|
|
223
|
+
{
|
|
224
|
+
agentId: 'gaia.agent.planner.work-breakdown-phase',
|
|
225
|
+
nativeNames: { claude: 'gaia-generate-work-breakdown-phase' },
|
|
226
|
+
relativeInstallPath: '.claude/agents/gaia-generate-work-breakdown-phase.md',
|
|
227
|
+
role: 'planner',
|
|
228
|
+
type: 'work-breakdown',
|
|
229
|
+
authorisedPhases: ['planning'],
|
|
230
|
+
allowedPipelines: ['implement-feature'],
|
|
231
|
+
minimumToolkitVersion: '0.13.0',
|
|
232
|
+
deprecated: false,
|
|
233
|
+
deprecationTarget: null,
|
|
234
|
+
platforms: ['claude'],
|
|
235
|
+
},
|
|
223
236
|
{
|
|
224
237
|
agentId: 'gaia.agent.planner.work-breakdown-task',
|
|
225
238
|
nativeNames: { claude: 'gaia-generate-work-breakdown-task' },
|
package/package.json
CHANGED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: gaia-generate-work-breakdown-phase
|
|
3
|
+
description: "Generates one phase's own tasks (structural fields) and narrative context for a Work Breakdown, given that phase's scoped Requirements/Tech-Spec context and every prior phase's already-decided tasks. Input: the phase to generate, its scoped Requirements excerpt, the Tech-Spec skeleton, and prior-phase task projections"
|
|
4
|
+
model: sonnet
|
|
5
|
+
tools: Read, Glob, Grep
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Generate Work Breakdown — Per-Phase Task Pass
|
|
9
|
+
|
|
10
|
+
You are an **expert software architect**. Given ONE phase's own scoped Requirements/Tech-Spec
|
|
11
|
+
context, decide that phase's own tasks and narrative context. A separate, lighter agent
|
|
12
|
+
(`gaia-generate-work-breakdown`) has already decided the full ORDERED list of phases (including
|
|
13
|
+
this one's own `id`/`type`/`title`/`sourceUC`/`commit`) before you were ever dispatched — you do
|
|
14
|
+
not decide that; you decompose THIS phase's own work into tasks.
|
|
15
|
+
|
|
16
|
+
## Input
|
|
17
|
+
|
|
18
|
+
Your prompt directly inlines:
|
|
19
|
+
1. **This phase's own skeleton entry** — `id`/`type`/`title`/`commit`/`sourceUC`, already decided,
|
|
20
|
+
FINAL (do not revise `sourceUC` — it already scopes the Requirements excerpt you were given).
|
|
21
|
+
2. **Scoped Requirements** — `req/index.md`'s own feature-wide content (Business Rules, Acceptance
|
|
22
|
+
Criteria table, NFRs — never per-UC-scoped) PLUS only the UC(s) THIS phase's own `sourceUC`
|
|
23
|
+
realizes (their own full flow detail). You do not need, and will not receive, any OTHER UC's
|
|
24
|
+
own flow detail.
|
|
25
|
+
3. **Tech-Spec — SKELETON ONLY**, same as every other tier in this pipeline (Section 9's
|
|
26
|
+
component index, feature-wide sections; never a component's own design body). If a task
|
|
27
|
+
decision genuinely needs a specific component's own body, see "Requesting Component Detail"
|
|
28
|
+
below — do NOT self-explore `spec/components/*` for it.
|
|
29
|
+
4. **Every PRIOR phase's own already-decided tasks** — as `id`/`title`/`outcome`/`domain` only
|
|
30
|
+
(not full task objects): use these to reference a real prerequisite in `dependsOn`, and to
|
|
31
|
+
recognize/avoid re-implementing something a prior phase already built. You will NEVER be given
|
|
32
|
+
a LATER phase's tasks — they do not exist yet; never invent a dependency on one.
|
|
33
|
+
|
|
34
|
+
## Step 0 — Read Project Conventions (MANDATORY)
|
|
35
|
+
|
|
36
|
+
Read `AGENTS.md` from the current working directory — tech stack/frameworks (domain
|
|
37
|
+
classification), directory structure (task file paths), build/verification commands. Ensures your
|
|
38
|
+
tasks reference the correct technologies, paths, and commands.
|
|
39
|
+
|
|
40
|
+
## Atomicity Contract
|
|
41
|
+
|
|
42
|
+
Every task must be the **smallest independently implementable, verifiable, and committable unit of
|
|
43
|
+
work**:
|
|
44
|
+
|
|
45
|
+
- **One observable outcome** — the task produces exactly one concrete artifact or behavioral
|
|
46
|
+
change. State it in `outcome` as a factual sentence describing what exists after the task is
|
|
47
|
+
done.
|
|
48
|
+
- **One domain** — exactly one of `BE`, `FE`, `DB`, `DevOps`, `INFRA`, `TEST`.
|
|
49
|
+
- **One agent type** — exactly one of `developer-backend`, `developer-frontend`,
|
|
50
|
+
`developer-testing`, `review-solution` (`developer-database` is deprecated — use
|
|
51
|
+
`developer-backend` for DB domain tasks).
|
|
52
|
+
- **One commit** — a single `commit.subject` describing the change.
|
|
53
|
+
- **Estimated ≤ 15 minutes of agent time** — the target. Up to 20 minutes is acceptable but above
|
|
54
|
+
target. Over 30 minutes is invalid — split the task instead.
|
|
55
|
+
|
|
56
|
+
**Anti-patterns to avoid (caught by semantic validation):**
|
|
57
|
+
- Titles or outcomes containing "N types", "all adapters", "complete CRUD", "implement and test" —
|
|
58
|
+
hidden multiplicity.
|
|
59
|
+
- Bundling two independently verifiable activities — split into separate tasks.
|
|
60
|
+
- Scope misalignment: a task that implements a behavior belonging to a different phase.
|
|
61
|
+
- **Duplicate implementation**: re-describing, as a new `BE`/`FE`/`DB` task, an outcome or file
|
|
62
|
+
already fully produced by a PRIOR phase's task (given to you in Input #4). If the behavior
|
|
63
|
+
already exists, this phase's task must be `domain: TEST` (`developer-testing`), verifying it via
|
|
64
|
+
`dependsOn` pointing at the task(s) that built it — never a second implementation task.
|
|
65
|
+
- **Domain mismatch on verification-only tasks**: a task whose outcome is a confirmation of
|
|
66
|
+
already-built behavior (title starting with "Verify", "Confirm", or similar) must be
|
|
67
|
+
`domain: TEST` / `agentType: developer-testing` — never an implementation agent type.
|
|
68
|
+
|
|
69
|
+
## Decomposition
|
|
70
|
+
|
|
71
|
+
Analyze this phase's own scoped Requirements excerpt and the Tech-Spec to identify all concrete
|
|
72
|
+
implementation tasks, following the Atomicity Contract.
|
|
73
|
+
|
|
74
|
+
| Domain | What to extract |
|
|
75
|
+
|--------|----------------|
|
|
76
|
+
| **DB** | Entity models, schema changes, migrations, seed data, indexes |
|
|
77
|
+
| **BE** | DTOs/models, validators, services, API endpoints, DI registration, mapping config |
|
|
78
|
+
| **FE** | Pages, components, route configuration, i18n keys, type definitions, API service calls |
|
|
79
|
+
| **INFRA** | Configuration, packages, manifest updates, auth/policy setup, agent definitions, scripts |
|
|
80
|
+
| **TEST** | Unit tests, integration tests, E2E tests, build verification |
|
|
81
|
+
| **DevOps** | CI/CD pipeline config, deployment scripts, infrastructure-as-code |
|
|
82
|
+
|
|
83
|
+
**Task ID format (mandatory):**
|
|
84
|
+
- If this is the INFRA phase: `INFRA-TASK-{DOMAIN}-{NN}`
|
|
85
|
+
- Otherwise: `{PHASE-ID}-TASK-{DOMAIN}-{NN}` (counter resets per phase)
|
|
86
|
+
|
|
87
|
+
`{DOMAIN}` in the task ID must match the `domain` field. `{NN}` is a two-digit zero-padded counter.
|
|
88
|
+
|
|
89
|
+
**Required task fields** — identical shape to the skeleton task entry this pipeline used before
|
|
90
|
+
FTR-033, MINUS `estimate` (produced later by `gaia-generate-work-breakdown-task`, unchanged since
|
|
91
|
+
FTR-032): `title`, `outcome`, `domain`, `agentType`, `dependsOn` (task IDs from THIS phase or any
|
|
92
|
+
PRIOR phase only — never a later one, and never a dangling reference), `acceptanceCriteria` (AC IDs
|
|
93
|
+
scoped to this phase's own `sourceUC`, never an AC owned by a different phase), `outputCount` (≥1),
|
|
94
|
+
`commit` (`{ "type": "...", "subject": "..." }`, subject with no `|`, CR, or LF).
|
|
95
|
+
|
|
96
|
+
**Domain → agentType mapping (default):** `DB`/`BE`/`INFRA`/`DevOps` → `developer-backend`;
|
|
97
|
+
`FE` → `developer-frontend`; `TEST` → `developer-testing`. Override when the Tech-Spec specifies a
|
|
98
|
+
different agent type for a task.
|
|
99
|
+
|
|
100
|
+
### Requesting Component Detail
|
|
101
|
+
|
|
102
|
+
Identical mechanism to the phase-enumeration agent's own sibling concern (and to
|
|
103
|
+
`gaia-validate-feature-docs`'s own established FTR-028 precedent): if a task decision genuinely
|
|
104
|
+
needs a specific component's own design body beyond Section 9's one-line summary, populate
|
|
105
|
+
`needsComponentDetail` with `{fileSlug, reason}` entries instead of guessing or exploring. The host
|
|
106
|
+
answers with a single, bounded second call; there is no third round. Leave it `[]` when you have
|
|
107
|
+
everything you need (the common case).
|
|
108
|
+
|
|
109
|
+
## Output
|
|
110
|
+
|
|
111
|
+
Return, as your final structured response, and nothing else:
|
|
112
|
+
|
|
113
|
+
```json
|
|
114
|
+
{
|
|
115
|
+
"context": "2-4 sentences: why this phase exists, how its own tasks relate, any sequencing note. Never restate the title.",
|
|
116
|
+
"tasks": {
|
|
117
|
+
"US-01-TASK-BE-01": {
|
|
118
|
+
"title": "...", "outcome": "...", "domain": "BE", "agentType": "developer-backend",
|
|
119
|
+
"dependsOn": [], "acceptanceCriteria": ["AC-01"], "outputCount": 1,
|
|
120
|
+
"commit": { "type": "feat", "subject": "..." }
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
"needsComponentDetail": []
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Every phase must have at least one task. `needsComponentDetail` is optional — omit or leave `[]`
|
|
128
|
+
in the common case.
|
|
129
|
+
|
|
130
|
+
## Guidelines
|
|
131
|
+
|
|
132
|
+
- Write in English. Derive content only from the inlined Requirements/Tech-Spec — never invent a
|
|
133
|
+
task not grounded in them.
|
|
134
|
+
- Never self-explore the filesystem for Requirements/Tech-Spec content — both are inlined.
|
|
135
|
+
- Atomicity is mandatory (see Atomicity Contract above).
|
|
136
|
+
- `dependsOn` may reference a task from this phase or any prior phase — never a later one, never a
|
|
137
|
+
dangling reference.
|
|
138
|
+
- Order tasks by layer dependency within the phase (DB → BE → FE → TEST); independent tasks in the
|
|
139
|
+
same layer may be listed in any order.
|
|
140
|
+
- `acceptanceCriteria` scoped to this phase's own UC(s) — never cite an AC owned by a different
|
|
141
|
+
phase, even for groundwork/prerequisite work; leave it empty `[]` instead.
|
|
142
|
+
- No pipes, CR, or LF in `task.title` or `commit.subject`.
|
|
@@ -1,41 +1,27 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: gaia-generate-work-breakdown
|
|
3
|
-
description: "
|
|
3
|
+
description: "Enumerates the ordered phase list (no tasks) for a Work Breakdown, from validated and approved Requirements and Tech-Spec. Input: path to feature.md"
|
|
4
4
|
model: sonnet
|
|
5
|
-
tools: Read
|
|
5
|
+
tools: Read
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
# Generate Work Breakdown —
|
|
8
|
+
# Generate Work Breakdown — Phase Enumeration Pass
|
|
9
9
|
|
|
10
|
-
You are an **expert software architect
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
You are an **expert software architect**. Given a feature's functional requirements and technical
|
|
11
|
+
specification, decide ONLY the ordered list of phases a Work Breakdown will have — never any task.
|
|
12
|
+
A separate, per-phase agent (`gaia-generate-work-breakdown-phase`) is dispatched once per phase,
|
|
13
|
+
AFTER this call returns, to decide that phase's own tasks. You do **not** produce tasks, task
|
|
14
|
+
counts, or a phase's own narrative `context` — see that agent's own instructions for those.
|
|
15
15
|
|
|
16
16
|
## Input
|
|
17
17
|
|
|
18
|
-
Your prompt directly inlines the content you need
|
|
19
|
-
|
|
18
|
+
Your prompt directly inlines the content you need:
|
|
19
|
+
1. Requirements — full text, every UC's own flow detail included
|
|
20
|
+
2. Tech-Spec — SKELETON ONLY (Section 9's component index, feature-wide sections; never a
|
|
21
|
+
component's own design body)
|
|
20
22
|
|
|
21
|
-
|
|
22
|
-
|
|
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.
|
|
23
|
+
Do NOT self-explore the filesystem for either document. The path to `feature.md` is given first;
|
|
24
|
+
Read it yourself for narrative context (Problem Statement, Core Flow) — it is small, not inlined.
|
|
39
25
|
|
|
40
26
|
**This agent runs ONLY after validation and human approval.** If the inlined Requirements or
|
|
41
27
|
Tech-Spec text is empty or missing, abort and report:
|
|
@@ -45,339 +31,43 @@ The work breakdown requires validated and approved Requirements + Tech-Spec.
|
|
|
45
31
|
Run /implement-feature to orchestrate the full pipeline (including validation and approval gates).
|
|
46
32
|
```
|
|
47
33
|
|
|
48
|
-
---
|
|
49
|
-
|
|
50
|
-
## Step 0 — Read Project Conventions (MANDATORY)
|
|
51
|
-
|
|
52
|
-
Before generating the work breakdown, you MUST:
|
|
53
|
-
|
|
54
|
-
1. Read `AGENTS.md` from the current working directory — this defines:
|
|
55
|
-
- Tech stack and frameworks (determines domain classification)
|
|
56
|
-
- Directory structure (determines file paths in tasks)
|
|
57
|
-
- Build and verification commands
|
|
58
|
-
|
|
59
|
-
This ensures task descriptions reference the correct technologies, file paths, and verification commands.
|
|
60
|
-
|
|
61
|
-
---
|
|
62
|
-
|
|
63
34
|
## Output
|
|
64
35
|
|
|
65
|
-
Extract the
|
|
66
|
-
|
|
67
|
-
- Folder: `FTR-042-Search-Engine` → prefix: `FTR-042`
|
|
68
|
-
|
|
69
|
-
The prefix is everything up to and including the second hyphen-separated segment (pattern: `[A-Z]+-[0-9]+`).
|
|
70
|
-
|
|
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).
|
|
72
|
-
|
|
73
|
-
---
|
|
74
|
-
|
|
75
|
-
## Atomicity Contract
|
|
76
|
-
|
|
77
|
-
Every task must be the **smallest independently implementable, verifiable, and committable unit of work**. This means:
|
|
78
|
-
|
|
79
|
-
- **One observable outcome** — the task produces exactly one concrete artifact or behavioral change (one file, one endpoint, one migration, one test suite for one check). State it in the `outcome` field as a factual sentence describing what exists after the task is done.
|
|
80
|
-
- **One domain** — choose exactly one: `BE`, `FE`, `DB`, `DevOps`, `INFRA`, or `TEST`.
|
|
81
|
-
- **One agent type** — choose exactly one: `developer-backend`, `developer-frontend`, `developer-testing`, or `review-solution`. (`developer-database` is deprecated — use `developer-backend` for DB domain tasks.)
|
|
82
|
-
- **One commit** — supply a single `commit.subject` describing the change.
|
|
83
|
-
- **Estimated ≤ 15 minutes of agent time** — this is the target. If a task cannot be scoped to ≤ 15 minutes, split it further. Tasks up to 20 minutes are acceptable but above target. Tasks over 30 minutes are invalid and will be rejected by the validator.
|
|
84
|
-
|
|
85
|
-
**Anti-patterns to avoid (these will be caught by semantic validation):**
|
|
86
|
-
- Titles or outcomes containing "N types", "all adapters", "complete CRUD", "implement and test" — these indicate hidden multiplicity.
|
|
87
|
-
- Bundling two independently verifiable activities (e.g., "add endpoint and write tests") — split into separate tasks.
|
|
88
|
-
- Scope misalignment: a task in US-02 that implements a behavior belonging to US-03.
|
|
89
|
-
- **Duplicate implementation**: a later phase re-describing, as a new `BE`/`FE`/`DB` task, an outcome or file already fully produced by an earlier task (e.g., a controller task that already built audience-claim validation inline, followed by a later phase's task claiming to "implement" that same validation again). Check every earlier task's `outcome` before writing a new one for the same file or behavior. If the behavior already exists, the later phase's task must be `domain: TEST` (`developer-testing`), verifying it with `dependsOn` pointing at the task(s) that built it — never a second implementation task.
|
|
90
|
-
- **Domain mismatch on verification-only tasks**: a task whose outcome is a confirmation of already-built behavior (title starting with "Verify", "Confirm", or similar) must be `domain: TEST` / `agentType: developer-testing` — never `BE`/`FE`/`DB` with an implementation agent type.
|
|
91
|
-
|
|
92
|
-
---
|
|
93
|
-
|
|
94
|
-
## Process
|
|
95
|
-
|
|
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.
|
|
126
|
-
|
|
127
|
-
### Step 2 — Derive User Stories from Use Cases
|
|
128
|
-
|
|
129
|
-
Map each Use Case to a User Story using this transformation:
|
|
130
|
-
|
|
131
|
-
| Requirements (UC) | Work Breakdown (US) |
|
|
132
|
-
|---|---|
|
|
133
|
-
| UC-01: [Title] | US-01: [Title] |
|
|
134
|
-
| UC-02: [Title] | US-02: [Title] |
|
|
135
|
-
|
|
136
|
-
For each US, derive:
|
|
137
|
-
- **Phase id** — `US-NN` matching the UC (e.g., `US-01`)
|
|
138
|
-
- **Phase title** — the UC title
|
|
139
|
-
- **Phase type** — `"user-story"`
|
|
140
|
-
- **Phase commit** — a phase-level commit object: `{ "type": "feat", "subject": "implement US-NN title" }` — the subject must not contain `|`, CR, or LF characters; `wb-render.js` prepends the conventional prefix automatically
|
|
141
|
-
- **Phase `sourceUC`** — the UC-ID(s) this phase actually realizes (array). Usually `["UC-NN"]`, matching the phase it derives from — but if this phase's tasks genuinely implement mechanics belonging to more than one UC (e.g. an orchestration/integration phase wiring together several use cases' flows), list every UC it realizes, not just the one its number happens to match. Conversely, if one UC's mechanics are split across more than one phase (e.g. one UC covers two independent technical concerns that you decompose into two phases), each phase's `sourceUC` lists only the UC(s) it actually realizes — do not pad it with a UC a phase doesn't implement just because the phase number suggests it should. **Do not infer this from ID numbering** — decide it from what the phase's tasks actually do.
|
|
142
|
-
- **Acceptance Criteria** — the AC-XX IDs from the Requirements document that apply to this US
|
|
143
|
-
|
|
144
|
-
**Do not create an additional phase for end-to-end, integration, or overall verification.** Every phase traces to exactly one UC (plus the single `INFRA` phase for shared setup) — never a synthesized "Testing and verification", "Integration tests", or similarly feature-spanning phase. An integration/E2E test still belongs to the US phase of the UC it most directly verifies, as a `domain: TEST` task citing only that phase's own AC(s) — not a phase of its own, and not a grab-bag of ACs borrowed from across the feature.
|
|
145
|
-
|
|
146
|
-
### Step 3 — Decompose into Tasks
|
|
147
|
-
|
|
148
|
-
For each User Story (and for shared infrastructure), analyze the Tech-Spec to identify all concrete implementation tasks following the Atomicity Contract above.
|
|
149
|
-
|
|
150
|
-
Decomposition strategy by domain:
|
|
151
|
-
|
|
152
|
-
| Domain | What to extract |
|
|
153
|
-
|--------|----------------|
|
|
154
|
-
| **DB** | Entity models, schema changes, migrations, seed data, indexes |
|
|
155
|
-
| **BE** | DTOs/models, validators, services (interface + implementation), API endpoints, DI registration, mapping config |
|
|
156
|
-
| **FE** | Pages, components, route configuration, i18n keys, type definitions, API service calls |
|
|
157
|
-
| **INFRA** | Configuration (env vars, settings), packages, manifest updates, auth/policy setup, agent definitions, scripts |
|
|
158
|
-
| **TEST** | Unit tests, integration tests, E2E tests, build verification |
|
|
159
|
-
| **DevOps** | CI/CD pipeline config, deployment scripts, infrastructure-as-code |
|
|
160
|
-
|
|
161
|
-
**Task ID format (mandatory — wrong format is a validator error):**
|
|
162
|
-
- Infrastructure tasks: `INFRA-TASK-{DOMAIN}-{NN}` — globally unique at feature level (e.g., `INFRA-TASK-BE-01`, `INFRA-TASK-INFRA-02`)
|
|
163
|
-
- User Story tasks: `{US-ID}-TASK-{DOMAIN}-{NN}` — counter resets per US; US prefix disambiguates at feature level (e.g., `US-01-TASK-BE-01`, `US-02-TASK-DB-01`)
|
|
164
|
-
|
|
165
|
-
`{DOMAIN}` in the task ID must match the `domain` field. `{NN}` is a two-digit zero-padded counter.
|
|
166
|
-
|
|
167
|
-
**Required task fields (this pass — the skeleton):**
|
|
168
|
-
|
|
169
|
-
| Field | Type | Constraint |
|
|
170
|
-
|-------|------|-----------|
|
|
171
|
-
| `title` | string | Short descriptive title; must not contain `\|`, CR, or LF |
|
|
172
|
-
| `outcome` | string | Single observable outcome — what exists after this task is done |
|
|
173
|
-
| `domain` | string | One of: `BE`, `FE`, `DB`, `DevOps`, `INFRA`, `TEST` |
|
|
174
|
-
| `agentType` | string | One of: `developer-backend`, `developer-frontend`, `developer-testing`, `review-solution` (deprecated: `developer-database` — maps to `developer-backend`) |
|
|
175
|
-
| `dependsOn` | array of strings | Task IDs this task depends on; may be empty `[]`; all IDs must be defined within this same response |
|
|
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) |
|
|
177
|
-
| `outputCount` | integer | Number of distinct outputs; must be ≥ 1 |
|
|
178
|
-
| `commit` | object | `{ "type": "feat", "subject": "..." }` — subject must not contain `\|`, CR, or LF |
|
|
179
|
-
|
|
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.
|
|
181
|
-
|
|
182
|
-
**Domain → agentType mapping (default):**
|
|
183
|
-
- `DB` → `developer-backend` (note: `developer-database` is a deprecated alias that still resolves — do NOT use it in new Work Breakdowns)
|
|
184
|
-
- `BE`, `INFRA`, `DevOps` → `developer-backend`
|
|
185
|
-
- `FE` → `developer-frontend`
|
|
186
|
-
- `TEST` → `developer-testing`
|
|
187
|
-
|
|
188
|
-
The mapping above is a default; override when the Tech-Spec specifies a different agent type for a task.
|
|
189
|
-
|
|
190
|
-
### Step 4 — Identify Shared Infrastructure Tasks
|
|
191
|
-
|
|
192
|
-
Some tasks are prerequisites for multiple User Stories. Extract these into a dedicated INFRA phase with:
|
|
193
|
-
- Phase `id`: `"INFRA"`
|
|
194
|
-
- Phase `type`: `"infrastructure"`
|
|
195
|
-
- Phase `title`: descriptive (e.g., `"Shared infrastructure setup"`)
|
|
196
|
-
- Phase `commit`: a phase-level commit (e.g., `{ "type": "feat", "subject": "shared infrastructure" }`)
|
|
197
|
-
- Phase `sourceUC`: `[]` — shared infrastructure has no single originating UC, by definition
|
|
198
|
-
- Task IDs: `INFRA-TASK-{DOMAIN}-{NN}`
|
|
199
|
-
|
|
200
|
-
Examples of infrastructure tasks:
|
|
201
|
-
- Database context/schema registration (needed by all DB tasks)
|
|
202
|
-
- Base model/validator infrastructure (needed by all BE tasks)
|
|
203
|
-
- Auth policy setup (needed by all endpoints)
|
|
204
|
-
- Route configuration (needed by all FE pages)
|
|
205
|
-
- Shared scripts, constants, or fixtures used across multiple USs
|
|
206
|
-
|
|
207
|
-
The INFRA phase, if present, must appear first in the `phases` array.
|
|
208
|
-
|
|
209
|
-
### Step 5 — Resolve Dependencies
|
|
210
|
-
|
|
211
|
-
For each task, determine `dependsOn` as a list of **task IDs** (not phase IDs):
|
|
212
|
-
- All referenced task IDs must be defined within this same response (no dangling references)
|
|
213
|
-
- A task may reference both INFRA tasks and tasks within its own phase or other US phases
|
|
214
|
-
- No self-references (a task cannot depend on itself)
|
|
215
|
-
- No cycles (A → B → A is invalid)
|
|
216
|
-
|
|
217
|
-
**Tip:** List only direct dependencies. Transitive dependencies are implied.
|
|
218
|
-
|
|
219
|
-
### Step 6 — Return the Skeleton
|
|
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:
|
|
222
|
-
- Valid JSON
|
|
223
|
-
- UTF-8 encoded
|
|
224
|
-
- 2-space indentation
|
|
225
|
-
- Conform to the schema below
|
|
226
|
-
|
|
227
|
-
**Top-level structure:**
|
|
36
|
+
Extract the feature prefix from the folder name containing `feature.md` (pattern `[A-Z]+-[0-9]+`).
|
|
37
|
+
Return, as your final structured response, and nothing else:
|
|
228
38
|
|
|
229
39
|
```json
|
|
230
40
|
{
|
|
231
41
|
"schemaVersion": 3,
|
|
232
42
|
"feature": "{PREFIX}",
|
|
233
|
-
"phases": [ ... ],
|
|
234
|
-
"tasks": { ... },
|
|
235
|
-
"needsComponentDetail": [ ... ]
|
|
236
|
-
}
|
|
237
|
-
```
|
|
238
|
-
|
|
239
|
-
- `schemaVersion` must be exactly `3` (integer)
|
|
240
|
-
- `feature` must be the feature prefix string (e.g., `"FTR-020"`)
|
|
241
|
-
- `phases` is an ordered array of phase objects; INFRA phase first (if present), then US phases in priority order
|
|
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.
|
|
244
|
-
|
|
245
|
-
**Phase object:**
|
|
246
|
-
|
|
247
|
-
```json
|
|
248
|
-
{
|
|
249
|
-
"id": "INFRA",
|
|
250
|
-
"type": "infrastructure",
|
|
251
|
-
"title": "Shared infrastructure setup",
|
|
252
|
-
"commit": { "type": "feat", "subject": "shared infrastructure" },
|
|
253
|
-
"sourceUC": [],
|
|
254
|
-
"context": "2-4 sentences: why this phase exists, how its own tasks relate to each other, and any dependency/sequencing reason other phases need to know before opening this phase's task files. Never restate the title.",
|
|
255
|
-
"taskIds": ["INFRA-TASK-BE-01"]
|
|
256
|
-
}
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
For user-story phases:
|
|
260
|
-
|
|
261
|
-
```json
|
|
262
|
-
{
|
|
263
|
-
"id": "US-01",
|
|
264
|
-
"type": "user-story",
|
|
265
|
-
"title": "As a... I want... so that...",
|
|
266
|
-
"commit": { "type": "feat", "subject": "implement US-01 user management" },
|
|
267
|
-
"sourceUC": ["UC-01"],
|
|
268
|
-
"context": "2-4 sentences: why this phase exists, how its own tasks relate to each other, and any dependency/sequencing reason other phases need to know before opening this phase's task files. Never restate the title.",
|
|
269
|
-
"taskIds": ["US-01-TASK-DB-01", "US-01-TASK-BE-01"]
|
|
270
|
-
}
|
|
271
|
-
```
|
|
272
|
-
|
|
273
|
-
- `phase.commit.subject` must not contain `|`, CR, or LF — `wb-render.js` constructs the full conventional message from it
|
|
274
|
-
- `phase.context` is persisted verbatim into this phase's own `wbs/user-stories/{id}.md` detail file (the host does not re-derive or summarize it) — it is the one piece of phase-detail content that genuinely needs your judgment; "Tasks in this phase" and "Depends on phases" are both derived mechanically by the host from `taskIds`/each task's own `title`/`dependsOn`, so do not restate those here
|
|
275
|
-
- Each phase must have at least one task
|
|
276
|
-
|
|
277
|
-
**Task entry (in the `tasks` map, keyed by its own ID):**
|
|
278
|
-
|
|
279
|
-
```json
|
|
280
|
-
{
|
|
281
|
-
"title": "Create base repository interface",
|
|
282
|
-
"outcome": "IRepository<T> interface exists at src/interfaces/IRepository.ts with CRUD method signatures",
|
|
283
|
-
"domain": "BE",
|
|
284
|
-
"agentType": "developer-backend",
|
|
285
|
-
"dependsOn": [],
|
|
286
|
-
"acceptanceCriteria": [],
|
|
287
|
-
"outputCount": 1,
|
|
288
|
-
"commit": { "type": "feat", "subject": "add IRepository base interface" }
|
|
289
|
-
}
|
|
290
|
-
```
|
|
291
|
-
|
|
292
|
-
**Full example response:**
|
|
293
|
-
|
|
294
|
-
```json
|
|
295
|
-
{
|
|
296
|
-
"schemaVersion": 3,
|
|
297
|
-
"feature": "FTR-NNN",
|
|
298
43
|
"phases": [
|
|
299
|
-
{
|
|
300
|
-
"
|
|
301
|
-
|
|
302
|
-
"
|
|
303
|
-
|
|
304
|
-
"sourceUC": [],
|
|
305
|
-
"context": "Shared repository interface every other phase's data-access tasks depend on — opened first so downstream phases never block on it later.",
|
|
306
|
-
"taskIds": ["INFRA-TASK-BE-01"]
|
|
307
|
-
},
|
|
308
|
-
{
|
|
309
|
-
"id": "US-01",
|
|
310
|
-
"type": "user-story",
|
|
311
|
-
"title": "As an admin, I want to create a user, so that new members can access the system",
|
|
312
|
-
"commit": { "type": "feat", "subject": "implement US-01 user creation" },
|
|
313
|
-
"sourceUC": ["UC-01"],
|
|
314
|
-
"context": "Covers the full create-user path end to end: the entity/migration task establishes storage, the service task implements validation and persistence on top of it.",
|
|
315
|
-
"taskIds": ["US-01-TASK-DB-01", "US-01-TASK-BE-01"]
|
|
316
|
-
}
|
|
317
|
-
],
|
|
318
|
-
"tasks": {
|
|
319
|
-
"INFRA-TASK-BE-01": {
|
|
320
|
-
"title": "Create base repository interface",
|
|
321
|
-
"outcome": "IRepository<T> interface exists at src/interfaces/IRepository.ts with CRUD method signatures",
|
|
322
|
-
"domain": "BE",
|
|
323
|
-
"agentType": "developer-backend",
|
|
324
|
-
"dependsOn": [],
|
|
325
|
-
"acceptanceCriteria": [],
|
|
326
|
-
"outputCount": 1,
|
|
327
|
-
"commit": { "type": "feat", "subject": "add IRepository base interface" }
|
|
328
|
-
},
|
|
329
|
-
"US-01-TASK-DB-01": {
|
|
330
|
-
"title": "Create User entity and migration",
|
|
331
|
-
"outcome": "User entity class and initial migration exist; database table is created on migration run",
|
|
332
|
-
"domain": "DB",
|
|
333
|
-
"agentType": "developer-backend",
|
|
334
|
-
"dependsOn": ["INFRA-TASK-BE-01"],
|
|
335
|
-
"acceptanceCriteria": ["AC-01", "AC-03"],
|
|
336
|
-
"outputCount": 1,
|
|
337
|
-
"commit": { "type": "feat", "subject": "add User entity and migration" }
|
|
338
|
-
},
|
|
339
|
-
"US-01-TASK-BE-01": {
|
|
340
|
-
"title": "Implement CreateUserService",
|
|
341
|
-
"outcome": "CreateUserService class exists with createUser method; validates input, persists User, returns created entity",
|
|
342
|
-
"domain": "BE",
|
|
343
|
-
"agentType": "developer-backend",
|
|
344
|
-
"dependsOn": ["US-01-TASK-DB-01"],
|
|
345
|
-
"acceptanceCriteria": ["AC-02"],
|
|
346
|
-
"outputCount": 1,
|
|
347
|
-
"commit": { "type": "feat", "subject": "add CreateUserService" }
|
|
348
|
-
}
|
|
349
|
-
}
|
|
44
|
+
{ "id": "INFRA", "type": "infrastructure", "title": "Shared infrastructure setup",
|
|
45
|
+
"commit": { "type": "feat", "subject": "shared infrastructure" }, "sourceUC": [] },
|
|
46
|
+
{ "id": "US-01", "type": "user-story", "title": "As an admin, I want...",
|
|
47
|
+
"commit": { "type": "feat", "subject": "implement US-01 ..." }, "sourceUC": ["UC-01"] }
|
|
48
|
+
]
|
|
350
49
|
}
|
|
351
50
|
```
|
|
352
51
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
2. Concrete options (2–4) representing reasonable decomposition choices
|
|
361
|
-
3. **Always include** an option: "Leave as open point to discuss later"
|
|
362
|
-
|
|
363
|
-
Do NOT guess or invent task decompositions when the source is unclear. Ask first, then continue.
|
|
364
|
-
|
|
365
|
-
---
|
|
52
|
+
- Map each Use Case to a User Story phase (`US-NN` matching the UC by default; see Guidelines
|
|
53
|
+
below for when `sourceUC` legitimately diverges from the number).
|
|
54
|
+
- INFRA phase (if shared prerequisites exist across multiple User Stories) appears first, with
|
|
55
|
+
`sourceUC: []`.
|
|
56
|
+
- **Do not create an additional phase for end-to-end, integration, or overall verification** —
|
|
57
|
+
every phase traces to exactly one UC's worth of work (plus the single INFRA phase).
|
|
58
|
+
- `commit.subject` must not contain `|`, CR, or LF.
|
|
366
59
|
|
|
367
60
|
## Guidelines
|
|
368
61
|
|
|
369
|
-
-
|
|
370
|
-
|
|
371
|
-
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
-
|
|
377
|
-
|
|
378
|
-
-
|
|
379
|
-
-
|
|
380
|
-
|
|
381
|
-
- **schemaVersion must be exactly 3** — do not omit it, do not use any other value
|
|
382
|
-
- **No pipes, CR, or LF** in `phase.title`, `task.title`, or `commit.subject` — these characters break CSV generation
|
|
383
|
-
- **Traceability**: every UC must map to a US phase; every Must-priority AC must be covered by at least one task's `acceptanceCriteria` **within that AC's own owning US phase** (per its "Related UC" scope) — not by a task in an earlier or later phase, even one that contributes toward satisfying it eventually
|
|
62
|
+
- Derive every phase from the Requirements and Tech-Spec — do not invent a phase not grounded in
|
|
63
|
+
them.
|
|
64
|
+
- `sourceUC` must reflect what a phase's own (eventual) tasks will actually implement, not its
|
|
65
|
+
number — if a phase's work spans more than one UC, or a UC's own work is split across more than
|
|
66
|
+
one phase, decide `sourceUC` from the REAL decomposition, not ID numbering. This decision is
|
|
67
|
+
FINAL: `sourceUC` is handed to the per-phase agent as a fixed input (it scopes that phase's own
|
|
68
|
+
Requirements excerpt) and cannot be revised downstream — get it right here.
|
|
69
|
+
- INFRA phase first (if present), then US phases in priority order (Must before Should before
|
|
70
|
+
Could).
|
|
71
|
+
- `schemaVersion` must be exactly `3`.
|
|
72
|
+
- Write in English. No pipes, CR, or LF in `title` or `commit.subject` — these break CSV
|
|
73
|
+
generation downstream.
|
|
@@ -5,33 +5,40 @@ export const meta = {
|
|
|
5
5
|
phases: [{ title: 'Work Breakdown' }, { title: 'Effort Estimate' }],
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
// ──
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
|
|
8
|
+
// ── Phase-enumeration + per-phase JSON schemas (FTR-033) ─────────────────────
|
|
9
|
+
// FTR-033 splits the single monolithic skeleton call (deciding the ENTIRE feature's task graph in
|
|
10
|
+
// one turn — confirmed to fail reliably at real feature scale, independent of prompt size or
|
|
11
|
+
// dispatch mechanism) into two bounded tiers: enumeration (this schema) decides only the ordered
|
|
12
|
+
// phase list; PHASE_TASKS_SCHEMA below decides one phase's own tasks per call.
|
|
13
|
+
const PHASE_ENUMERATION_SCHEMA = {
|
|
13
14
|
type: 'object',
|
|
14
|
-
required: ['schemaVersion', 'feature', 'phases'
|
|
15
|
+
required: ['schemaVersion', 'feature', 'phases'],
|
|
15
16
|
properties: {
|
|
16
17
|
schemaVersion: { const: 3 },
|
|
17
18
|
feature: { type: 'string' },
|
|
18
19
|
phases: { type: 'array', items: {
|
|
19
20
|
type: 'object',
|
|
20
|
-
required: ['id', 'type', 'title', 'commit', 'sourceUC'
|
|
21
|
+
required: ['id', 'type', 'title', 'commit', 'sourceUC'],
|
|
21
22
|
properties: {
|
|
22
23
|
id: { type: 'string' }, type: { enum: ['infrastructure', 'user-story'] },
|
|
23
24
|
title: { type: 'string' },
|
|
24
25
|
commit: { type: 'object', required: ['type', 'subject'],
|
|
25
26
|
properties: { type: { type: 'string' }, subject: { type: 'string' } } },
|
|
26
27
|
sourceUC: { type: 'array', items: { type: 'string' } },
|
|
27
|
-
// 2-4 sentences: why this phase exists, how its tasks relate, any sequencing note other
|
|
28
|
-
// phases/tasks need before opening this phase's own task files — the one part of the
|
|
29
|
-
// FTR-020 phase-detail template that genuinely needs narrative judgment, not something
|
|
30
|
-
// the host can derive mechanically the way it does Source UC/Tasks-in-phase/Depends-on.
|
|
31
|
-
context: { type: 'string' },
|
|
32
|
-
taskIds: { type: 'array', items: { type: 'string' } },
|
|
33
28
|
},
|
|
34
29
|
} },
|
|
30
|
+
},
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const PHASE_TASKS_SCHEMA = {
|
|
34
|
+
type: 'object',
|
|
35
|
+
required: ['context', 'tasks'],
|
|
36
|
+
properties: {
|
|
37
|
+
// 2-4 sentences: why this phase exists, how its tasks relate, any sequencing note other
|
|
38
|
+
// phases/tasks need before opening this phase's own task files — the one part of the
|
|
39
|
+
// FTR-020 phase-detail template that genuinely needs narrative judgment, not something
|
|
40
|
+
// the host can derive mechanically the way it does Source UC/Tasks-in-phase/Depends-on.
|
|
41
|
+
context: { type: 'string' },
|
|
35
42
|
tasks: { type: 'object', additionalProperties: {
|
|
36
43
|
type: 'object',
|
|
37
44
|
required: ['title', 'outcome', 'domain', 'agentType', 'dependsOn',
|
|
@@ -58,11 +65,13 @@ const SKELETON_SCHEMA = {
|
|
|
58
65
|
} },
|
|
59
66
|
// Bounded drill-down (mirrors pm-phase1.js's own needsComponentDetail precedent for the
|
|
60
67
|
// feature-doc validator, FTR-028) — declared in `properties` only, never `required`: the
|
|
61
|
-
// common case is an empty array, costing nothing extra. See
|
|
62
|
-
// own "Requesting Component Detail" section for when
|
|
63
|
-
// of self-exploring spec/components/* (the real,
|
|
64
|
-
// feature — confirmed against a real run: 1.18M of
|
|
65
|
-
// were cache_read_input_tokens, not the declared
|
|
68
|
+
// common case is an empty array, costing nothing extra. See
|
|
69
|
+
// gaia-generate-work-breakdown-phase.md's own "Requesting Component Detail" section for when
|
|
70
|
+
// the agent should populate this instead of self-exploring spec/components/* (the real,
|
|
71
|
+
// evidenced dominant cost driver on a large feature — confirmed against a real run: 1.18M of
|
|
72
|
+
// 1.38M total tokens on one failing attempt were cache_read_input_tokens, not the declared
|
|
73
|
+
// output schema size). FTR-033: available per-phase, not at the enumeration tier — phase-level
|
|
74
|
+
// decisions (id/type/title/sourceUC/commit) never need a component's own design body.
|
|
66
75
|
needsComponentDetail: { type: 'array', items: { type: 'object',
|
|
67
76
|
properties: { fileSlug: { type: 'string' }, reason: { type: 'string' } }, required: ['fileSlug'] } },
|
|
68
77
|
},
|
|
@@ -74,12 +83,15 @@ const SKELETON_SCHEMA = {
|
|
|
74
83
|
// exception, reserved for a genuine per-task decision gap, not a substitute for broad exploration.
|
|
75
84
|
const SKELETON_DRILL_DOWN_MAX_COMPONENTS = 20
|
|
76
85
|
|
|
77
|
-
// Builds the
|
|
86
|
+
// Builds the phase-enumeration prompt: Requirements FULL text + Tech-Spec SKELETON ONLY
|
|
78
87
|
// (never per-component bodies) inlined directly, mirroring pm-phase1.js's own
|
|
79
88
|
// 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
|
-
|
|
82
|
-
|
|
89
|
+
// document or any per-item file via its own Read/Glob/Grep tool access. No drillDownText
|
|
90
|
+
// parameter — the enumeration call never declares needsComponentDetail (FTR-033: phase-level
|
|
91
|
+
// decisions never need a component's own design body; that mechanism now lives only at the
|
|
92
|
+
// per-phase tier, see buildPhaseTaskPrompt below).
|
|
93
|
+
function buildPhaseEnumerationPrompt(featurePath, requirementsText, techSpecSkeletonText) {
|
|
94
|
+
return [
|
|
83
95
|
featurePath,
|
|
84
96
|
'',
|
|
85
97
|
'Requirements:',
|
|
@@ -87,11 +99,7 @@ function buildSkeletonPrompt(featurePath, requirementsText, techSpecSkeletonText
|
|
|
87
99
|
'',
|
|
88
100
|
'Tech-Spec:',
|
|
89
101
|
techSpecSkeletonText,
|
|
90
|
-
]
|
|
91
|
-
if (drillDownText) {
|
|
92
|
-
parts.push('', 'Component detail you requested:', drillDownText)
|
|
93
|
-
}
|
|
94
|
-
return parts.join('\n')
|
|
102
|
+
].join('\n')
|
|
95
103
|
}
|
|
96
104
|
|
|
97
105
|
// Validates each needsComponentDetail entry's fileSlug against the real component table (a
|
|
@@ -113,19 +121,20 @@ function buildComponentDrillDownText(needsComponentDetail, specIndexContent, spe
|
|
|
113
121
|
}).join('\n\n---\n\n')
|
|
114
122
|
}
|
|
115
123
|
|
|
116
|
-
//
|
|
117
|
-
// (
|
|
118
|
-
//
|
|
119
|
-
//
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
const phases =
|
|
126
|
-
id: p.id, type: p.type,
|
|
124
|
+
// Materializes the enumeration response into index.json's structural, map-keyed shape.
|
|
125
|
+
// FTR-033 (BR-09): title/commit ARE additively persisted here (unlike the old materializeIndex,
|
|
126
|
+
// which deliberately left them out, OQ-04) — a resumed run between phase-enumeration and the last
|
|
127
|
+
// phase's completion must be able to re-render any EARLIER-but-already-completed phase's own
|
|
128
|
+
// detail file (which needs title/commit) without re-calling the phase-enumeration agent.
|
|
129
|
+
// `context` is still NOT persisted here — it is only known once that phase's own per-phase call
|
|
130
|
+
// returns, and is rendered straight to the phase's detail file at that point (see the per-phase
|
|
131
|
+
// loop below), same "render immediately, don't round-trip through index.json" principle as before.
|
|
132
|
+
function materializeEnumeratedPhases(phaseList) {
|
|
133
|
+
const phases = phaseList.phases.map(p => ({
|
|
134
|
+
id: p.id, type: p.type, title: p.title, commit: p.commit, sourceUC: p.sourceUC,
|
|
135
|
+
detailFile: `user-stories/${p.id}.md`, taskIds: [],
|
|
127
136
|
}))
|
|
128
|
-
return { schemaVersion: 3, feature:
|
|
137
|
+
return { schemaVersion: 3, feature: phaseList.feature, phases, tasks: {} }
|
|
129
138
|
}
|
|
130
139
|
|
|
131
140
|
// Duplicated from src/claude/scripts/wbs-assembler.js's renderPhaseDetailBlock/template-engine
|
|
@@ -278,6 +287,50 @@ function parseComponentsTableLocal(specIndexContent) {
|
|
|
278
287
|
return entries
|
|
279
288
|
}
|
|
280
289
|
|
|
290
|
+
// FTR-033: local re-implementation of req-assembler.js's own parseUseCaseIndexTable, reusing the
|
|
291
|
+
// ALREADY-local _findSectionRange/_parseTableRows this file established for Tech-Spec's own
|
|
292
|
+
// Section 9 table (FTR-026) — same "sandboxed runtime has no require" reason.
|
|
293
|
+
function parseUseCaseIndexTableLocal(reqIndexContent) {
|
|
294
|
+
const { start, end, lines } = _findSectionRange(reqIndexContent, 2)
|
|
295
|
+
return _parseTableRows(lines, start, end).map(cells => {
|
|
296
|
+
const [id, title, actor, priority, fileCol] = cells
|
|
297
|
+
const m = fileCol && fileCol.match(/\(([^)]+)\)/)
|
|
298
|
+
if (!m) throw new Error(`malformed File column for UC "${id}" in req/index.md`)
|
|
299
|
+
return { id, file: m[1] }
|
|
300
|
+
})
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Scopes Requirements to one phase's own sourceUC: req/index.md's feature-wide content (Business
|
|
304
|
+
// Rules/AC table/NFRs, never per-UC) is ALWAYS included in full; only the per-UC flow detail is
|
|
305
|
+
// narrowed to this phase's own UC(s). Falls back to the raw text unchanged for a legacy
|
|
306
|
+
// (non-split) feature — hasReq is false, so sourceUC-based file lookups are meaningless anyway.
|
|
307
|
+
function buildPhaseRequirementsExcerpt(reqIndexContent, sourceUC, reqDirArg) {
|
|
308
|
+
const ucTable = parseUseCaseIndexTableLocal(reqIndexContent)
|
|
309
|
+
const matched = ucTable.filter(row => (sourceUC || []).includes(row.id))
|
|
310
|
+
if (matched.length === 0) return reqIndexContent
|
|
311
|
+
const ucBodies = matched.map(row => c.read(`${reqDirArg}/${row.file}`)).join('\n\n')
|
|
312
|
+
return `${reqIndexContent}\n\n${ucBodies}`
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Compact prompt for the per-phase task-generation worker: this phase's own skeleton entry, its
|
|
316
|
+
// scoped Requirements excerpt, the Tech-Spec skeleton, and every prior phase's own tasks as a
|
|
317
|
+
// lightweight id/title/outcome/domain projection (never a later phase's tasks, which do not exist
|
|
318
|
+
// yet) — mirrors buildTaskPrompt's own "siblings" list further below, same duplicate-
|
|
319
|
+
// implementation-awareness purpose, one tier up.
|
|
320
|
+
function buildPhaseTaskPrompt(phase, requirementsExcerpt, techSpecSkeletonText, priorTasks, drillDownText) {
|
|
321
|
+
const priorTasksList = priorTasks.length
|
|
322
|
+
? priorTasks.map(t => `- ${t.id}: ${t.title} — ${t.outcome} (domain: ${t.domain})`).join('\n')
|
|
323
|
+
: '(none — this is the first phase generated)'
|
|
324
|
+
const parts = [
|
|
325
|
+
`Phase:\n${JSON.stringify(phase, null, 2)}`,
|
|
326
|
+
`Requirements (scoped to this phase's own sourceUC, plus feature-wide sections):\n${requirementsExcerpt}`,
|
|
327
|
+
`Tech-Spec:\n${techSpecSkeletonText}`,
|
|
328
|
+
`Tasks already decided by prior phases:\n${priorTasksList}`,
|
|
329
|
+
]
|
|
330
|
+
if (drillDownText) parts.push(`Component detail you requested:\n${drillDownText}`)
|
|
331
|
+
return parts.join('\n\n')
|
|
332
|
+
}
|
|
333
|
+
|
|
281
334
|
// Matches a task's own acceptanceCriteria against each component's Addresses column (the same
|
|
282
335
|
// relation spec/index.md's own Section 9 table already expresses) to give the per-task worker
|
|
283
336
|
// only the Tech-Spec content relevant to its own task, instead of the entire assembled spec —
|
|
@@ -385,7 +438,11 @@ return c.run(meta.name, featureDir, prefix, async () => {
|
|
|
385
438
|
const wbsDir = `${featureDir}/wbs`
|
|
386
439
|
const indexPath = `${wbsDir}/index.json`
|
|
387
440
|
|
|
388
|
-
// 1.
|
|
441
|
+
// 1. Phase enumeration (no declared worker() output — host writes every file itself). FTR-033:
|
|
442
|
+
// split from the old single monolithic skeleton call (which decided every phase AND every task
|
|
443
|
+
// in one turn — confirmed to fail reliably at real feature scale, independent of prompt size or
|
|
444
|
+
// dispatch mechanism) into this lightweight enumeration tier plus the bounded per-phase loop
|
|
445
|
+
// below.
|
|
389
446
|
let index
|
|
390
447
|
if (!c.exists(indexPath)) {
|
|
391
448
|
// Requirements FULL text + Tech-Spec SKELETON ONLY, inlined directly — same shape as
|
|
@@ -397,35 +454,65 @@ return c.run(meta.name, featureDir, prefix, async () => {
|
|
|
397
454
|
: c.read(file('Requirements.md'))
|
|
398
455
|
const techSpecSkeletonForSkeleton = hasSpec ? c.read(specIndexPath) : c.read(file('Tech-Spec.md'))
|
|
399
456
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
457
|
+
// No needsComponentDetail round here — the enumeration agent never declares or asks for it
|
|
458
|
+
// (PHASE_ENUMERATION_SCHEMA has no such property; gaia-generate-work-breakdown.md's own Output
|
|
459
|
+
// section doesn't mention it either). Phase-level decisions (id/type/title/sourceUC/commit)
|
|
460
|
+
// never need a component's own design body; the drill-down mechanism now lives ONLY at the
|
|
461
|
+
// per-phase tier below, where task-level decisions genuinely can need it.
|
|
462
|
+
const phaseList = await c.worker('gaia.agent.planner.work-breakdown',
|
|
463
|
+
buildPhaseEnumerationPrompt(featurePath, requirementsForSkeleton, techSpecSkeletonForSkeleton),
|
|
464
|
+
{ schema: PHASE_ENUMERATION_SCHEMA })
|
|
465
|
+
|
|
466
|
+
index = materializeEnumeratedPhases(phaseList)
|
|
467
|
+
c.writeOutput(indexPath, JSON.stringify(index, null, 2))
|
|
468
|
+
} else {
|
|
469
|
+
index = c.json(indexPath) // resume: reuse already-enumerated phases / already-generated tasks as-is
|
|
470
|
+
}
|
|
403
471
|
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
472
|
+
// 1b. Per-phase task generation (resume-by-phase: a phase whose taskIds is already non-empty is
|
|
473
|
+
// skipped entirely, never redispatched — BR-04/BR-05). Dispatched strictly in enumerated order:
|
|
474
|
+
// INFRA first (if present), then US phases in priority order (same order gaia-generate-
|
|
475
|
+
// work-breakdown.md's own Guidelines already produce for the phases array).
|
|
476
|
+
const reqIndexContentForPhases = c.read(reqIndexPath)
|
|
477
|
+
const techSpecSkeletonForPhases = hasSpec ? c.read(specIndexPath) : c.read(file('Tech-Spec.md'))
|
|
478
|
+
const taskIdToPhase = new Map()
|
|
479
|
+
for (const phase of index.phases) {
|
|
480
|
+
if (phase.taskIds.length > 0) {
|
|
481
|
+
for (const taskId of phase.taskIds) taskIdToPhase.set(taskId, phase.id)
|
|
482
|
+
continue
|
|
483
|
+
}
|
|
484
|
+
const reqExcerpt = hasReq
|
|
485
|
+
? buildPhaseRequirementsExcerpt(reqIndexContentForPhases, phase.sourceUC, reqDir)
|
|
486
|
+
: reqIndexContentForPhases
|
|
487
|
+
const priorTasks = Object.entries(index.tasks).map(([id, t]) => ({ id, title: t.title, outcome: t.outcome, domain: t.domain }))
|
|
488
|
+
|
|
489
|
+
let phaseResult = await c.worker('gaia.agent.planner.work-breakdown-phase',
|
|
490
|
+
buildPhaseTaskPrompt(phase, reqExcerpt, techSpecSkeletonForPhases, priorTasks),
|
|
491
|
+
{ schema: PHASE_TASKS_SCHEMA, label: `work-breakdown-phase:${phase.id}` })
|
|
492
|
+
|
|
493
|
+
// Bounded, single-round drill-down (FTR-028 precedent, reused unmodified from FTR-032's own
|
|
494
|
+
// whole-feature-skeleton mechanism) — scoped to this one phase's call now.
|
|
495
|
+
const phaseNeedsComponentDetail = phaseResult.needsComponentDetail || []
|
|
496
|
+
if (hasSpec && phaseNeedsComponentDetail.length > 0) {
|
|
497
|
+
const drillDownText = buildComponentDrillDownText(phaseNeedsComponentDetail, c.read(specIndexPath), specDir)
|
|
411
498
|
if (drillDownText) {
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
{ schema:
|
|
499
|
+
phaseResult = await c.worker('gaia.agent.planner.work-breakdown-phase',
|
|
500
|
+
buildPhaseTaskPrompt(phase, reqExcerpt, techSpecSkeletonForPhases, priorTasks, drillDownText),
|
|
501
|
+
{ schema: PHASE_TASKS_SCHEMA, label: `work-breakdown-phase:${phase.id}:with-drilldown` })
|
|
415
502
|
}
|
|
416
503
|
}
|
|
417
504
|
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
const skeletonPhase = skeleton.phases.find(p => p.id === phase.id)
|
|
422
|
-
const taskTitles = skeletonPhase.taskIds.map(taskId => ({ id: taskId, title: skeleton.tasks[taskId].title }))
|
|
423
|
-
const dependsOnPhases = computeDependsOnPhases(skeleton, phase.id, taskIdToPhase)
|
|
424
|
-
c.writeOutput(`${wbsDir}/${phase.detailFile}`, renderPhaseDetailBlock(skeletonPhase, taskTitles, dependsOnPhases))
|
|
505
|
+
for (const [taskId, t] of Object.entries(phaseResult.tasks)) {
|
|
506
|
+
index.tasks[taskId] = { ...t, detailFile: `tasks/${taskId}.md`, status: 'pending' }
|
|
507
|
+
taskIdToPhase.set(taskId, phase.id)
|
|
425
508
|
}
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
509
|
+
phase.taskIds = Object.keys(phaseResult.tasks)
|
|
510
|
+
|
|
511
|
+
const taskTitles = phase.taskIds.map(taskId => ({ id: taskId, title: index.tasks[taskId].title }))
|
|
512
|
+
const dependsOnPhases = computeDependsOnPhases(index, phase.id, taskIdToPhase)
|
|
513
|
+
c.writeOutput(`${wbsDir}/${phase.detailFile}`,
|
|
514
|
+
renderPhaseDetailBlock({ ...phase, context: phaseResult.context }, taskTitles, dependsOnPhases))
|
|
515
|
+
c.writeOutput(indexPath, JSON.stringify(index, null, 2)) // persist BEFORE the next phase's call (BR-04)
|
|
429
516
|
}
|
|
430
517
|
|
|
431
518
|
// 2. Structural pre-checks (wb-skeleton-check.js, same wb-graph-checks.js functions
|