@cleocode/skills 2026.5.122 → 2026.5.124

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cleocode/skills",
3
- "version": "2026.5.122",
3
+ "version": "2026.5.124",
4
4
  "description": "CLEO skill definitions - bundled with CLEO monorepo",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -2,8 +2,8 @@
2
2
  name: ct-cleo
3
3
  description: CLEO task management protocol - session, task, and workflow guidance. Use when managing tasks, sessions, or multi-agent workflows with the CLEO CLI protocol.
4
4
  metadata:
5
- version: 2.3.0
6
- lastReviewed: 2026-05-24
5
+ version: 2.5.0
6
+ lastReviewed: 2026-05-26
7
7
  stability: stable
8
8
  ---
9
9
 
@@ -14,8 +14,8 @@ Full protocol content lives in `~/.cleo/templates/CLEO-INJECTION.md`.
14
14
  Emit any section with: `cleo briefing inject --section <name>`
15
15
 
16
16
  Supported sections: `session-start` · `work-loop` · `triggers` · `task-creation`
17
- · `task-discovery` · `session-commands` · `memory` · `nexus` · `orchestration`
18
- · `playbooks` · `documents` · `error-handling` · `pre-complete-gate`
17
+ · `task-discovery` · `task-relationships` · `session-commands` · `memory` · `nexus`
18
+ · `orchestration` · `playbooks` · `documents` · `error-handling` · `pre-complete-gate`
19
19
  · `spawn-tiers` · `rules` · `memory-jit` · `escalation`
20
20
 
21
21
  ## Quick Reference
@@ -23,235 +23,196 @@ Supported sections: `session-start` · `work-loop` · `triggers` · `task-creati
23
23
  | Need | Command |
24
24
  |------|---------|
25
25
  | Start session | `cleo session status` → `cleo briefing` |
26
- | Find work | `cleo next` → `cleo show <id>` |
26
+ | Find work | `cleo next` → `cleo focus <id>` |
27
27
  | Search tasks | `cleo find "query"` |
28
28
  | Complete task | `cleo verify T### --gate ... --evidence "..."` → `cleo complete T###` |
29
29
  | Save memory | `cleo memory observe "..." --title "..."` |
30
30
  | Spawn subagent | `cleo orchestrate spawn <taskId> --tier 2` |
31
- | Create a Saga (above-Epic group, ADR-073) | `cleo saga create --title "..." --acceptance "..."` |
32
- | Link Epic to Saga | `cleo saga add <sagaId> <epicId>` |
31
+ | Create a Saga | `cleo saga create --title "..." --acceptance "..."` |
32
+ | Saga-level ready | `cleo orchestrate ready <sagaId>` |
33
+ | Saga-level waves | `cleo orchestrate waves <sagaId>` |
34
+ | Saga rollup | `cleo saga rollup <sagaId>` |
33
35
  | List Saga members | `cleo saga members <sagaId>` |
34
36
 
35
37
  ## Skill-Specific Extensions
36
38
 
37
- ### Task Hierarchy (canonical source: ADR-073 §1)
39
+ - Task hierarchy, Saga commands, add-batch decomposition, docs policy, and CLI output details live in CLEO-INJECTION.md; emit `task-creation`, `documents`, and `pre-complete-gate` when needed.
40
+ - For add-batch input, The top-level JSON MUST be an array of task objects, not an object wrapper like `{ "tasks": [...] }`.
41
+ - Dry-run count semantics: `/data/count` and `/data/wouldCreate` predict writes; `/data/insertedCount` must be `0` for dry-run.
42
+ - Mutation output paths: use `/data/created/0`, `/data/updated/0`, and `/data/deleted/0`; never parse legacy full records.
43
+ - Docs path policy and strict preflight: keep docs repo-relative, Do not pass arbitrary external absolute paths, and discover runtime kinds with `cleo docs list-types` / `DocKindRegistry`.
38
44
 
39
- CLEO has 4 tiers. Each defined by scope-of-change + agent ownership. All IDs stored as `T####`;
40
- `type` column discriminates; prefixes (`SG-`, `E-`, `T-`) are display + import-mapping only.
45
+ ### Task Relationship Systems depends, blockedBy, relates
41
46
 
