@dtlabs/ai-toolkit 0.11.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/bin/cli.js +757 -68
  3. package/lib/agent-registry.js +1018 -0
  4. package/lib/execution-ledger.js +609 -0
  5. package/package.json +1 -1
  6. package/src/claude/agents/{concurrency-safety-assessment.md → gaia-concurrency-safety-assessment.md} +1 -1
  7. package/src/claude/agents/{define-feature.md → gaia-define-feature.md} +37 -61
  8. package/src/claude/agents/{dependency-injection-refactoring.md → gaia-dependency-injection-refactoring.md} +1 -1
  9. package/src/claude/agents/{dependency-supply-chain-security.md → gaia-dependency-supply-chain-security.md} +1 -1
  10. package/src/claude/agents/{developer-backend.md → gaia-developer-backend.md} +1 -1
  11. package/src/claude/agents/{developer-frontend.md → gaia-developer-frontend.md} +1 -1
  12. package/src/claude/agents/{developer-testing.md → gaia-developer-testing.md} +1 -1
  13. package/src/claude/agents/{domain-model-refactoring.md → gaia-domain-model-refactoring.md} +1 -1
  14. package/src/claude/agents/{generate-requirements.md → gaia-generate-requirements.md} +1 -1
  15. package/src/claude/agents/{generate-tech-spec.md → gaia-generate-tech-spec.md} +1 -1
  16. package/src/claude/agents/{generate-work-breakdown.md → gaia-generate-work-breakdown.md} +5 -5
  17. package/src/claude/agents/{generic-software-assessment.md → gaia-generic-software-assessment.md} +1 -1
  18. package/src/claude/agents/{god-class-decomposition.md → gaia-god-class-decomposition.md} +1 -1
  19. package/src/claude/agents/{init-agents-md.md → gaia-init-agents-md.md} +1 -1
  20. package/src/claude/agents/{install-toolkit.md → gaia-install-toolkit.md} +1 -1
  21. package/src/claude/agents/{intervention-documentation-standard.md → gaia-intervention-documentation-standard.md} +1 -1
  22. package/src/claude/agents/{layered-architecture-assessment.md → gaia-layered-architecture-assessment.md} +1 -1
  23. package/src/claude/agents/{review-solution.md → gaia-review-solution.md} +1 -1
  24. package/src/claude/agents/{security-hardening.md → gaia-security-hardening.md} +1 -1
  25. package/src/claude/agents/{validate-feature-docs.md → gaia-validate-feature-docs.md} +2 -2
  26. package/src/claude/agents/{validate-work-breakdown-semantic.md → gaia-validate-work-breakdown-semantic.md} +1 -1
  27. package/src/claude/commands/next-task.md +4 -4
  28. package/src/claude/scripts/wb-render.js +101 -2
  29. package/src/claude/scripts/wb-validate.js +32 -2
  30. package/src/claude/skills/assess-codebase/SKILL.md +1 -1
  31. package/src/claude/skills/define-feature/SKILL.md +2 -2
  32. package/src/claude/skills/hi-gaia/SKILL.md +1 -1
  33. package/src/claude/skills/implement-feature/SKILL.md +190 -10
  34. package/src/claude/skills/init-agents/SKILL.md +2 -2
  35. package/src/claude/skills/install-toolkit/SKILL.md +2 -2
  36. package/src/claude/workflows/pm-phase1.js +94 -73
  37. package/src/claude/workflows/pm-phase2.js +118 -108
  38. package/src/claude/workflows/pm-phase3.js +219 -75
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: define-feature
2
+ name: gaia-define-feature
3
3
  description: "Define Feature — grills the user with targeted questions to define a new feature, then writes feature.md in docs/features/FTR-XXX-slug/. Output: feature.md ready for /implement-feature"
4
4
  model: sonnet
5
5
  ---
@@ -80,56 +80,53 @@ Ask the user to **confirm or correct the draft**. Then grill ONLY on the `❓` g
80
80
 
81
81
  ## Phase 1 — Setup
82
82
 
83
- ### 1a. Discover the next FTR number
83
+ ### 1a. Resolve the features root (MANDATORY — single source of truth)
84
84
 
85
- Scan `docs/features/` for existing folders matching the pattern `FTR-[0-9]+*`. Extract the highest number and increment by 1. If no folders exist, start at `FTR-001`.
85
+ Before any file-system operation, run the following command **once** via Bash and capture its stdout into `featuresRoot`:
86
86
 
