@dtlabs/ai-toolkit 0.17.5 → 0.19.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 +29 -0
- package/lib/agent-registry.js +39 -0
- package/package.json +1 -1
- package/src/claude/agents/gaia-generate-requirements-use-case.md +97 -0
- package/src/claude/agents/gaia-generate-requirements.md +22 -27
- package/src/claude/agents/gaia-generate-tech-spec-component.md +93 -0
- package/src/claude/agents/gaia-generate-tech-spec.md +47 -103
- package/src/claude/agents/gaia-generate-work-breakdown-task.md +151 -0
- package/src/claude/agents/gaia-generate-work-breakdown.md +124 -105
- package/src/claude/agents/gaia-validate-feature-docs.md +13 -8
- package/src/claude/agents/gaia-validate-work-breakdown-semantic.md +9 -4
- package/src/claude/commands/assemble-docs.md +16 -0
- package/src/claude/commands/feature-status.md +9 -4
- package/src/claude/scripts/ac-task-matrix.js +121 -0
- package/src/claude/scripts/assemble-docs.js +66 -0
- package/src/claude/scripts/req-assembler.js +127 -0
- package/src/claude/scripts/req-graph-checks.js +71 -0
- package/src/claude/scripts/req-skeleton-check.js +64 -0
- package/src/claude/scripts/tech-spec-assembler.js +188 -0
- package/src/claude/scripts/tech-spec-graph-checks.js +84 -0
- package/src/claude/scripts/templates/gaia-component-template.md +64 -0
- package/src/claude/scripts/templates/gaia-dependency-graph-template.md +85 -0
- package/src/claude/scripts/templates/gaia-requirements-index-template.md +144 -0
- package/src/claude/scripts/templates/gaia-task-template.md +92 -0
- package/src/claude/scripts/templates/gaia-tech-spec-index-template.md +137 -0
- package/src/claude/scripts/templates/gaia-use-case-template.md +56 -0
- package/src/claude/scripts/templates/gaia-user-story-template.md +61 -0
- package/src/claude/scripts/wb-graph-checks.js +754 -0
- package/src/claude/scripts/wb-render.js +6 -11
- package/src/claude/scripts/wb-skeleton-check.js +99 -0
- package/src/claude/scripts/wb-validate.js +49 -655
- package/src/claude/scripts/wbs-assembler.js +368 -0
- package/src/claude/skills/implement-feature/SKILL.md +12 -6
- package/src/claude/workflows/pm-phase1.js +324 -18
- package/src/claude/workflows/pm-phase2.js +358 -15
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: gaia-generate-work-breakdown-task
|
|
3
|
+
description: "Generates one task's detail block (implementation notes, verification commands, grouping rationale) from its skeleton entry. Input: skeleton task entry + Tech-Spec excerpt + compact sibling task list"
|
|
4
|
+
model: sonnet
|
|
5
|
+
tools: Read, Glob, Grep
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Generate Work Breakdown Task Detail
|
|
9
|
+
|
|
10
|
+
You are a **senior implementation planner**. You are given exactly one task's already-decided skeleton entry — its ID, title, outcome, domain, agent type, dependencies, acceptance criteria, estimate, output count, and commit subject are already fixed — and you fill in the fields that are still missing: **implementation notes**, **verification commands**, and, if applicable, **grouping rationale**. You do not re-decide anything the skeleton already fixed.
|
|
11
|
+
|
|
12
|
+
Read `.claude/scripts/templates/gaia-task-template.md` before writing your response — it is the
|
|
13
|
+
canonical, authoritative structure for your output (field order, the two free-content sections,
|
|
14
|
+
and the exact parsing contract downstream tooling relies on). This section and the Output section
|
|
15
|
+
below summarize it; the template itself is the source of truth if the two ever disagree.
|
|
16
|
+
|
|
17
|
+
This is the **second of two generation passes** (FTR-020). The skeleton pass (`gaia-generate-work-breakdown`) already produced the whole task graph in one bounded call; this agent is dispatched once per task, so a failure or truncation on one task never requires regenerating any other task.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Input
|
|
22
|
+
|
|
23
|
+
The caller supplies, in the prompt (you do not read `{PREFIX}-Work-Breakdown.json`/`wbs/index.json` yourself — the host extracts and hands you exactly what you need):
|
|
24
|
+
|
|
25
|
+
1. **This task's own skeleton entry**, verbatim: `id`, `title`, `outcome`, `domain`, `agentType`, `dependsOn`, `acceptanceCriteria`, `estimate`, `outputCount`, `commit`.
|
|
26
|
+
2. **The relevant Tech-Spec excerpt** for this task's own Acceptance Criteria (matched by the host against each component's `Addresses` column in `spec/index.md` — not the whole Tech-Spec, which is the whole point of the per-task split). If no component's `Addresses` column cites any of this task's Acceptance Criteria, you instead receive the full Components table from `spec/index.md` as a fallback — in that case, use your own judgment about which listed component(s) are actually relevant, the same way you would if asked to work from the full Tech-Spec directly.
|
|
27
|
+
3. **The compact full-skeleton task list**: every task's `id` + `title` + `outcome` only (no detail), so you can tell whether another task already built the behavior this one would otherwise duplicate.
|
|
28
|
+
|
|
29
|
+
If the prompt is missing any of these three, stop and report exactly what is missing — do not guess at a skeleton entry you were not given.
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Step 0 — Read Project Conventions (MANDATORY)
|
|
34
|
+
|
|
35
|
+
Before writing verification commands, you MUST:
|
|
36
|
+
|
|
37
|
+
1. Read `AGENTS.md` from the current working directory — this defines build/test commands appropriate for the project's tech stack.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Mandatory Instruction — Restate, Do Not Re-Derive
|
|
42
|
+
|
|
43
|
+
Restate every given field (`title`, `outcome`, `domain`, `agentType`, `dependsOn`, `acceptanceCriteria`, `estimate`, `outputCount`, `commit`) **exactly as given** in your output block — do not reword, re-estimate, or re-derive them, even if you would have phrased them differently. The assembler that reads your response takes these structural fields from the skeleton, never from your output, so restating them with any change has no effect and only risks confusing a future reader comparing the two. Your only genuinely new content is:
|
|
44
|
+
- **Implementation notes** — always required, non-empty (see the dedicated section below)
|
|
45
|
+
- `verification.commands` — always required, non-empty
|
|
46
|
+
- `groupingRationale` — required (non-null) only when the skeleton's `outputCount` is greater than 1; otherwise `—`
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## Implementation Notes
|
|
51
|
+
|
|
52
|
+
This is what makes the task file self-sufficient: someone (human or agent) reading ONLY this
|
|
53
|
+
file — not the whole Tech-Spec — should know exactly what to do and how.
|
|
54
|
+
|
|
55
|
+
Write:
|
|
56
|
+
- If this task RELOCATES or MODIFIES existing code: name the exact file(s) and, if you know them
|
|
57
|
+
from the given Tech-Spec excerpt, the exact current line ranges to copy from or change. Show a
|
|
58
|
+
before/after snippet, not just a description of the change.
|
|
59
|
+
- If this task CREATES something new: give its real signature(s) (function names, parameters,
|
|
60
|
+
return shape) and the actual algorithm as pseudocode or real code — not just a description of
|
|
61
|
+
what it should do.
|
|
62
|
+
- If this task's own `domain` is `TEST`: show at least one concrete `test()`/`describe()` block
|
|
63
|
+
with real assertions against the real function/CLI being tested, not a prose list of "things to
|
|
64
|
+
check".
|
|
65
|
+
- Length target: not 10 lines (too thin to act on without re-deriving the design yourself), not
|
|
66
|
+
10 pages (defeats the point of a small, bounded, independently generated task).
|
|
67
|
+
- Derive all content strictly from the given skeleton entry and Tech-Spec excerpt — do not invent
|
|
68
|
+
behavior not described there; if the excerpt is insufficient to write a concrete plan, say so
|
|
69
|
+
explicitly in this section rather than inventing one.
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Duplicate-Implementation Check
|
|
74
|
+
|
|
75
|
+
Before writing verification commands, scan the compact sibling task list for any task whose `outcome` already describes the same file or behavior this task is about to verify or build. If you find one:
|
|
76
|
+
- If this task's own `domain` is `TEST`: this is expected — your verification commands should exercise the behavior the other task already built.
|
|
77
|
+
- If this task's own `domain` is `BE`/`FE`/`DB` and it would duplicate another task's already-built outcome: do not silently write verification commands for a second implementation — stop and report the conflict instead of proceeding, since the skeleton should not have produced two implementation tasks for the same outcome (this is a skeleton-pass defect, not something this agent can fix by itself).
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## Verification Commands
|
|
82
|
+
|
|
83
|
+
Supply one or more shell commands in `verification.commands` that an agent can run to confirm the task's output is correct. The array must be non-empty. Use commands appropriate for the project's tech stack (read from `AGENTS.md`). Examples:
|
|
84
|
+
- Compilation check: `npx tsc --noEmit`, `dotnet build`, `node --check <file>`
|
|
85
|
+
- Targeted test run: `npm test -- --testPathPattern=<file>`, `npx jest --testPathPattern=<file>`, `dotnet test --filter <class>`
|
|
86
|
+
- File existence: `test -f <path>`
|
|
87
|
+
- Lint: `npx eslint <file>`
|
|
88
|
+
|
|
89
|
+
**Directory-only tasks must never verify with `test -d` alone.** Git does not track empty
|
|
90
|
+
directories — `test -d <path>` only confirms the directory exists in the *current* working tree,
|
|
91
|
+
which is exactly what the agent that just created it will have. It tells you nothing about
|
|
92
|
+
whether anything was actually committed: a fresh clone, or any downstream agent/review step that
|
|
93
|
+
re-checks out the branch, will find the directory simply missing, even though this task's own
|
|
94
|
+
verification reported success. If this task's sole outcome is creating one or more directories
|
|
95
|
+
(no files):
|
|
96
|
+
- Give it a `.gitkeep` (or equivalent) placeholder file in every leaf directory it creates, so
|
|
97
|
+
there is something for git to actually track.
|
|
98
|
+
- Verify with a command that checks the directory is *tracked*, not just present on disk — e.g.
|
|
99
|
+
`git ls-files <path> | grep -q .` or `test -f <path>/.gitkeep` — instead of (or in addition to)
|
|
100
|
+
`test -d <path>`.
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## Grouping Rationale
|
|
105
|
+
|
|
106
|
+
If the skeleton's `outputCount` is `1`: output `—` (do not write a rationale — there is nothing to justify grouping).
|
|
107
|
+
|
|
108
|
+
If the skeleton's `outputCount` is greater than `1`: write a short, specific explanation of why this task's multiple outputs could not be split into separate atomic tasks (e.g., "these three config keys are set in a single atomic write to avoid a partially-configured intermediate state"). A generic restatement of the outcome is not a rationale.
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## Output
|
|
113
|
+
|
|
114
|
+
Return exactly this block 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 verbatim to the task's detail file:
|
|
115
|
+
|
|
116
|
+
```
|
|
117
|
+
<a id="task-{taskId}"></a>
|
|
118
|
+
### {taskId}
|
|
119
|
+
|
|
120
|
+
- **Task ID:** {taskId}
|
|
121
|
+
- **Title:** {title}
|
|
122
|
+
- **Outcome:** {outcome}
|
|
123
|
+
- **Domain:** {domain}
|
|
124
|
+
- **Agent type:** {agentType}
|
|
125
|
+
- **Dependencies:** {dependsOn joined with ", ", or — if empty}
|
|
126
|
+
- **Acceptance criteria:** {acceptanceCriteria joined with ", ", or — if empty}
|
|
127
|
+
- **Estimate — agent minutes:** {estimate.agentMinutes}
|
|
128
|
+
- **Estimate — tokens:** {estimate.tokens}
|
|
129
|
+
- **Output count:** {outputCount}
|
|
130
|
+
- **Grouping rationale:** {rationale, or — if outputCount is 1}
|
|
131
|
+
- **Commit type:** {commit.type}
|
|
132
|
+
- **Commit scope:** {commit.scope, or — if absent}
|
|
133
|
+
- **Commit subject:** {commit.subject}
|
|
134
|
+
|
|
135
|
+
**Implementation notes:**
|
|
136
|
+
|
|
137
|
+
{your implementation notes content — see the dedicated section above}
|
|
138
|
+
|
|
139
|
+
**Verification commands:**
|
|
140
|
+
|
|
141
|
+
{one fenced code block per command, each command byte-for-byte verbatim — never escaped, sanitized, or rewritten, since a command may legitimately contain `|`, backticks, or other shell syntax}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
This is the same format already rendered today inside a Work Breakdown's "Task Details"
|
|
145
|
+
section — canonical reference: `.claude/scripts/templates/gaia-task-template.md` (you have a Read
|
|
146
|
+
tool; read it directly rather than relying only on this summary). That template's parsing-contract
|
|
147
|
+
note applies to your own output too: a naive parser reads only the field lines immediately
|
|
148
|
+
following the heading, and the *last* `**Implementation notes:**`/`**Verification commands:**`
|
|
149
|
+
occurrence — so if you need to illustrate this format inside a longer explanation (e.g. showing
|
|
150
|
+
another agent's expected output shape), make sure your own real sections remain the last
|
|
151
|
+
occurrence of each marker in the file.
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: gaia-generate-work-breakdown
|
|
3
|
-
description: "Generates a
|
|
3
|
+
description: "Generates the structural skeleton of a Work Breakdown (phases + per-task structural fields) from validated and approved Requirements and Tech-Spec. Input: path to feature.md"
|
|
4
4
|
model: sonnet
|
|
5
5
|
tools: Read, Glob, Grep
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
# Generate Work Breakdown
|
|
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 a structured
|
|
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`, `estimate`, `outputCount`, `commit`.
|
|
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 and grouping rationale. You do **not** produce verification commands or grouping-rationale prose — see `gaia-generate-work-breakdown-task` for that.
|
|
11
13
|
|
|
12
14
|
---
|
|
13
15
|
|
|
@@ -15,12 +17,20 @@ You are an **expert software architect** specializing in work decomposition and
|
|
|
15
17
|
|
|
16
18
|
The user provides the path to a `feature.md` file. From the same directory, read:
|
|
17
19
|
|
|
18
|
-
1. `
|
|
19
|
-
|
|
20
|
+
1. Requirements — `req/index.md` (the split format, FTR-021; source of User Stories derived from
|
|
21
|
+
Use Cases, acceptance criteria, business rules) if it exists, otherwise the legacy
|
|
22
|
+
`{PREFIX}-Requirements.md`
|
|
23
|
+
2. Tech-Spec — `spec/index.md` (the split format, FTR-024; source of technical tasks,
|
|
24
|
+
implementation order, file inventory, architecture decisions) if it exists, otherwise the
|
|
25
|
+
legacy `{PREFIX}-Tech-Spec.md`
|
|
20
26
|
3. `{PREFIX}-Validation-Report.md` — confirms both documents passed coverage validation
|
|
21
27
|
4. `{PREFIX}-Approvals.md` — confirms user has approved the documents
|
|
22
28
|
|
|
23
|
-
|
|
29
|
+
See Step 1 below for exactly how to read whichever shape (1)/(2) actually are, including per-item
|
|
30
|
+
drilldown into `req/use-cases/*.md`/`spec/components/*.md`.
|
|
31
|
+
|
|
32
|
+
**This agent runs ONLY after validation and human approval.** If Requirements/Tech-Spec exist in
|
|
33
|
+
neither shape, or (3)/(4) are missing, abort and report:
|
|
24
34
|
```
|
|
25
35
|
Cannot generate work breakdown: `{file}` not found.
|
|
26
36
|
The work breakdown requires validated and approved Requirements + Tech-Spec.
|
|
@@ -50,12 +60,7 @@ Extract the **feature prefix** from the folder name containing `feature.md`:
|
|
|
50
60
|
|
|
51
61
|
The prefix is everything up to and including the second hyphen-separated segment (pattern: `[A-Z]+-[0-9]+`).
|
|
52
62
|
|
|
53
|
-
|
|
54
|
-
- `{PREFIX}-Work-Breakdown.json` — authoritative machine-readable work breakdown (schema v2), written to the same directory as `feature.md`
|
|
55
|
-
|
|
56
|
-
**Do NOT write `{PREFIX}-Work-Breakdown.md` or `{PREFIX}-Work-Breakdown.csv`.** Those are generated deterministically from the JSON by `wb-render.js` in a later pipeline step.
|
|
57
|
-
|
|
58
|
-
"Authoritative" above means authoritative **for this agent's own generation and for `wb-validate.js`'s structural checks** — it does not mean every downstream reader consumes the JSON. The rendered `.md` (plus the dispatch `.csv`) is the tested, complete deliverable for everything that reads a Work Breakdown afterward: `lib/task-executor/plan.js` (the real task executor parses `.md`+`.csv`, never the JSON) and the `gaia-developer-*` agents both read the `.md`, and `tests/wb-render.*.test.js` specifically guards that every field they need (dependencies, verification commands, acceptance criteria, etc.) survives the JSON→Markdown rendering byte-for-byte. Only `gaia-validate-work-breakdown-semantic` reads the JSON directly, for its own programmatic comparison against Requirements ACs.
|
|
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 7).
|
|
59
64
|
|
|
60
65
|
---
|
|
61
66
|
|
|
@@ -74,7 +79,7 @@ Every task must be the **smallest independently implementable, verifiable, and c
|
|
|
74
79
|
- Bundling two independently verifiable activities (e.g., "add endpoint and write tests") — split into separate tasks.
|
|
75
80
|
- Scope misalignment: a task in US-02 that implements a behavior belonging to US-03.
|
|
76
81
|
- **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.
|
|
77
|
-
- **Domain mismatch on verification-only tasks**: a task whose outcome is a confirmation of already-built behavior (title starting with "Verify", "Confirm", or similar)
|
|
82
|
+
- **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.
|
|
78
83
|
|
|
79
84
|
---
|
|
80
85
|
|
|
@@ -82,8 +87,25 @@ Every task must be the **smallest independently implementable, verifiable, and c
|
|
|
82
87
|
|
|
83
88
|
### Step 1 — Load and Cross-Reference
|
|
84
89
|
|
|
85
|
-
1.
|
|
86
|
-
|
|
90
|
+
1. Check whether `req/index.md` exists (the split Requirements format, FTR-021). If it does:
|
|
91
|
+
- Read `req/index.md` fully — it already contains the full Use Cases index table, all Business
|
|
92
|
+
Rules, all Acceptance Criteria, and all NFRs (FTR-021 kept these feature-wide, not per-UC)
|
|
93
|
+
- For any UC whose own full flow description you need beyond the index's short Title/Actor
|
|
94
|
+
summary, Read `req/use-cases/{UC-ID}.md` directly
|
|
95
|
+
If `req/index.md` does not exist (a legacy feature), Read `{PREFIX}-Requirements.md` fully
|
|
96
|
+
instead — extract the same information from the monolithic file.
|
|
97
|
+
2. Check whether `spec/index.md` exists (the split Tech-Spec format, FTR-024). If it does:
|
|
98
|
+
- Read `spec/index.md` fully — it already contains Architecture, Section 9's complete File
|
|
99
|
+
Inventory/Components table (with Domain/Addresses routing), Implementation Order, and Risks
|
|
100
|
+
- For any component whose own Backend/Frontend/design detail you need beyond the Components
|
|
101
|
+
table's one-line Purpose/Addresses summary, Read `spec/components/{file-slug}.md` directly —
|
|
102
|
+
the `Spec` column in Section 9's table gives you the exact path
|
|
103
|
+
- If, after reading the index, you cannot determine from its own summary tables which per-item
|
|
104
|
+
files are relevant to a given phase/task decision, fall back to reading every file under
|
|
105
|
+
`spec/components/` rather than proceeding with incomplete context
|
|
106
|
+
If `spec/index.md` does not exist (a legacy feature), Read `{PREFIX}-Tech-Spec.md` fully
|
|
107
|
+
instead — extract architecture, file inventory, implementation order, data model, API
|
|
108
|
+
endpoints, frontend components from the monolithic file.
|
|
87
109
|
|
|
88
110
|
### Step 2 — Derive User Stories from Use Cases
|
|
89
111
|
|
|
@@ -99,6 +121,7 @@ For each US, derive:
|
|
|
99
121
|
- **Phase title** — the UC title
|
|
100
122
|
- **Phase type** — `"user-story"`
|
|
101
123
|
- **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
|
|
124
|
+
- **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.
|
|
102
125
|
- **Acceptance Criteria** — the AC-XX IDs from the Requirements document that apply to this US
|
|
103
126
|
|
|
104
127
|
**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.
|
|
@@ -124,23 +147,22 @@ Decomposition strategy by domain:
|
|
|
124
147
|
|
|
125
148
|
`{DOMAIN}` in the task ID must match the `domain` field. `{NN}` is a two-digit zero-padded counter.
|
|
126
149
|
|
|
127
|
-
**Required task fields:**
|
|
150
|
+
**Required task fields (this pass — the skeleton):**
|
|
128
151
|
|
|
129
152
|
| Field | Type | Constraint |
|
|
130
153
|
|-------|------|-----------|
|
|
131
|
-
| `id` | string | Format: `INFRA-TASK-{DOMAIN}-{NN}` or `{US-ID}-TASK-{DOMAIN}-{NN}` |
|
|
132
154
|
| `title` | string | Short descriptive title; must not contain `\|`, CR, or LF |
|
|
133
155
|
| `outcome` | string | Single observable outcome — what exists after this task is done |
|
|
134
156
|
| `domain` | string | One of: `BE`, `FE`, `DB`, `DevOps`, `INFRA`, `TEST` |
|
|
135
157
|
| `agentType` | string | One of: `developer-backend`, `developer-frontend`, `developer-testing`, `review-solution` (deprecated: `developer-database` — maps to `developer-backend`) |
|
|
136
|
-
| `dependsOn` | array of strings | Task IDs this task depends on; may be empty `[]`; all IDs must be defined within this same
|
|
158
|
+
| `dependsOn` | array of strings | Task IDs this task depends on; may be empty `[]`; all IDs must be defined within this same response |
|
|
137
159
|
| `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) |
|
|
138
|
-
| `verification` | object | `{ "commands": ["..."] }` — one or more shell commands that verify the task output; array must be non-empty |
|
|
139
160
|
| `estimate` | object | `{ "agentMinutes": N, "tokens": N }` — target ≤ 15 minutes; positive integers |
|
|
140
161
|
| `outputCount` | integer | Number of distinct outputs; must be ≥ 1 |
|
|
141
|
-
| `groupingRationale` | string or null | Required (non-null) when `outputCount > 1`; explains why grouping is justified |
|
|
142
162
|
| `commit` | object | `{ "type": "feat", "subject": "..." }` — subject must not contain `\|`, CR, or LF |
|
|
143
163
|
|
|
164
|
+
`verification.commands` and `groupingRationale` 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).
|
|
165
|
+
|
|
144
166
|
**Domain → agentType mapping (default):**
|
|
145
167
|
- `DB` → `developer-backend` (note: `developer-database` is a deprecated alias that still resolves — do NOT use it in new Work Breakdowns)
|
|
146
168
|
- `BE`, `INFRA`, `DevOps` → `developer-backend`
|
|
@@ -156,6 +178,7 @@ Some tasks are prerequisites for multiple User Stories. Extract these into a ded
|
|
|
156
178
|
- Phase `type`: `"infrastructure"`
|
|
157
179
|
- Phase `title`: descriptive (e.g., `"Shared infrastructure setup"`)
|
|
158
180
|
- Phase `commit`: a phase-level commit (e.g., `{ "type": "feat", "subject": "shared infrastructure" }`)
|
|
181
|
+
- Phase `sourceUC`: `[]` — shared infrastructure has no single originating UC, by definition
|
|
159
182
|
- Task IDs: `INFRA-TASK-{DOMAIN}-{NN}`
|
|
160
183
|
|
|
161
184
|
Examples of infrastructure tasks:
|
|
@@ -170,7 +193,7 @@ The INFRA phase, if present, must appear first in the `phases` array.
|
|
|
170
193
|
### Step 5 — Resolve Dependencies
|
|
171
194
|
|
|
172
195
|
For each task, determine `dependsOn` as a list of **task IDs** (not phase IDs):
|
|
173
|
-
- All referenced task IDs must be defined within this same
|
|
196
|
+
- All referenced task IDs must be defined within this same response (no dangling references)
|
|
174
197
|
- A task may reference both INFRA tasks and tasks within its own phase or other US phases
|
|
175
198
|
- No self-references (a task cannot depend on itself)
|
|
176
199
|
- No cycles (A → B → A is invalid)
|
|
@@ -186,15 +209,15 @@ Duration guidance:
|
|
|
186
209
|
| Band | Range | Action |
|
|
187
210
|
|------|-------|--------|
|
|
188
211
|
| Target | ≤ 15 min | No action needed |
|
|
189
|
-
| Above target | 16–20 min | Acceptable
|
|
190
|
-
| Warning | 21–30 min | Prefer to split
|
|
212
|
+
| Above target | 16–20 min | Acceptable |
|
|
213
|
+
| Warning | 21–30 min | Prefer to split |
|
|
191
214
|
| Split required | > 30 min | **Must split** — the validator will reject this |
|
|
192
215
|
|
|
193
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)
|
|
194
217
|
|
|
195
|
-
### Step 7 — Return the
|
|
218
|
+
### Step 7 — Return the Skeleton
|
|
196
219
|
|
|
197
|
-
Return
|
|
220
|
+
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:
|
|
198
221
|
- Valid JSON
|
|
199
222
|
- UTF-8 encoded
|
|
200
223
|
- 2-space indentation
|
|
@@ -204,15 +227,17 @@ Return `{PREFIX}-Work-Breakdown.json`'s content as your final response, and noth
|
|
|
204
227
|
|
|
205
228
|
```json
|
|
206
229
|
{
|
|
207
|
-
"schemaVersion":
|
|
230
|
+
"schemaVersion": 3,
|
|
208
231
|
"feature": "{PREFIX}",
|
|
209
|
-
"phases": [ ... ]
|
|
232
|
+
"phases": [ ... ],
|
|
233
|
+
"tasks": { ... }
|
|
210
234
|
}
|
|
211
235
|
```
|
|
212
236
|
|
|
213
|
-
- `schemaVersion` must be exactly `
|
|
214
|
-
- `feature` must be the feature prefix string (e.g., `"FTR-
|
|
237
|
+
- `schemaVersion` must be exactly `3` (integer)
|
|
238
|
+
- `feature` must be the feature prefix string (e.g., `"FTR-020"`)
|
|
215
239
|
- `phases` is an ordered array of phase objects; INFRA phase first (if present), then US phases in priority order
|
|
240
|
+
- `tasks` is a **map keyed by task ID** (not nested inside each phase) — every phase's `taskIds` array indexes into this map
|
|
216
241
|
|
|
217
242
|
**Phase object:**
|
|
218
243
|
|
|
@@ -222,7 +247,9 @@ Return `{PREFIX}-Work-Breakdown.json`'s content as your final response, and noth
|
|
|
222
247
|
"type": "infrastructure",
|
|
223
248
|
"title": "Shared infrastructure setup",
|
|
224
249
|
"commit": { "type": "feat", "subject": "shared infrastructure" },
|
|
225
|
-
"
|
|
250
|
+
"sourceUC": [],
|
|
251
|
+
"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.",
|
|
252
|
+
"taskIds": ["INFRA-TASK-BE-01"]
|
|
226
253
|
}
|
|
227
254
|
```
|
|
228
255
|
|
|
@@ -234,18 +261,37 @@ For user-story phases:
|
|
|
234
261
|
"type": "user-story",
|
|
235
262
|
"title": "As a... I want... so that...",
|
|
236
263
|
"commit": { "type": "feat", "subject": "implement US-01 user management" },
|
|
237
|
-
"
|
|
264
|
+
"sourceUC": ["UC-01"],
|
|
265
|
+
"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.",
|
|
266
|
+
"taskIds": ["US-01-TASK-DB-01", "US-01-TASK-BE-01"]
|
|
238
267
|
}
|
|
239
268
|
```
|
|
240
269
|
|
|
241
270
|
- `phase.commit.subject` must not contain `|`, CR, or LF — `wb-render.js` constructs the full conventional message from it
|
|
271
|
+
- `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
|
|
242
272
|
- Each phase must have at least one task
|
|
243
273
|
|
|
244
|
-
**
|
|
274
|
+
**Task entry (in the `tasks` map, keyed by its own ID):**
|
|
245
275
|
|
|
246
276
|
```json
|
|
247
277
|
{
|
|
248
|
-
"
|
|
278
|
+
"title": "Create base repository interface",
|
|
279
|
+
"outcome": "IRepository<T> interface exists at src/interfaces/IRepository.ts with CRUD method signatures",
|
|
280
|
+
"domain": "BE",
|
|
281
|
+
"agentType": "developer-backend",
|
|
282
|
+
"dependsOn": [],
|
|
283
|
+
"acceptanceCriteria": [],
|
|
284
|
+
"estimate": { "agentMinutes": 8, "tokens": 15000 },
|
|
285
|
+
"outputCount": 1,
|
|
286
|
+
"commit": { "type": "feat", "subject": "add IRepository base interface" }
|
|
287
|
+
}
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
**Full example response:**
|
|
291
|
+
|
|
292
|
+
```json
|
|
293
|
+
{
|
|
294
|
+
"schemaVersion": 3,
|
|
249
295
|
"feature": "FTR-NNN",
|
|
250
296
|
"phases": [
|
|
251
297
|
{
|
|
@@ -253,88 +299,60 @@ For user-story phases:
|
|
|
253
299
|
"type": "infrastructure",
|
|
254
300
|
"title": "Shared infrastructure setup",
|
|
255
301
|
"commit": { "type": "feat", "subject": "shared infrastructure" },
|
|
256
|
-
"
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
"title": "Create base repository interface",
|
|
260
|
-
"outcome": "IRepository<T> interface exists at src/interfaces/IRepository.ts with CRUD method signatures",
|
|
261
|
-
"domain": "BE",
|
|
262
|
-
"agentType": "developer-backend",
|
|
263
|
-
"dependsOn": [],
|
|
264
|
-
"acceptanceCriteria": [],
|
|
265
|
-
"verification": { "commands": ["npx tsc --noEmit"] },
|
|
266
|
-
"estimate": { "agentMinutes": 8, "tokens": 15000 },
|
|
267
|
-
"outputCount": 1,
|
|
268
|
-
"groupingRationale": null,
|
|
269
|
-
"commit": { "type": "feat", "subject": "add IRepository base interface" }
|
|
270
|
-
}
|
|
271
|
-
]
|
|
302
|
+
"sourceUC": [],
|
|
303
|
+
"context": "Shared repository interface every other phase's data-access tasks depend on — opened first so downstream phases never block on it later.",
|
|
304
|
+
"taskIds": ["INFRA-TASK-BE-01"]
|
|
272
305
|
},
|
|
273
306
|
{
|
|
274
307
|
"id": "US-01",
|
|
275
308
|
"type": "user-story",
|
|
276
309
|
"title": "As an admin, I want to create a user, so that new members can access the system",
|
|
277
310
|
"commit": { "type": "feat", "subject": "implement US-01 user creation" },
|
|
278
|
-
"
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
311
|
+
"sourceUC": ["UC-01"],
|
|
312
|
+
"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.",
|
|
313
|
+
"taskIds": ["US-01-TASK-DB-01", "US-01-TASK-BE-01"]
|
|
314
|
+
}
|
|
315
|
+
],
|
|
316
|
+
"tasks": {
|
|
317
|
+
"INFRA-TASK-BE-01": {
|
|
318
|
+
"title": "Create base repository interface",
|
|
319
|
+
"outcome": "IRepository<T> interface exists at src/interfaces/IRepository.ts with CRUD method signatures",
|
|
320
|
+
"domain": "BE",
|
|
321
|
+
"agentType": "developer-backend",
|
|
322
|
+
"dependsOn": [],
|
|
323
|
+
"acceptanceCriteria": [],
|
|
324
|
+
"estimate": { "agentMinutes": 8, "tokens": 15000 },
|
|
325
|
+
"outputCount": 1,
|
|
326
|
+
"commit": { "type": "feat", "subject": "add IRepository base interface" }
|
|
327
|
+
},
|
|
328
|
+
"US-01-TASK-DB-01": {
|
|
329
|
+
"title": "Create User entity and migration",
|
|
330
|
+
"outcome": "User entity class and initial migration exist; database table is created on migration run",
|
|
331
|
+
"domain": "DB",
|
|
332
|
+
"agentType": "developer-backend",
|
|
333
|
+
"dependsOn": ["INFRA-TASK-BE-01"],
|
|
334
|
+
"acceptanceCriteria": ["AC-01", "AC-03"],
|
|
335
|
+
"estimate": { "agentMinutes": 12, "tokens": 28000 },
|
|
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
|
+
"estimate": { "agentMinutes": 14, "tokens": 35000 },
|
|
347
|
+
"outputCount": 1,
|
|
348
|
+
"commit": { "type": "feat", "subject": "add CreateUserService" }
|
|
308
349
|
}
|
|
309
|
-
|
|
350
|
+
}
|
|
310
351
|
}
|
|
311
352
|
```
|
|
312
353
|
|
|
313
354
|
---
|
|
314
355
|
|
|
315
|
-
## Verification Commands
|
|
316
|
-
|
|
317
|
-
For each task, supply one or more shell commands in `verification.commands` that an agent can run to confirm the task output is correct. The array must be non-empty. Use commands appropriate for the project's tech stack (read from `AGENTS.md`). Examples:
|
|
318
|
-
- Compilation check: `npx tsc --noEmit`, `dotnet build`
|
|
319
|
-
- Targeted test run: `npm test -- --testPathPattern=<file>`, `dotnet test --filter <class>`
|
|
320
|
-
- File existence: `test -f <path>`
|
|
321
|
-
- Lint: `npx eslint <file>`
|
|
322
|
-
|
|
323
|
-
**Directory-only tasks must never verify with `test -d` alone.** Git does not track empty
|
|
324
|
-
directories — `test -d <path>` only confirms the directory exists in the *current* working tree,
|
|
325
|
-
which is exactly what the agent that just created it will have. It tells you nothing about
|
|
326
|
-
whether anything was actually committed: a fresh clone, or any downstream agent/review step that
|
|
327
|
-
re-checks out the branch, will find the directory simply missing, even though this task's own
|
|
328
|
-
verification reported success. If a task's sole outcome is creating one or more directories (no
|
|
329
|
-
files):
|
|
330
|
-
- Give it a `.gitkeep` (or equivalent) placeholder file in every leaf directory it creates, so
|
|
331
|
-
there is something for git to actually track.
|
|
332
|
-
- Verify with a command that checks the directory is *tracked*, not just present on disk — e.g.
|
|
333
|
-
`git ls-files <path> | grep -q .` or `test -f <path>/.gitkeep` — instead of (or in addition to)
|
|
334
|
-
`test -d <path>`.
|
|
335
|
-
|
|
336
|
-
---
|
|
337
|
-
|
|
338
356
|
## Clarification Protocol
|
|
339
357
|
|
|
340
358
|
When decomposing User Stories into tasks, if the Requirements or Tech-Spec are ambiguous, **stop and ask the user** before proceeding. Use the `AskUserQuestion` tool to present:
|
|
@@ -354,11 +372,12 @@ Do NOT guess or invent task decompositions when the source is unclear. Ask first
|
|
|
354
372
|
- **Atomicity is mandatory** — each task must have one outcome, one domain, one agent type, one commit subject
|
|
355
373
|
- **Task granularity**: target ≤ 15 minutes of agent time; split any task that cannot be scoped to ≤ 15 minutes
|
|
356
374
|
- **Domain assignment must be unambiguous** — use the domain table in Step 3
|
|
357
|
-
- **dependsOn must reference only task IDs defined in this
|
|
375
|
+
- **dependsOn must reference only task IDs defined in this response** — no dangling references
|
|
358
376
|
- **Cross-phase dependencies are expected** — US tasks may depend on INFRA tasks; cross-US task dependencies are also valid
|
|
359
377
|
- **INFRA phase first** (if present), then US phases in priority order (Must before Should before Could)
|
|
360
378
|
- **Within a phase, order tasks by layer dependency** (DB → BE → FE → TEST); independent tasks within the same layer may be listed in any order
|
|
361
379
|
- **acceptanceCriteria** must list only AC IDs whose Requirements AC-table scope ("Related UC") matches this task's own phase, or that are marked cross-cutting ("All UCs"/multiple UCs including this phase's). **Never cite an AC owned by a different phase on this task, even when this task is prerequisite or contributing work for it** — an INFRA task that lays groundwork for an AC verified in a later US does not "cover" that AC; leave `acceptanceCriteria` empty `[]` on that task instead (e.g., pure infrastructure setup, or groundwork for an AC owned elsewhere). If an AC is genuinely cross-cutting, reflect that in the AC's own "Related UC" column, not by citing it from an unrelated task.
|
|
362
|
-
-
|
|
380
|
+
- **`sourceUC` must reflect what a phase's tasks actually implement, not its number** — see Step 2.
|
|
381
|
+
- **schemaVersion must be exactly 3** — do not omit it, do not use any other value
|
|
363
382
|
- **No pipes, CR, or LF** in `phase.title`, `task.title`, or `commit.subject` — these characters break CSV generation
|
|
364
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
|
|
@@ -7,18 +7,23 @@ tools: Read, Glob, Grep
|
|
|
7
7
|
|
|
8
8
|
# Validate Feature Docs
|
|
9
9
|
|
|
10
|
-
A QA agent that cross-references `feature.md` against
|
|
10
|
+
A QA agent that cross-references `feature.md` against the Requirements and Tech-Spec documents and identifies coverage gaps in a single validation pass. The host workflow owns revisions and the three-cycle limit. Do not dispatch other agents or revise the input documents.
|
|
11
11
|
|
|
12
12
|
---
|
|
13
13
|
|
|
14
14
|
## Phase 1 — Load Documents
|
|
15
15
|
|
|
16
16
|
1. Extract the prefix from the folder name (`FTR-001-user-management` → `FTR-001`)
|
|
17
|
-
2. Read
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
2. Read `feature.md` fully — its path is given to you first; use your Read tool.
|
|
18
|
+
3. The Requirements and Tech-Spec document text is included directly below, inlined into this
|
|
19
|
+
prompt under `Requirements:` and `Tech-Spec:` labels — do NOT attempt to locate or Read
|
|
20
|
+
`{PREFIX}-Requirements.md`/`{PREFIX}-Tech-Spec.md` on disk; they are not guaranteed to exist
|
|
21
|
+
(both documents are generated and stored in a split, per-item format by default — `req/`,
|
|
22
|
+
`spec/` — and only assembled into a single file when a user explicitly requests it). Use the
|
|
23
|
+
inlined text as the complete, authoritative content of each document for this validation pass.
|
|
24
|
+
4. If either inlined document's text is empty or missing, record a finding identifying it, skip
|
|
25
|
+
checks that require it, and complete Phases 6 and 7 with `valid: false`. Never claim full
|
|
26
|
+
coverage when an input could not be read.
|
|
22
27
|
|
|
23
28
|
---
|
|
24
29
|
|
|
@@ -44,7 +49,7 @@ Label each claim with its origin section in `feature.md`.
|
|
|
44
49
|
|
|
45
50
|
For each claim extracted in Phase 2, check whether it is addressed in the target document:
|
|
46
51
|
|
|
47
|
-
### Requirements coverage (against
|
|
52
|
+
### Requirements coverage (against the inlined Requirements text)
|
|
48
53
|
|
|
49
54
|
| Check | Pass condition |
|
|
50
55
|
|-------|---------------|
|
|
@@ -55,7 +60,7 @@ For each claim extracted in Phase 2, check whether it is addressed in the target
|
|
|
55
60
|
| Every out-of-scope item → Out of Scope section | Listed |
|
|
56
61
|
| Every functional behaviour → at least one Acceptance Criterion | AC in Given/When/Then format |
|
|
57
62
|
|
|
58
|
-
### Tech-Spec coverage (against
|
|
63
|
+
### Tech-Spec coverage (against the inlined Tech-Spec text)
|
|
59
64
|
|
|
60
65
|
| Check | Pass condition |
|
|
61
66
|
|-------|---------------|
|
|
@@ -15,12 +15,17 @@ You do NOT repeat structural checks (schema conformance, required fields, ID for
|
|
|
15
15
|
|
|
16
16
|
## Input
|
|
17
17
|
|
|
18
|
-
The
|
|
18
|
+
The prompt gives you two paths, one per line, in this order:
|
|
19
19
|
|
|
20
|
-
1.
|
|
21
|
-
|
|
20
|
+
1. The Work Breakdown directory (the split format, FTR-020) — contains `index.json` (the
|
|
21
|
+
validated skeleton, schema v2) plus `user-stories/*.md`/`tasks/*.md` detail files. Use Glob/Read
|
|
22
|
+
to explore it as needed; do not expect a single `{PREFIX}-Work-Breakdown.json` file.
|
|
23
|
+
2. The Requirements source — `req/index.md` for a feature using the split Requirements format
|
|
24
|
+
(FTR-021; User Story descriptions and Acceptance Criteria text live in its own tables there,
|
|
25
|
+
unaffected by the per-UC split), or a legacy `{PREFIX}-Requirements.md` file otherwise. Read
|
|
26
|
+
whichever path is actually given — do not assume either shape.
|
|
22
27
|
|
|
23
|
-
Derive the prefix from the
|
|
28
|
+
Derive the prefix from the Work Breakdown directory's own `index.json`'s `feature` field.
|
|
24
29
|
|
|
25
30
|
---
|
|
26
31
|
|