42
- | Tier | Prefix | Scope-of-change | Owner (ADR-070) |
43
- |---------|--------|-------------------------------------------------|----------------------|
44
- | Saga | `SG-` | ≥2 Epics across ≥2 releases (themed grouping) | Orchestrator (read) |
45
- | Epic | `E-` | One releasable slice; ≥1 PR to `main` | Orchestrator (HITL) |
46
- | Task | `T-` | One atomic PR-sized change; single wave | Phase Lead |
47
- | Subtask | (none) | One commit; ≤2 files; rolls up to Task's PR | Worker (leaf) |
47
+ CLEO has **three distinct relationship systems** with different storage, semantics, and CLI exposure. Do not conflate them.
48
48
 
49
- **I8 Subtask-to-PR aggregation:** A Task ships as exactly ONE PR. Subtasks contribute commits
50
- to that single PR; Subtasks never own a PR. Promote a Subtask to a sibling Task if it warrants
51
- its own PR.
49
+ | System | Storage | Semantics | CLI Exposure |
50
+ |--------|---------|-----------|--------------|
51
+ | `depends` | `task_dependencies` table (`task_id`, `depends_on`) | **Blocking dependency** — task cannot start until all `depends` tasks are `done` | `cleo add --depends T1,T2` / `cleo update --depends` / `--add-depends` / `--remove-depends` |
52
+ | `blockedBy` | `tasks.blocked_by` column (free-text) | **Human-readable reason** why a task is blocked (e.g. "waiting for API key") | `cleo update --blocked-by "reason"` / `--clear-blocked-by` |
53
+ | `relates` | `task_relations` table (`task_id`, `related_to`, `relation_type`, `reason`) | **Semantic, non-blocking** relationships: `blocks`, `related`, `duplicates`, `absorbs`, `fixes`, `extends`, `supersedes` | `cleo relates add <from> <to> <type> <reason>` / `cleo relates remove` / `cleo relates list` |
52
54
 
53
- Full charter (8 invariants + lifecycle decision table + prefix registry) lives in
54
- `.cleo/adrs/ADR-073-above-epic-naming.md` §1–§2. CLI commands for Sagas: see
55
- CLEO-INJECTION.md `task-creation` section (`cleo briefing inject --section task-creation`).
55
+ #### Key distinction
56
56
 
57
- For full decision trees and operation reference tables, emit sections above.
57
+ - **`depends`** controls **execution order** (wave planning, `cleo next` eligibility). It is a hard dependency.
58
+ - **`blockedBy`** is a **status annotation** — it does NOT link to another task, it just explains why this task is `blocked`.
59
+ - **`relates`** is **informational linkage** — it does NOT block execution, but it records that two tasks have a semantic relationship (e.g. "T1001 supersedes T1002" or "T1003 duplicates T1004").
58
60
 
59
- ## Human Render Contract (ADR-077)
61
+ #### CRITICAL: Do NOT use `relates` for execution gates
60
62
 
61
- Every CLI command emits a typed `RenderableEnvelope<T>` from `@cleocode/contracts`.
62
- Agents can route their own rendering off `envelope.data.kind` without re-parsing
63
- the payload shape. Canonical patterns:
63
+ `relates` is **never** a blocking dependency. If task B must wait for task A to finish, use `--depends`:
64
64
 
65
- | Goal | Command |
66
- |------|---------|
67
- | Show one task (typed envelope; human render via core registry) | `cleo show T<id>` |
68
- | Force human render when JSON is the default | `cleo show T<id> --human` |
69
- | Generic hierarchy walker from any root (B9 / T10134) | `cleo tree T<id>` |
65
+ ```bash
66
+ # CORRECT — execution dependency
67
+ cleo add "Implement auth" --depends T1001,T1002
70
68
 
71
- `cleo tree <id>` walks both `parent` and `task_relations.relation_type='groups'`
72
- edges to full depth useful for Saga → Epic → Task → Subtask snapshots from
73
- any starting node.
69
+ # WRONG relates does NOT block execution
70
+ cleo relates add T1003 T1001 blocks "waiting for auth"
71
+ ```
74
72
 
75
- ### `RenderableEnvelope<T>` discriminator
73
+ #### Common pitfall: using `blockedBy` for task IDs
76
74
 
77
- `envelope.data.kind` is one of:
75
+ `--blocked-by` expects a **string reason**, not task IDs. To express "this task is blocked until that task finishes", use `--depends`:
78
76
 