87
+ ```bash
88
+ featuresRoot=$(ai-toolkit resolve-features-root) && echo "$featuresRoot"
87
89
  ```
88
- docs/features/FTR-001-user-management/ → max = 1
89
- docs/features/FTR-002-product-catalog/ → max = 2
90
+
91
+ If the command exits non-zero, stop immediately and report the error to the user — do not proceed.
92
+
93
+ This single captured value (`featuresRoot`) is the **sole source of truth** for the features root in every subsequent step. Do **not** hard-code `docs/features/`, `internal_docs/features/`, or any other path. Both the directory under which `feature.md` is written (step 1d) and the `--dir` argument passed to the ledger facade must use this exact value — they must always agree, with no divergent or duplicate roots.
94
+
95
+ ### 1b. Discover the next FTR number
96
+
97
+ Scan `{featuresRoot}` for existing folders matching the pattern `FTR-[0-9]+*`. Extract the highest number and increment by 1. If no folders exist, start at `FTR-001`.
98
+
99
+ ```
100
+ {featuresRoot}/FTR-001-user-management/ → max = 1
101
+ {featuresRoot}/FTR-002-product-catalog/ → max = 2
90
102
  → next = FTR-003
91
103
  ```
92
104
 
93
- ### 1b. Ask for a feature name
105
+ ### 1c. Ask for a feature name
94
106
 
95
107
  Ask the user: **"What is the name of this feature?"** (short, descriptive, in their language). Use it to build the folder slug in kebab-case.
96
108
 
97
109
  Example: "Supplier Onboarding" → `FTR-003-supplier-onboarding`
98
110
 
99
- ### 1c. Create feature directory and initialize ledger
111
+ ### 1d. Create feature directory and open ledger entry
100
112
 
101
- Once you know the PREFIX (from 1a) and the feature slug (from 1b), do the following **before asking any grilling questions**:
113
+ Once you know the PREFIX (from 1b) and the feature slug (from 1c), do the following **before asking any grilling questions**:
102
114
 
103
- 1. Compute the paths:
104
- - `featureDir` = `docs/features/{PREFIX}-{slug}` (e.g. `docs/features/FTR-003-supplier-onboarding`)
105
- - `ledgerPath` = `{featureDir}/{PREFIX}-token-ledger.json`
115
+ 1. Compute `featureDir` using the captured `featuresRoot`:
116
+ - `featureDir` = `{featuresRoot}/{PREFIX}-{slug}` (e.g. `{featuresRoot}/FTR-003-supplier-onboarding`)
117
+ - This path is also the `--dir` value for every `ai-toolkit ledger` call that follows.
106
118
 
107
119
  2. Create the feature directory via Bash:
108
120
  ```bash
109
- mkdir -p {featureDir}
121
+ mkdir -p "{featureDir}"
110
122
  ```
111
123
 
112
- 3. Capture the current UTC timestamp via Bash:
124
+ 3. Open the ledger entry via the facade. Run the following command via Bash (quote the `--dir` path to handle spaces):
113
125
  ```bash
114
- started_at=$(date -u +"%Y-%m-%dT%H:%M:%SZ") && echo $started_at
126
+ ai-toolkit ledger open --dir "{featureDir}" --prefix {PREFIX} --agent define-feature:define --phase define --model sonnet --attempt 1
115
127
  ```
116
128
 
117
- 4. Write the initial ledger file at `{ledgerPath}` using the Write tool with exactly this content (substitute the actual PREFIX and timestamp):
118
- ```json
119
- [
120
- {
121
- "agent": "define-feature:define",
122
- "phase": "define",
123
- "model": "sonnet",
124
- "status": "running",
125
- "phase_delta_tokens": 0,
126
- "started_at": "<started_at from step 3>",
127
- "completed_at": null
128
- }
129
- ]
130
- ```
131
-
132
- The ledger records that the define-feature agent is now running. Proceed to Phase 2.
129
+ The ledger now records that the define-feature agent is running. Proceed to Phase 2.
133
130
 
134
131
  ---
135
132
 
@@ -254,12 +251,12 @@ Synthesize all answers into a structured `feature.md` file.
254
251
  ### Output path
255
252
 
256
253
  ```
257
- docs/features/{PREFIX}-{slug}/feature.md
254
+ {featuresRoot}/{PREFIX}-{slug}/feature.md
258
255
  ```
259
256
 
260
- Example: `docs/features/FTR-003-supplier-onboarding/feature.md`
257
+ Example: `{featuresRoot}/FTR-003-supplier-onboarding/feature.md`
261
258
 
262
- The directory was created in Phase 1c. If for any reason it doesn't exist, create it now.
259
+ The directory was created in Phase 1d using `featuresRoot`. If for any reason it doesn't exist, create it now using the same `featuresRoot` path — do not substitute a different root.
263
260
 
264
261
  ### feature.md template
265
262
 
@@ -352,34 +349,13 @@ For each entity: name, key fields (name, type, constraints), relationships.
352
349
 
353
350
  ### 4b. Finalize ledger entry
354
351
 
355
- After writing `feature.md`, update the ledger entry to record successful completion:
352
+ After writing `feature.md`, close the ledger entry via the facade. Run the following command via Bash (quote the `--dir` path to handle spaces):
356
353
 
357
- 1. Capture the current UTC timestamp via Bash:
358
- ```bash
359
- completed_at=$(date -u +"%Y-%m-%dT%H:%M:%SZ") && echo $completed_at
360
- ```
354
+ ```bash
355
+ ai-toolkit ledger close --dir "{featureDir}" --prefix {PREFIX} --agent define-feature:define --attempt 1
356
+ ```
361
357
 
362
- 2. Read the ledger file at `{featureDir}/{PREFIX}-token-ledger.json` using the Read tool.
363
-
364
- 3. Write the updated ledger file using the Write tool, replacing the entry's fields:
365
- - `"status"`: `"done"`
366
- - `"completed_at"`: the timestamp captured in step 1
367
- - `"phase_delta_tokens"`: leave as `0` — token measurement is not available to the agent directly
368
-
369
- Example final ledger content:
370
- ```json
371
- [
372
- {
373
- "agent": "define-feature:define",
374
- "phase": "define",
375
- "model": "sonnet",
376
- "status": "done",
377
- "phase_delta_tokens": 0,
378
- "started_at": "<original started_at>",
379
- "completed_at": "<completed_at from step 1>"
380
- }
381
- ]
382
- ```
358
+ **Important:** `--tokens` is intentionally omitted. The ledger facade records `phase_delta_tokens` as `null` when `--tokens` is not supplied. `define-feature` is an interactive/LLM activity whose token consumption cannot be observed from within the agent itself — do **not** pass `0` or any estimate.
383
359
 
384
360
  ---
385
361
 
@@ -392,7 +368,7 @@ After writing `feature.md`:
392
368
  3. Tell the user the next step:
393
369
 
394
370
  ```
395
- ✅ feature.md written at docs/features/{PREFIX}-{slug}/feature.md
371
+ ✅ feature.md written at {featuresRoot}/{PREFIX}-{slug}/feature.md
396
372
 
397
373
  Summary:
398
374
  Actors: N
@@ -400,7 +376,7 @@ Summary:
400
376
  Acceptance criteria: N (Must: N, Should: N, Could: N)
401
377
  Open questions: N
402
378
 
403
- Next step: run /implement-feature docs/features/{PREFIX}-{slug}/feature.md
379
+ Next step: run /implement-feature {featuresRoot}/{PREFIX}-{slug}/feature.md
404
380
  to start the full pipeline (requirements → spec → implementation → PR).
405
381
  ```
406
382
 
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: dependency-injection-refactoring
2
+ name: gaia-dependency-injection-refactoring
3
3
  description: "Dependency Injection Refactoring — guides the incremental conversion of tightly coupled or statically accessed dependencies to constructor-injected services, preserving build integrity at every step. Language-agnostic; reads AGENTS.md for framework-specific DI patterns. Input: target classes from intervention document"
4
4
  model: sonnet
5
5
  ---
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: dependency-supply-chain-security
2
+ name: gaia-dependency-supply-chain-security
3
3
  description: "Dependency Supply Chain Security — guides the implementation of dependency integrity controls: trusted package sources, deterministic restore via lock files, unused dependency audit, and SCA integration in CI/CD pipelines. Works with npm, pip, NuGet, Maven, and other package managers; reads AGENTS.md for project stack. Output: remediated configuration files + CI/CD pipeline changes"
4
4
  model: sonnet
5
5
  ---
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: developer-backend
2
+ name: gaia-developer-backend
3
3
  description: "Senior Backend Developer — implements backend tasks (DB, BE, INFRA domains) from the Work Breakdown following project conventions. Input: path to feature.md + task IDs"
4
4
  model: sonnet
5
5
  ---
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: developer-frontend
2
+ name: gaia-developer-frontend
3
3
  description: "Senior Frontend Developer — implements frontend tasks (FE domain) from the Work Breakdown following project conventions. Input: path to feature.md + task IDs"