79
- | `kind` | Payload shape | Renderer family |
80
- |--------|---------------|-----------------|
81
- | `'tree'` | `TreeResponse<T>` (flat-node form) | `renderTree` |
82
- | `'table'` | `TableResponse<T>` (rows + schema) | `renderTable` |
83
- | `'list'` | `ListResponse<T>` | `renderList` |
84
- | `'grouped-list'` | `GroupedListResponse<T>` | `renderGroupedList` |
85
- | `'section'` | `{icon, header, items}` | `renderSection` |
86
- | `'single'` | single-record detail | per-command renderer |
87
- | `'generic'` | fallback `Record<string, unknown>` | kv-block helper |
77
+ ```bash
78
+ # CORRECT
79
+ cleo add "Implement auth" --depends T1001
88
80
 
89
- ### Where the rendering lives
81
+ # WRONG blocked-by is free text, not a task reference
82
+ cleo update T1003 --blocked-by T1001
83
+ ```
90
84
 
91
- All rendering logic registry, families, primitives — lives under
92
- `packages/core/src/render/`. `packages/cleo/src/cli/renderers/index.ts` is a
93
- ~20-LOC thin dispatcher. Static UI primitives (Tree, Table, Section, Badge,
94
- Legend) live under `packages/animations/render/`. Typed icon enums
95
- (`StatusIcon`, `KindIcon`, `BadgeIcon`, `RelationIcon`) live in
96
- `@cleocode/contracts/render/icon.ts`. Family renderers self-register at
97
- module load via `registerRenderer(command, kind, fn)` — importing
98
- `@cleocode/core/render` populates every slot via side-effect re-exports.
85
+ #### `cleo relates` command reference
99
86
 
100
- Full architecture + invariants: see `cleo docs fetch adr-077-human-render-contract`.
87
+ ```bash
88
+ # Add a semantic relationship
89
+ cleo relates add T1001 T1002 supersedes "T1002 is absorbed into the new auth flow"
101
90
 
102
- ## Decomposing an epic into N tasks
91
+ # List relations for a task
92
+ cleo relates list T1001
103
93
 
104
- When you need to bulk-create child tasks under an epic, use `cleo add-batch`. It inserts all
105
- tasks in a single atomic transaction — if ANY task fails validation, ALL inserts are rolled back.
106
- This is the canonical pattern for epic decomposition; prefer it over N sequential `cleo add` calls.
94
+ # Remove a relation
95
+ cleo relates remove T1001 T1002
107
96
 
108
- ### Canonical command
97
+ # Suggest related tasks based on shared attributes
98
+ cleo relates suggest T1001 --threshold=50
109
99
 
110
- ```bash
111
- cleo add-batch --file tasks.json --parent <epicId>
100
+ # Discover related tasks using various methods
101
+ cleo relates discover T1001
112
102
  ```
113
103
 
114
- ### Minimal JSON example
115
-
116
- Create a `tasks.json` file (array of task objects):
117
-
118
- ```json
119
- [
120
- {
121
- "title": "Research: survey add-batch prior art",
122
- "acceptance": "Written summary of 3+ prior approaches|Coverage of rollback semantics"
123
- },
124
- {
125
- "title": "Implement: add-batch CORE op with atomic semantics",
126
- "acceptance": "All tasks inserted or none|Returns IDs of created tasks"
127
- },
128
- {
129
- "title": "TF: teach add-batch in ct-cleo SKILL.md + CLEO-INJECTION",
130
- "acceptance": "SKILL.md contains Decomposing an epic section|INJECTION Task Creation table includes add-batch row"
131
- }
132
- ]
133
- ```
104
+ Valid relation types: `blocks`, `related`, `duplicates`, `absorbs`, `fixes`, `extends`, `supersedes`.
134
105
 
135
- Every object in the array supports the same fields as `cleo add` (`title`, `acceptance`,
136
- `kind`, `priority`, `size`, `labels`, `depends`). The `--parent` flag applies to all items.
106
+ #### Schema types mismatch note
137
107
 
138
- ### Flags
108
+ The DB schema `TASK_RELATION_TYPES` (`related`, `blocks`, `duplicates`, `absorbs`, `fixes`, `extends`, `supersedes`) must match the runtime types. The CLI `cleo relates add` accepts the DB schema types. Always normalize to the DB enum before persisting.
139
109
 
140
- | Flag | Description |
141
- |------|-------------|
142
- | `--file <path>` | Path to JSON file (array of task objects) |
143
- | `-` | Read JSON array from stdin (`echo '[...]' \| cleo add-batch --file - --parent <id>`) |
144
- | `--parent <id>` | Parent epic/task ID. All created tasks become direct children. |
145
- | `--dry-run` | Validate and preview all tasks without inserting. Shows what would be created. |
110
+ ## Task Hierarchy (PM-Core V2 — ADR-088)
146
111
 
147
- ### Rollback semantic
112
+ **Canonical source:** `docs/adr/ADR-088-pm-core-v2-workgraph-relations-completion-criteria.md`.
148
113
 
149
- ```
150
- ANY task fails → ALL inserts rolled back (zero partial state)
151
- ```
114
+ | Tier | Prefix | type value | Scope-of-change |
115
+ |---------|--------|------------|----------------------------------------------------|
116
+ | Saga | `SG-` | `saga` | Theme grouping ≥2 Epics across ≥2 releases |
117
+ | Epic | `E-` | `epic` | One releasable slice; ≥1 PR to `main` |
118
+ | Task | `T-` | `task` | One atomic PR-sized change; single wave |
119
+ | Subtask | (none) | `subtask` | One commit; ≤2 files; contributes to Task's PR |
152
120
 
153
- Run `--dry-run` first to catch validation errors (missing `acceptance`, title too long, etc.)
154
- before committing the batch.
121
+ **Containment (I1):** `tasks.parent_id` is the **only** containment edge. Direct children,
122
+ ancestor/descendant traversal, closure rollups, and default parent completion are all derived
123
+ from `parent_id`. The parent matrix is:
155
124
 
156
- ### Meta-dogfood: how T9813 itself was decomposed
125
+ | Child type | Parent type |
126
+ |------------|-----------------|
127
+ | `subtask` | `task` |
128
+ | `task` | `epic` |
129
+ | `epic` | `saga` or `null`|
130
+ | `saga` | `null` |
157
131
 
158
- The Epic T9813 (`add-batch` feature saga) used this exact pattern to create its child tasks
159
- (T9814–T9819) in a single call. Use the task decomposition from your epic planning as the
160
- input JSON — `cleo show <epicId>` acceptance criteria → tasks array → `cleo add-batch`.
132
+ **Storage (I2):** All IDs stored as `T####`; `type` column discriminates tier (not `label`).
133
+ Prefixes (`SG-`, `E-`) are DISPLAY + import-mapping only.
161
134
 