4
4
  model: sonnet
5
5
  ---
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: developer-testing
2
+ name: gaia-developer-testing
3
3
  description: "Senior Test Engineer — creates unit/integration tests for implemented features (TEST domain). Input: path to feature.md + task IDs"
4
4
  model: sonnet
5
5
  ---
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: domain-model-refactoring
2
+ name: gaia-domain-model-refactoring
3
3
  description: "Domain Model Refactoring — guides the decomposition of monolithic domain model files, introduction of type hierarchies for structurally similar types, and alignment of model names with domain ubiquitous language. Language-agnostic; reads AGENTS.md for project conventions. Input: target model directory from intervention document"
4
4
  model: sonnet
5
5
  ---
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: generate-requirements
2
+ name: gaia-generate-requirements
3
3
  description: "Generates a functional requirements document from a feature description. Input: path to feature.md"
4
4
  model: haiku
5
5
  tools: Read, Glob, Grep, Write
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: generate-tech-spec
2
+ name: gaia-generate-tech-spec
3
3
  description: "Generates a technical specification document from a feature description and requirements. Input: path to feature.md"
4
4
  model: haiku
5
5
  tools: Read, Glob, Grep, Write
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: generate-work-breakdown
2
+ name: gaia-generate-work-breakdown
3
3
  description: "Generates a structured work breakdown (User Stories + Tasks) from validated and approved Requirements and Tech-Spec. Input: path to feature.md"
4
4
  model: haiku
5
5
  tools: Read, Glob, Grep, Write
@@ -24,7 +24,7 @@ The user provides the path to a `feature.md` file. From the same directory, read
24
24
  ```
25
25
  Cannot generate work breakdown: `{file}` not found.
26
26
  The work breakdown requires validated and approved Requirements + Tech-Spec.
27
- Run /agent-project-manager to orchestrate the full pipeline (including validation and approval gates).
27
+ Run /implement-feature to orchestrate the full pipeline (including validation and approval gates).
28
28
  ```
29
29
 
30
30
  ---
@@ -63,7 +63,7 @@ Every task must be the **smallest independently implementable, verifiable, and c
63
63
 
64
64
  - **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.
65
65
  - **One domain** — choose exactly one: `BE`, `FE`, `DB`, `DevOps`, `INFRA`, or `TEST`.
66
- - **One agent type** — choose exactly one: `developer-backend`, `developer-frontend`, `developer-testing`, `developer-database`, or `review-solution`.
66
+ - **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.)
67
67
  - **One commit** — supply a single `commit.subject` describing the change.
68
68
  - **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.
69
69
 
@@ -126,7 +126,7 @@ Decomposition strategy by domain:
126
126
  | `title` | string | Short descriptive title; must not contain `\|`, CR, or LF |
127
127
  | `outcome` | string | Single observable outcome — what exists after this task is done |
128
128
  | `domain` | string | One of: `BE`, `FE`, `DB`, `DevOps`, `INFRA`, `TEST` |
129
- | `agentType` | string | One of: `developer-backend`, `developer-frontend`, `developer-testing`, `developer-database`, `review-solution` |
129
+ | `agentType` | string | One of: `developer-backend`, `developer-frontend`, `developer-testing`, `review-solution` (deprecated: `developer-database` — maps to `developer-backend`) |
130
130
  | `dependsOn` | array of strings | Task IDs this task depends on; may be empty `[]`; all IDs must be defined within this same JSON |
131
131
  | `acceptanceCriteria` | array of strings | AC IDs from Requirements that this task covers; may be empty `[]` |
132
132
  | `verification` | object | `{ "commands": ["..."] }` — one or more shell commands that verify the task output; array must be non-empty |
@@ -136,7 +136,7 @@ Decomposition strategy by domain:
136
136
  | `commit` | object | `{ "type": "feat", "subject": "..." }` — subject must not contain `\|`, CR, or LF |
137
137
 
138
138
  **Domain → agentType mapping (default):**
139
- - `DB` → `developer-database`
139
+ - `DB` → `developer-backend` (note: `developer-database` is a deprecated alias that still resolves — do NOT use it in new Work Breakdowns)
140
140
  - `BE`, `INFRA`, `DevOps` → `developer-backend`
141
141
  - `FE` → `developer-frontend`