162
- ### Related
135
+ **Non-containment (I3):** `task_relations` is for secondary graph semantics ONLY — dependency,
136
+ ordering, cross-reference, evidence, supersession, provenance. `task_relations` MUST NOT satisfy
137
+ containment, child listing, ancestor/descendant traversal, parent rollup, parent completion,
138
+ nesting-budget, or closure semantics. The `groups` relation type is retired; do not use it for
139
+ hierarchy.
163
140
 
164
- - Saga/Epic workflow: `cleo briefing inject --section task-creation`
165
- - Single task: `cleo add --type task --parent <id> --acceptance "..." --title "..."`
141
+ ## Typed Completion Criteria (PM-Core V2)
166
142
 
167
- ---
143
+ `task_acceptance_criteria.kind` is one of:
168
144
 
169
- ## Worktree-Aware CLI Routing (T10389 / ADR-068 amendment §3.1)
145
+ | Kind | Requires `target_task_id` | Purpose |
146
+ |------|--------------------------|---------|
147
+ | `text` | No | Human-authored acceptance criterion |
148
+ | `child_task` | **Yes** | Deterministic projection from a direct `parent_id` child |
149
+ | `evidence_bound` | No | Gate-backed criterion (`implemented`, `testsPassed`, `qaPassed`) |
170
150
 
171
- Two CLI verbs auto-route their writes back to the canonical project
172
- root when invoked from inside an agent-spawned worktree:
151
+ **Key rules:**
152
+ - A parent with children uses `child_task` criteria by default; these are **deterministic
153
+ projections** from `parent_id` containment.
154
+ - `text` and `evidence_bound` criteria must NOT use `target_task_id`.
155
+ - Cancelled children do NOT automatically satisfy parent completion.
156
+ - Adding child work under a done parent reopens affected ancestors.
173
157
 
174
- - `cleo docs add <ownerId> <file> --type <kind> --slug <slug>` — the
175
- blob lands in the MAIN repo's `tasks.db`. The file path is resolved
176
- against the WORKTREE cwd (not the canonical root) before dispatch,
177
- so relative paths like `docs/note.md` work as expected from inside
178
- the worktree.
179
- - `cleo changeset add --slug <slug> --tasks <ids> --kind <kind> --summary <text>` —
180
- dual-writes to `<canonical-root>/.changeset/<slug>.md` AND the SSoT
181
- blob store. The `.changeset/` file lands in the MAIN repo, never
182
- the worktree.
158
+ ## Saga Operations (PM-Core V2)
183
159
 
184
- Both verbs detect stray `.cleo/tasks.db` inside the worktree
185
- (pre-T9803 leak or rogue write) and emit `E_STRAY_WORKTREE_DB` with a
186
- clear `rm -rf <worktree>/.cleo` remediation BEFORE the deeper DB
187
- chokepoint guard fires.
160
+ Saga-level orchestration is first-class. Saga membership uses `parent_id`
161
+ containment (NOT `task_relations.groups`). Use saga IDs directly with orchestrate commands:
188
162
 
189
163
  ```bash
190
- # from ~/.local/share/cleo/worktrees/<hash>/T10389/
191
- cleo docs add T10389 ./investigation.md --type research --slug t10389-research
192
- # stderr: [T10389] routing SSoT write from worktree cwd ... → canonical project root ...
193
- # row lands in main repo's tasks.db, retrievable via `cleo docs fetch t10389-research`
194
- ```
164
+ # Saga-level ready frontier — parallel-safe tasks across all member epics
165
+ cleo orchestrate ready <sagaId>
195
166
 
196
- If you see `E_PATH_TRAVERSAL`, `E_FILE_ERROR: Cannot read file`, or
197
- `E_WT_DB_ISOLATION_VIOLATION` when calling either verb, update to a
198
- build that ships the T10389 fix-pack (closes T10353 + T10354 + T10294
199
- + T10365). Suppress the routing log with `CLEO_QUIET=1` for clean
200
- stderr in automation.
167
+ # Saga-level dependency waves unified wave plan across all member epics
168
+ cleo orchestrate waves <sagaId>
201
169
 
202
- ---
170
+ # Saga status rollup — completion %, member counts
171
+ cleo saga rollup <sagaId>
203
172
 
204
- ## CLI Output Contract (ADR-086 / Epic T9927 / E9 of Saga T9855)
205
-
206
- `cleo` stdout is now **envelope-only**. NEVER pipe `cleo` output through
207
- `tail`/`jq`/`python` — every common shape has a first-class flag.
173
+ # Saga membership listing via parent_id containment
174
+ cleo saga members <sagaId>
175
+ ```
208
176
 
209
- | Need | Flag | Example |
210
- |------|------|---------|
211
- | Scalar extract (no jq) | `--field <jsonpointer>` | `cleo add 'X' --acceptance "..." --field /data/task/id` |
212
- | ID-only pipeline | `--output id` | `cleo list --parent EPIC --output id \| while read c; do …; done` |
213
- | Affected count | `--output count` | `cleo list --parent EPIC --status pending --output count` |
214
- | TSV (no header) | `--output table` | `cleo list --parent EPIC --output table` |
215
- | Silent (exit code only) | `--output silent` | `cleo update T123 --status done --output silent` |
216
- | 1-line per record | `--summary` | `cleo list --parent EPIC --summary` |
217
- | Suppress stderr noise | `--quiet` | `id=$(cleo add 'X' --acceptance "..." --quiet --field /data/task/id)` |
218
- | Full record (legacy) | `--full` | `cleo show T123 --full` |
177
+ **Epic-level fallback:** If saga-level orchestrate fails, enumerate member epics from
178
+ `cleo saga members <sagaId>` and call `cleo orchestrate ready <epicId>` for each member
179
+ individually. Do not use `task_relations.groups` as a fallback for hierarchy it is
180
+ non-containment only per I3.
219
181
 
220
- ### Defaults
182
+ ## WorkGraph (PM-Core V2)
221
183
 
222
- - **Read ops** (`show`, `list`, `find`) return the full LAFS envelope.
223
- - **Mutate ops** (`add`, `add-batch`, `update`, `complete`) — return a
224
- minimal envelope `{success, data: {count, ids[]}}` (T9931). Use `--full`
225
- to opt back into the full record set.
226
- - **stdout** carries exactly ONE LAFS envelope terminated by a single
227
- newline. Sub-step logs/progress/warnings route through Pino → stderr.
228
- This is regression-locked by CI gates `lint-stdout-discipline` (T10135)
229
- and `lint-stdout-write-allowlist` (T9924).
184
+ The WorkGraph subsystem provides scaffold validation, atomic application, and planning
185
+ document generation:
230
186
 
231
- ### Canonical agent patterns
187
+ | Feature | What it does |
188
+ |---------|--------------|
189
+ | Scaffold Dry-Run Validator | Validates WorkGraph JSON payloads against schema invariants before mutation. Returns `wouldCreate`/`wouldUpdate`/`wouldDelete` without side effects. |
190
+ | Scaffold Apply Engine | Atomically applies validated WorkGraph scaffolds to the task database. Creates, updates, and deletes tasks/relations/ACs in a single transaction. Sibling-relation-based (SQLite trigger blocks parent-child relation edges). |
191
+ | Planning Doc Generator | `generatePlanningDoc()` produces structured markdown plans from the WorkGraph. Supports "agent" (compact) and "maintainer" (prose) output modes. |
232
192
 
193
+ Example — dry-run a scaffold before applying:
233
194
  ```bash