142
142
  - `TEST` → `developer-testing`
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: generic-software-assessment
2
+ name: gaia-generic-software-assessment
3
3
  description: "Generic Software Assessment — performs a broad quality analysis across architecture, code quality, security, testability, observability, and DevOps. Reads AGENTS.md for project conventions. Output: {ASSESS_PREFIX}-Generic-Assessment.md"
4
4
  tools: Read, Grep, Glob, Bash, Write
5
5
  model: haiku
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: god-class-decomposition
2
+ name: gaia-god-class-decomposition
3
3
  description: "God Class Decomposition — guides the safe, incremental decomposition of oversized classes and methods using responsibility cluster analysis, extract-method, extract-class, and parameter object refactoring. Language-agnostic; reads AGENTS.md for project conventions. Input: target class(es) from intervention document"
4
4
  model: sonnet
5
5
  ---
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: init-agents-md
2
+ name: gaia-init-agents-md
3
3
  description: "Analyzes a project codebase and generates a complete AGENTS.md file with tech stack, conventions, real code patterns, build/test commands, and hard constraints. Input: (optional) path to project root — defaults to current working directory."
4
4
  model: sonnet
5
5
  tools: Read, Glob, Grep, Bash, Write
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: install-toolkit
2
+ name: gaia-install-toolkit
3
3
  description: "Installs the ai-toolkit into a destination project by copying agents, skills, commands, and procedures. Input: [path-to-destination] [--force] — defaults to current working directory"
4
4
  model: sonnet
5
5
  ---
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: intervention-documentation-standard
2
+ name: gaia-intervention-documentation-standard
3
3
  description: "Intervention Documentation Standard — generates structured, self-contained intervention documents from assessment findings. Each document is independently actionable by an architect or developer agent. Input: assessment findings; Output: {ASSESS_PREFIX}-INT-NNN-{slug}.md per finding + Interventions-Index.md"
4
4
  model: sonnet
5
5
  tools: Read, Grep, Glob, Bash, Write
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: layered-architecture-assessment
2
+ name: gaia-layered-architecture-assessment
3
3
  description: "Layered Architecture Assessment — audits a codebase for layer boundary violations, missing separation of concerns, and namespace/package misalignment. Produces a violation inventory and a recommended layer model. Language-agnostic; reads AGENTS.md for project conventions. Output: {ASSESS_PREFIX}-Layer-Assessment.md"
4
4
  tools: Read, Grep, Glob, Bash, Write
5
5
  model: haiku
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: review-solution
2
+ name: gaia-review-solution
3
3
  description: "Senior Architect — full solution review across quality, reuse, architecture, and security dimensions. Reports findings without auto-fixing. Input: scope (files, US-XX, or git diff)"
4
4
  model: opus
5
5
  tools: Read, Grep, Glob, Bash
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: security-hardening
2
+ name: gaia-security-hardening
3
3
  description: "Security Hardening — guides the implementation of targeted security fixes: input validation at system boundaries, parameterised queries, secret management, log sanitisation, and TLS/transport configuration. Language-agnostic; reads AGENTS.md for project-specific patterns. Input: security findings from assessment"
4
4
  model: sonnet
5
5
  ---
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: validate-feature-docs
2
+ name: gaia-validate-feature-docs
3
3
  description: "Validates Requirements and Tech-Spec documents against feature.md. Triggers targeted revision of failing documents if gaps are found. Input: path to feature.md"
4
4
  model: haiku
5
5
  tools: Read, Glob, Grep, Write
@@ -18,7 +18,7 @@ A QA agent that cross-references `feature.md` against `{PREFIX}-Requirements.md`
18
18
  - `feature.md` — source of truth
19
19
  - `{PREFIX}-Requirements.md`
20
20
  - `{PREFIX}-Tech-Spec.md`
21
- 3. If either output document is missing, abort and report: "Cannot validate: `{file}` not found. Run /agent-project-manager first."
21
+ 3. If either output document is missing, abort and report: "Cannot validate: `{file}` not found. Run /implement-feature first."
22
22
 
23
23
  ---
24
24
 
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: validate-work-breakdown-semantic
2
+ name: gaia-validate-work-breakdown-semantic
3
3
  description: "Semantic validator for Work Breakdown JSON — analyzes task coherence, scope alignment, and estimate realism. Runs after wb-validate.js exits 0. Input: path to {PREFIX}-Work-Breakdown.json"
4
4
  model: sonnet
5
5
  tools: Read, Glob, Grep
@@ -23,14 +23,14 @@ Steps:
23
23
 
24
24
  Suggested agent: <subagent_type>
25
25
  Suggested invocation:
26
- subagent_type: <developer-backend | developer-frontend | developer-testing>
26
+ subagent_type: <gaia-developer-backend | gaia-developer-frontend | gaia-developer-testing>
27
27
  prompt: <feature-md-path> <task-id>
28
28
  ```
29
29
 
30
30
  Domain → agent mapping:
31
- - `DB`, `BE`, `INFRA` → `developer-backend`
32
- - `FE` → `developer-frontend`
33
- - `TEST` → `developer-testing`
31
+ - `DB`, `BE`, `INFRA` → `gaia-developer-backend`
32
+ - `FE` → `gaia-developer-frontend`
33
+ - `TEST` → `gaia-developer-testing`
34
34
 
35
35
  5. If no unblocked task remains, output: `✅ All tasks are either done, in progress, or blocked. Check blockers.`
36
36
 
@@ -105,6 +105,38 @@ function sanitizeField(str) {
105
105
  .trim()
106
106
  }
107
107
 
108
+ // ── Verification-command helpers (LOSSLESS — never sanitized) ─────────────────
109
+ //
110
+ // Verification commands are executable and MUST survive verbatim: operators like
111
+ // `||`, shell pipes `|`, and regex alternations `grep -E 'a|b|c'` are destroyed by
112
+ // sanitizeField (which maps `|` → space to protect table/CSV column structure).
113
+ // These commands are therefore rendered in a dedicated section as fenced code
114
+ // blocks whose content is byte-equivalent to the source JSON. The task table shows
115
+ // only a reference (a count + anchor link), never a sanitized copy of a command.
116
+
117
+ function getVerificationCommands(task) {
118
+ const v = task && (task.verificationCommands ?? task.verification?.commands)
119
+ return Array.isArray(v) ? v : []
120
+ }
121
+
122
+ // Stable, deterministic HTML anchor id for a task's authoritative detail block.
123
+ // The summary table links here; the "Task Details" section renders every field.
124
+ function taskAnchor(id) {
125
+ const s = typeof id === 'string' ? id : String(id ?? '')
126
+ return 'task-' + s.replace(/[^A-Za-z0-9_-]/g, '-')
127
+ }
128
+
129
+ // Render a single command inside a fenced code block, byte-equivalent to `cmd`.
130
+ // The fence length is chosen to exceed the longest backtick run in the content so a
131
+ // command that itself contains backticks can never prematurely close the fence.
132
+ function fencedCommandBlock(cmd) {
133
+ const s = typeof cmd === 'string' ? cmd : String(cmd ?? '')
134
+ const runs = s.match(/`+/g) || []
135
+ const maxRun = runs.reduce((m, r) => Math.max(m, r.length), 0)
136
+ const fence = '`'.repeat(Math.max(3, maxRun + 1))
137
+ return `${fence}\n${s}\n${fence}`
138
+ }
139
+
108
140
  function buildCommitSubject(task) {
109
141
  if (!task.commit || typeof task.commit !== 'object') {
110
142
  return 'chore: implement task'
@@ -189,8 +221,15 @@ function renderMarkdown(wb, prefix, phaseDepsMap) {
189
221
  const est = mins !== null ? String(mins) : '—'
190
222
  const depArr = Array.isArray(task.dependsOn) && task.dependsOn.length > 0 ? task.dependsOn : null
191
223
  const deps = depArr ? depArr.join(', ') : '—'
192
- const verifArr = task.verificationCommands ?? task.verification?.commands ?? []
193
- const verif = verifArr.length > 0 ? sanitizeField(verifArr.join('; ')) : '—'
224
+ // The summary table is a digest only. Verification commands are NOT rendered
225
+ // in-table (sanitizeField would destroy their operators). The cell links to the
226
+ // authoritative per-task "Task Details" section, which carries every field
227
+ // (outcome, acceptance criteria, estimates, grouping rationale, commit, and the
228
+ // lossless verification commands). See renderTaskDetails / fencedCommandBlock.
229
+ const verifCmds = getVerificationCommands(task)
230
+ const verif = verifCmds.length > 0
231
+ ? `${verifCmds.length} cmd — [details](#${taskAnchor(task.id)})`
232
+ : `[details](#${taskAnchor(task.id)})`
194
233
  return `| ${id} | ${title} | ${outcome} | ${domain} | ${est} | ${deps} | ${verif} |`
195
234
  }
196
235
 
@@ -263,6 +302,66 @@ function renderMarkdown(wb, prefix, phaseDepsMap) {
263
302
  }
264
303
  }
265
304
 
305
+ // ── Task Details (AUTHORITATIVE) ──
306
+ // The summary tables above are a digest. This section is the authoritative,
307
+ // self-contained representation of every task: it renders each field integrally so
308
+ // the Work Breakdown Markdown (plus the dispatch CSV) is a complete deliverable —
309
+ // the intermediate JSON is not required by any downstream consumer.
310
+ //
311
+ // Free-text fields (title, outcome, grouping rationale, commit subject) are rendered
312
+ // OUTSIDE any table and are therefore preserved verbatim — including pipes, which
313
+ // sanitizeField would otherwise map to spaces to protect table/CSV columns.
314
+ // Verification commands are emitted as fenced code blocks that are byte-equivalent
315
+ // to the source; the fence length exceeds any backtick run in the command so a
316
+ // command containing backticks can never break out of its block.
317
+ const raw = v => (typeof v === 'string' ? v : String(v ?? ''))
318
+ const getTokens = task => {
319
+ const t = task.estimate?.tokens ?? task.tokens
320
+ return (t !== undefined && t !== null) ? String(t) : '—'
321
+ }
322
+ const listOrDash = arr =>
323
+ (Array.isArray(arr) && arr.length > 0) ? arr.join(', ') : '—'
324
+
325
+ L.push('## Task Details')
326
+ L.push('')
327
+ L.push('> Authoritative per-task detail. Every field is rendered integrally so this document, together with the dispatch CSV, is a complete deliverable requiring no separate JSON. Verification commands are preserved **verbatim** in fenced code blocks — operators such as `||`, shell pipes `|`, and regex alternations (`grep -E \'a|b|c\'`) survive byte-for-byte. Each command is an independent fenced block.')
328
+ L.push('')
329
+ for (const phase of wb.phases) {
330
+ for (const task of (Array.isArray(phase.tasks) ? phase.tasks : [])) {
331
+ const cmds = getVerificationCommands(task)
332
+ const mins = getAgentMinutes(task)
333
+ L.push(`<a id="${taskAnchor(task.id)}"></a>`)
334
+ L.push(`### ${raw(task.id)}`)
335
+ L.push('')
336
+ L.push(`- **Task ID:** ${raw(task.id)}`)
337
+ L.push(`- **Title:** ${raw(task.title)}`)
338
+ L.push(`- **Outcome:** ${raw(task.outcome)}`)
339
+ L.push(`- **Domain:** ${raw(task.domain)}`)
340
+ L.push(`- **Agent type:** ${raw(task.agentType)}`)
341
+ L.push(`- **Dependencies:** ${listOrDash(task.dependsOn)}`)
342
+ L.push(`- **Acceptance criteria:** ${listOrDash(task.acceptanceCriteria)}`)
343
+ L.push(`- **Estimate — agent minutes:** ${mins !== null ? String(mins) : '—'}`)
344
+ L.push(`- **Estimate — tokens:** ${getTokens(task)}`)
345
+ L.push(`- **Output count:** ${task.outputCount != null ? String(task.outputCount) : '—'}`)
346
+ L.push(`- **Grouping rationale:** ${task.groupingRationale != null ? raw(task.groupingRationale) : '—'}`)
347
+ L.push(`- **Commit type:** ${task.commit && task.commit.type != null ? raw(task.commit.type) : '—'}`)
348
+ L.push(`- **Commit scope:** ${task.commit && task.commit.scope != null ? raw(task.commit.scope) : '—'}`)
349
+ L.push(`- **Commit subject:** ${task.commit && task.commit.subject != null ? raw(task.commit.subject) : '—'}`)
350
+ L.push('')
351
+ L.push('**Verification commands:**')
352
+ L.push('')
353
+ if (cmds.length === 0) {
354
+ L.push('_No verification commands._')
355
+ L.push('')
356
+ } else {
357
+ for (const cmd of cmds) {
358
+ L.push(fencedCommandBlock(cmd))
359
+ L.push('')
360
+ }
361
+ }
362
+ }
363
+ }
364
+
266
365
  // ── Statistics ──
267
366
  L.push('## Statistics')
268
367
  L.push('')
@@ -59,6 +59,36 @@ const ERRORS = {
59
59
  EMPTY_PHASE: 'empty_phase', // check 23
60
60
  }
61
61
 
62
+ // ── Markdown table row splitter ─────────────────────────────────────────────
63
+ // Splits a Markdown table row into cells on unescaped pipe separators only.
64
+ // A backslash-escaped pipe (\|) is treated as a literal pipe *inside* the cell
65
+ // rather than a column separator and is unescaped in the returned cell, so a
66
+ // criterion such as `ai-toolkit ledger open\|close\|fail\|skip` stays in a
67
+ // single cell and does not shift the "Related UC" column.
68
+ // Returns the raw split including the leading/trailing empty cells produced by
69
+ // the row's outer pipes, matching the previous `line.split('|')` contract so
70
+ // callers can keep using `.slice(1, -1)`.
71
+ function splitTableRow(line) {
72
+ const cells = []
73
+ let current = ''
74
+ for (let i = 0; i < line.length; i++) {
75
+ const ch = line[i]
76
+ if (ch === '\\' && line[i + 1] === '|') {
77
+ current += '|' // unescape: keep the literal pipe inside this cell
78
+ i++ // consume the escaped '|'
79
+ continue
80
+ }
81
+ if (ch === '|') {
82
+ cells.push(current)
83
+ current = ''
84
+ continue
85
+ }
86
+ current += ch
87
+ }
88
+ cells.push(current)
89
+ return cells
90
+ }
91
+
62
92
  // ── AC table parser (prerequisite for checks 19–21) ─────────────────────────
63
93
 
64
94
  function parseAcTable(requirementsPath) {
@@ -122,7 +152,7 @@ function parseAcTable(requirementsPath) {
122
152
  }
123
153
 
124
154
  // ── Validate column headers ────────────────────────────────────────────────
125
- const headerCells = lines[tableHeaderIdx].split('|').slice(1, -1).map(c => c.trim())
155
+ const headerCells = splitTableRow(lines[tableHeaderIdx]).slice(1, -1).map(c => c.trim())
126
156
  if (headerCells.length < 3) {
127
157
  process.stderr.write('Error: AC table header has fewer than 3 columns\n')
128
158
  process.exit(2)
@@ -143,7 +173,7 @@ function parseAcTable(requirementsPath) {
143
173
  if (!line.trim().startsWith('|')) break
144
174
 
145
175
  // Skip separator rows: every cell is only dashes, colons, or spaces
146
- const rawCells = line.split('|').slice(1, -1)
176
+ const rawCells = splitTableRow(line).slice(1, -1)
147
177
  if (rawCells.every(c => /^[\s\-:]+$/.test(c))) continue
148
178
 
149
179
  const cells = rawCells.map(c => c.trim())
@@ -157,7 +157,7 @@ Append a horizontal rule (`---`) followed by:
157
157
  |-------|------------|-------|-------------|---------------|-------|---------------|-----------------|----------|
158
158
  | {agent} | {scope} | {model} | {est} | {actual} | {±delta} | {est_cost} | {actual_cost} | {Xmin Ys} |
159
159
  ...
160
- | assessment-manager/am-phase1 (orchestrator) | — | sonnet | 80,000 | {actual} | ±{delta} | $0.4320 | ${actual_cost} | {duration} |
160
+ | am-phase1 (orchestrator) | — | sonnet | 80,000 | {actual} | ±{delta} | $0.4320 | ${actual_cost} | {duration} |
161
161
  ```
162
162
 
163
163
  Column rules:
@@ -3,10 +3,10 @@ description: "Define Feature — reads existing repo context, then grills only o
3
3
  disable-model-invocation: true
4
4
  ---
5
5
 
6
- Spawn the `define-feature` agent, passing through whatever the user provided as a source reference (a backlog entry, ticket path, spec path, or free-text description). If the user gave nothing, pass an empty source.
6
+ Spawn the `gaia-define-feature` agent, passing through whatever the user provided as a source reference (a backlog entry, ticket path, spec path, or free-text description). If the user gave nothing, pass an empty source.
7
7
 
8
8
  ```
9
- subagent_type: define-feature
9
+ subagent_type: gaia-define-feature
10
10
  prompt: |
11
11
  Start the feature definition session.
12
12
  Source reference (may be empty): {{user arguments verbatim}}
@@ -37,7 +37,7 @@ Here's what I know and what's available for you:
37
37
  - `/feature-status`, `/check-docs`, `/next-task`, `/pr-description`
38
38
  - `/assessment-status`
39
39
 
40
- **Specialized agents** (spawnable): project-manager, developer-backend, developer-frontend, developer-testing, review-solution, assessment-manager, and more.
40
+ **Specialized agents** (spawnable): developer-backend, developer-frontend, developer-testing, review-solution, and more.
41
41
 
42
42
  Let me take a look at your workspace to understand the context…
43
43