234
- # Scalar extract — no jq needed.
235
- id=$(cleo add 'Title' --type task --parent T9927 --acceptance "..." \
236
- --field /data/task/id)
195
+ # Validate scaffold payload
196
+ cleo workgraph validate --file scaffold.json --dry-run
237
197
 
238
- # ID-only pipeline no JSON parsing.
239
- cleo list --parent T9927 --output id | while read child; do
240
- cleo verify "$child" --gate qaPassed --evidence "tool:lint;tool:typecheck"
241
- done
198
+ # Apply validated scaffold atomically
199
+ cleo workgraph apply --file scaffold.json
200
+ ```
242
201
 
243
- # Count-only check.
244
- remaining=$(cleo list --parent T9927 --status pending --output count)
202
+ ## Task Context (PM-Core V2)
245
203
 
246
- # Fully clean pipeline stdout has IDs, stderr is empty unless error.
247
- cleo add-batch --file /tmp/batch.json --parent T9927 --quiet --output id
248
- ```
204
+ Bounded task context with token budgeting for agent ergonomics:
249
205
 
250
- ### Anti-patterns (REJECTED these are CLI bugs if they appear post-E9)
206
+ | Feature | What it does |
207
+ |---------|--------------|
208
+ | Task Context Pack | `coreTaskContext` returns targeted task information (identity, acceptance criteria, blockers, edges, activity) respecting a configurable token budget. Uses `TasksContextOmission` to track overages and provides expansion hints. |
209
+ | Saga Context & Readiness | Saga-level aggregate rollups: completion percentages, ready-frontiers, and blocker enumeration across all member epics via `parent_id` containment. |
251
210
 
252
- - `cleo show T123 | tail -1 | jq -r .data.task.id` → use `--field /data/task/id`
253
- - ❌ `cleo list --parent E1 | jq -r '.data.tasks[].id'` → use `--output id`
254
- - `cleo show T123 | python3 -c 'import json,sys; …'` → use `--field`
255
- - ❌ `cleo add 'X' 2>&1 | grep -oE 'T[0-9]+'` → use `--field /data/task/id`
211
+ Example get task context for agent use:
212
+ ```bash
213
+ # Full context with budget
214
+ cleo orchestrate report <taskId>
256
215
 
257
- Full contract + RFC 2119 invariants: `cleo docs fetch adr-086-cli-output-contract-e9`.
216
+ # Compact context for prompt injection
217
+ cleo focus <taskId>
218
+ ```
@@ -72,6 +72,31 @@ describe('ct-cleo SKILL.md — command correctness', () => {
72
72
  it('references cleo orchestrate spawn (the canonical spawn command)', () => {
73
73
  expect(skillContent).toContain('cleo orchestrate spawn');
74
74
  });
75
+
76
+ it('documents add-batch input as a top-level JSON array', () => {
77
+ expect(skillContent).toContain('The top-level JSON MUST be an array of task objects');
78
+ expect(skillContent).toContain('not an object wrapper like `{ "tasks": [...] }`');
79
+ });
80
+
81
+ it('uses mutation projection field paths instead of legacy full-record paths', () => {
82
+ expect(skillContent).toContain('/data/created/0');
83
+ expect(skillContent).toContain('/data/updated/0');
84
+ expect(skillContent).toContain('/data/deleted/0');
85
+ expect(skillContent).not.toContain('--field /data/task/id');
86
+ });
87
+
88
+ it('documents add-batch dry-run count semantics', () => {
89
+ expect(skillContent).toContain('/data/wouldCreate');
90
+ expect(skillContent).toContain('/data/insertedCount');
91
+ expect(skillContent).toContain('`0` for dry-run');
92
+ });
93
+
94
+ it('documents docs path policy and runtime doc kind discovery', () => {
95
+ expect(skillContent).toContain('Docs path policy and strict preflight');
96
+ expect(skillContent).toContain('Do not pass arbitrary external absolute paths');
97
+ expect(skillContent).toContain('cleo docs list-types');
98
+ expect(skillContent).toContain('DocKindRegistry');
99
+ });
75
100
  });
76
101
 
77
102
  // ---------------------------------------------------------------------------
@@ -117,4 +142,75 @@ describe('ct-cleo SKILL.md — phase coverage via emittable sections', () => {
117
142
  it('mentions work-loop section (core workflow loop)', () => {
118
143
  expect(skillContent).toContain('work-loop');
119
144
  });
145
+
146
+ it('mentions task-relationships section (PM-Core V2 relationship systems)', () => {
147
+ expect(skillContent).toContain('task-relationships');
148
+ });
149
+ });
150
+
151
+ // ---------------------------------------------------------------------------
152
+ // PM-Core V2 doctrine coverage (T10645)
153
+ // ---------------------------------------------------------------------------
154
+
155
+ describe('ct-cleo SKILL.md — PM-Core V2 doctrine (T10645)', () => {
156
+ it('documents PM-Core V2 task hierarchy with type=saga canonical', () => {
157
+ expect(skillContent).toContain('PM-Core V2');
158
+ expect(skillContent).toContain('ADR-088');
159
+ // Table shows `saga` as the type value in the hierarchy table
160
+ expect(skillContent).toMatch(/`saga`/);
161
+ expect(skillContent).toContain('parent_id');
162
+ });
163
+
164
+ it('documents I1 containment invariant (parent_id is only containment edge)', () => {
165
+ expect(skillContent).toContain('containment edge');
166
+ expect(skillContent).toContain('I1');
167
+ });
168
+
169
+ it('documents I3 non-containment invariant (task_relations is secondary only)', () => {
170
+ expect(skillContent).toContain('I3');
171
+ expect(skillContent).toContain('MUST NOT satisfy containment');
172
+ });
173
+
174
+ it('documents Saga operations via parent_id containment', () => {
175
+ expect(skillContent).toContain('cleo orchestrate ready <sagaId>');
176
+ expect(skillContent).toContain('cleo orchestrate waves <sagaId>');
177
+ expect(skillContent).toContain('cleo saga rollup <sagaId>');
178
+ });
179
+
180
+ it('does NOT present task_relations.groups as active hierarchy guidance', () => {
181
+ // Migration context references (e.g. "T10638 migration removes legacy groups")
182
+ // are acceptable; active guidance must not recommend groups for hierarchy.
183
+ // Must contain explicit anti-groups guidance.
184
+ expect(skillContent).toMatch(/not.*task_relations\.groups|Do not use.*task_relations\.groups/);
185
+ expect(skillContent).not.toMatch(/^[^#]*use.*task_relations\.groups.*for hierarchy/);
186
+ });
187
+
188
+ it('documents Task Context subsystem (T10629/T10630/T10631)', () => {
189
+ expect(skillContent).toContain('Task Context');
190
+ expect(skillContent).toContain('T10629');
191
+ expect(skillContent).toContain('cleo context');
192
+ });
193
+
194
+ it('documents WorkGraph scaffold subsystem (T10632/T10633/T10634)', () => {
195
+ expect(skillContent).toContain('WorkGraph');
196
+ expect(skillContent).toContain('T10632');
197
+ expect(skillContent).toContain('cleo graph validate');
198
+ expect(skillContent).toContain('cleo graph apply');
199
+ });
200
+
201
+ it('documents Completion Criteria with typed ACs (child_task / text / evidence_bound)', () => {
202
+ expect(skillContent).toContain('child_task');
203
+ expect(skillContent).toContain('evidence_bound');
204
+ expect(skillContent).toContain('typed acceptance criteria');
205
+ expect(skillContent).toContain('T10639');
206
+ });
207
+
208
+ it('indicates T10638 migration removed legacy groups hierarchy reads', () => {
209
+ expect(skillContent).toContain('T10638');
210
+ });
211
+
212
+ it('documents epic-level fallback for saga orchestrate', () => {
213
+ expect(skillContent).toContain('cleo saga members');
214
+ expect(skillContent).toMatch(/[Ee]pic-level/);
215
+ });
120
216
  });
@@ -109,6 +109,26 @@ describe('CLEO-INJECTION.md — command correctness', () => {
109
109
  it('contains "cleo find" command', () => {
110
110
  expect(injectionContent).toContain('cleo find');
111
111
  });
112
+
113
+ it('documents add-batch array input and dry-run count fields', () => {
114
+ expect(injectionContent).toContain('top-level JSON array of task objects');
115
+ expect(injectionContent).toContain('/data/wouldCreate');
116
+ expect(injectionContent).toContain('/data/insertedCount');
117
+ });
118
+
119
+ it('documents docs path policy, strict preflight, and runtime doc kinds', () => {
120
+ expect(injectionContent).toContain('repo-relative paths');
121
+ expect(injectionContent).toContain('arbitrary external absolute paths');
122
+ expect(injectionContent).toContain('cleo docs list-types');
123
+ expect(injectionContent).toContain('DocKindRegistry');
124
+ });
125
+
126
+ it('uses contract-backed mutate field paths', () => {
127
+ expect(injectionContent).toContain('/data/created/0');
128
+ expect(injectionContent).toContain('/data/updated/0');
129
+ expect(injectionContent).toContain('/data/deleted/0');
130
+ expect(injectionContent).not.toContain('--field /data/task/id');
131
+ });
112
132
  });
113
133
 
114
134
  // ---------------------------------------------------------------------------
@@ -105,18 +105,19 @@ that single PR. Subtasks never own a PR — if a unit warrants its own PR, it MU
105
105
  When decomposing, ask: "does this unit need its own PR?" → Task. "Is this a commit inside a
106
106
  larger PR?" → Subtask.
107
107
 
108
- ### Above-Epic: Sagas (ADR-073, v2026.5.77+)
108
+ ### Above-Epic: Sagas (PM-Core V2 — ADR-088)
109
109
 
110
110
  A **Saga** groups multiple Epics into a multi-release theme. Use a Saga when the work spans
111
- more than one releasable Epic and you want a stable handle for rollup/reporting. A Saga is a
112
- labeled top-level Epic (`label='saga'`) — NOT a new TaskType so the parent ladder (maxDepth=3)
113
- under each member Epic is unaffected.
111
+ more than one releasable Epic and you want a stable handle for rollup/reporting. A Saga uses
112
+ `type='saga'` (NOT `label='saga'`) — it is a peer type discriminator at the top of the
113
+ parent ladder. All IDs are stored as `T####`; prefixes (`SG-`, `E-`) are display-only.
114
+ Saga membership uses `parent_id` containment (NOT `task_relations.groups`).
114
115
 
115
116
  ```bash
116
117
  cleo saga create --title "Auth Modernization" --description "..." --acceptance "ac1|ac2|ac3|ac4|ac5"
117
- # returns SG-id (stored as T#### with label=saga)
118
- cleo saga add <sagaId> <epicId> # links member Epic via task_relations.type='groups'
119
- cleo saga members <sagaId> # returns member list (NOT cleo list --parent)
118
+ # returns SG-id (stored as T#### with type=saga)
119
+ cleo saga add <sagaId> <epicId> # sets epic.parent_id = sagaId (containment edge)
120
+ cleo saga members <sagaId> # returns members via parent_id containment
120
121
  cleo saga rollup <sagaId> # aggregates child statuses + completionPct
121
122
  ```
122
123
 
@@ -224,7 +224,7 @@ When operating without continuous HITL oversight, additional constraints apply:
224
224
  | `cleo orchestrate spawn T1586 --json` | Generate resolved spawn prompt |
225
225
  | `cleo orchestrate next --epic T1575` | Suggest next task |
226
226
  | `cleo saga rollup <sagaId>` | Cross-Epic status aggregation when orchestrating a multi-Epic Saga (ADR-073) |
227
- | `cleo saga members <sagaId>` | Member Epics of a Saga (NOT `cleo list --parent` Sagas use `task_relations.type=groups`) |
227
+ | `cleo saga members <sagaId>` | Member Epics of a Saga (parent_id containmentNOT `task_relations.groups`) |
228
228
  | `cleo pipeline stage.status --epic T1575` | Current pipeline stage |
229
229
  | `cleo pipeline stage.validate T1575 implementation` | Check gate before spawn |
230
230
  | `cleo pipeline stage.gate.pass T1575 research` | Advance pipeline stage |