@cleocode/skills 2026.5.83 → 2026.5.86

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 (55) hide show
  1. package/package.json +1 -1
  2. package/skills/_shared/__tests__/lifecycle-protocol-reconcile.test.ts +112 -0
  3. package/skills/_shared/__tests__/loom-adr-links.test.ts +163 -0
  4. package/skills/_shared/__tests__/loom-stage-coverage.test.ts +167 -0
  5. package/skills/ct-adr-recorder/SKILL.md +92 -0
  6. package/skills/ct-adr-recorder/__tests__/skill-adr-recorder.test.ts +65 -0
  7. package/skills/ct-consensus-voter/SKILL.md +14 -0
  8. package/skills/ct-contribution/SKILL.md +80 -0
  9. package/skills/ct-docs-lookup/SKILL.md +116 -1
  10. package/skills/ct-docs-lookup/references/ctx7-workflow.md +198 -0
  11. package/skills/ct-docs-lookup/references/library-id-resolution.md +217 -0
  12. package/skills/ct-docs-lookup/references/version-specific-docs.md +220 -0
  13. package/skills/ct-docs-review/SKILL.md +133 -1
  14. package/skills/ct-docs-review/__tests__/skill-docs-review.test.ts +53 -0
  15. package/skills/ct-docs-review/references/inline-comment-patterns.md +268 -0
  16. package/skills/ct-docs-review/references/pr-review-mode.md +270 -0
  17. package/skills/ct-docs-review/references/style-violations.md +341 -0
  18. package/skills/ct-docs-write/SKILL.md +157 -1
  19. package/skills/ct-docs-write/__tests__/skill-docs-write.test.ts +55 -0
  20. package/skills/ct-docs-write/references/audience-targeting.md +305 -0
  21. package/skills/ct-docs-write/references/cleo-style-guide.md +234 -0
  22. package/skills/ct-docs-write/references/markdown-patterns.md +329 -0
  23. package/skills/ct-documentor/SKILL.md +11 -0
  24. package/skills/ct-documentor/references/anti-patterns.md +216 -0
  25. package/skills/ct-documentor/references/chain-orchestration.md +194 -0
  26. package/skills/ct-documentor/references/doc-types-and-templates.md +301 -0
  27. package/skills/ct-documentor/references/style-coordination.md +195 -0
  28. package/skills/ct-epic-architect/SKILL.md +15 -0
  29. package/skills/ct-ivt-looper/SKILL.md +32 -0
  30. package/skills/ct-release-orchestrator/SKILL.md +16 -0
  31. package/skills/ct-research-agent/SKILL.md +24 -0
  32. package/skills/ct-research-agent/references/anti-patterns.md +154 -0
  33. package/skills/ct-research-agent/references/citation-and-evidence.md +140 -0
  34. package/skills/ct-research-agent/references/source-strategy.md +116 -0
  35. package/skills/ct-research-agent/references/triggers-and-routing.md +93 -0
  36. package/skills/ct-skill-validator/SKILL.md +19 -0
  37. package/skills/ct-skill-validator/scripts/check_depth.py +306 -0
  38. package/skills/ct-spec-writer/SKILL.md +86 -1
  39. package/skills/ct-spec-writer/__tests__/skill-spec-writer.test.ts +60 -0
  40. package/skills/ct-spec-writer/references/anti-patterns.md +176 -0
  41. package/skills/ct-spec-writer/references/rfc2119-language.md +138 -0
  42. package/skills/ct-spec-writer/references/spec-templates.md +233 -0
  43. package/skills/ct-spec-writer/references/traceability-matrix.md +145 -0
  44. package/skills/ct-task-executor/SKILL.md +25 -0
  45. package/skills/ct-task-executor/references/acceptance-criteria-mapping.md +163 -0
  46. package/skills/ct-task-executor/references/anti-patterns.md +201 -0
  47. package/skills/ct-task-executor/references/common-failures.md +193 -0
  48. package/skills/ct-task-executor/references/evidence-and-gates.md +179 -0
  49. package/skills/ct-task-executor/references/implementation-patterns.md +160 -0
  50. package/skills/ct-validator/SKILL.md +44 -0
  51. package/skills/ct-validator/references/anti-patterns.md +194 -0
  52. package/skills/ct-validator/references/compliance-reports.md +199 -0
  53. package/skills/ct-validator/references/schema-checking.md +191 -0
  54. package/skills/ct-validator/references/validation-modes.md +185 -0
  55. package/skills/manifest.json +82 -16
@@ -0,0 +1,116 @@
1
+ # Source Strategy
2
+
3
+ How to pick sources, what order to query them in, and how to bound the search
4
+ so that the research task completes within its token budget. The skill has
5
+ three primary source channels — web, Context7, and the local codebase — and
6
+ each carries different reliability and currency trade-offs.
7
+
8
+ ## Source Hierarchy
9
+
10
+ | Tier | Source | Strength | Weakness |
11
+ |------|--------|----------|----------|
12
+ | 1 | Local codebase | Ground truth for the project | Reflects past decisions, not future ones |
13
+ | 2 | Context7 (`ctx7 docs`) | Current official library docs | Limited to libraries published to Context7 |
14
+ | 3 | Web search | Breadth, recency, community wisdom | Variable signal-to-noise |
15
+ | 4 | LLM general knowledge | Conceptual framing | Stale, hallucination-prone |
16
+
17
+ Always query top-down. Codebase first — the answer may already exist as
18
+ prior art. Context7 second when a specific library or framework is named.
19
+ Web third when the question is open-ended. LLM general knowledge SHOULD be
20
+ used only to frame the question, never as a citable source.
21
+
22
+ ## Codebase Search Patterns
23
+
24
+ The skill operates in a worktree; the entire repository is reachable via
25
+ `Grep`, `Glob`, and `Read`. Use these patterns to find prior art quickly.
26
+
27
+ ```bash
28
+ # Find existing ADRs on the topic
29
+ Grep: pattern="<keyword>" path=".cleo/adrs/" output_mode="files_with_matches"
30
+
31
+ # Find prior research notes
32
+ Grep: pattern="<keyword>" path=".cleo/agent-outputs/" output_mode="files_with_matches"
33
+
34
+ # Find BRAIN decisions/patterns/observations on the topic
35
+ cleo memory find "<keyword>"
36
+
37
+ # Find related tasks
38
+ cleo find "<keyword>"
39
+ ```
40
+
41
+ When the topic touches code symbols, also use the GitNexus tools — they
42
+ return execution flows, callers, and impact rings that grep cannot surface.
43
+
44
+ ```bash
45
+ gitnexus_query({query: "<concept>"}) # process-grouped flows
46
+ gitnexus_context({name: "<symbol>"}) # 360 view of a symbol
47
+ gitnexus_impact({target: "<symbol>"}) # blast radius
48
+ ```
49
+
50
+ ## Context7 (ctx7) Workflow
51
+
52
+ For any question that names a library, framework, SDK, or CLI tool, use the
53
+ `ctx7` CLI before the open web. The workflow is two-step.
54
+
55
+ ```bash
56
+ # Step 1 — resolve the library ID
57
+ npx ctx7@latest library "<library-name>" "<user-question>"
58
+
59
+ # Step 2 — fetch docs for the resolved ID
60
+ npx ctx7@latest docs <libraryId> "<user-question>"
61
+
62
+ # Optional — retry with sandboxed agents pulling source + web
63
+ npx ctx7@latest docs <libraryId> "<user-question>" --research
64
+ ```
65
+
66
+ The official library name is required — pass `"Next.js"` not `"nextjs"`.
67
+ Version-specific docs use the `/org/project/version` form (e.g.
68
+ `/vercel/next.js/v14.3.0`). Pass the user's full question as the query —
69
+ specific queries return better matches than single words.
70
+
71
+ ## Web Search Tactics
72
+
73
+ Web search is the most variable channel. Apply these filters to keep the
74
+ signal high.
75
+
76
+ - Prefer the canonical source (official docs, GitHub README, RFC) over
77
+ blog posts, Medium, StackOverflow.
78
+ - Prefer recent results — append `2026`, `2025`, or the current year when
79
+ the topic is moving fast (LLM APIs, build tools, framework migrations).
80
+ - Cross-check at least two sources before stating a fact. A single blog
81
+ post is a lead, not a finding.
82
+ - Avoid AI-generated content farms — sites that publish hundreds of
83
+ thin "guides" daily are not citable.
84
+ - Strip tracking parameters from URLs when citing — they break and they
85
+ leak provenance.
86
+
87
+ ## Time-Boxing
88
+
89
+ Research is unbounded by nature; the skill MUST self-limit. Use this
90
+ schedule when the task does not specify otherwise.
91
+
92
+ | Topic complexity | Time budget | Sources to query |
93
+ |------------------|-------------|------------------|
94
+ | Single library/API question | 5 min | Context7 only |
95
+ | "What is the current best practice for X" | 15 min | Web + Context7 |
96
+ | "Compare options A, B, C for use-case Y" | 30 min | Web + Context7 + codebase |
97
+ | "Audit current implementation against state of the art" | 60 min | All sources |
98
+
99
+ When the budget is exhausted, write up what was found with `status: partial`
100
+ and list remaining questions in `needs_followup`. Partial research is more
101
+ useful than abandoned research.
102
+
103
+ ## Citation Format
104
+
105
+ Every finding MUST carry a source. Acceptable formats:
106
+
107
+ ```markdown
108
+ - According to [the Next.js routing docs](https://nextjs.org/docs/app), ...
109
+ - Context7 (`/vercel/next.js/v15`) confirms that ...
110
+ - See `.cleo/adrs/ADR-065-release-pipeline.md` §3 for prior decision on ...
111
+ - The current implementation at `packages/cleo/src/commands/release.ts:42` ...
112
+ - BRAIN observation `O-mpd07uma-0` records the pub1-diagnoser refusal pattern.
113
+ ```
114
+
115
+ Unsourced claims SHOULD be flagged with `[unsourced]` or moved to a
116
+ `Hypotheses` section. The reader must always be able to verify.
@@ -0,0 +1,93 @@
1
+ # Triggers and Routing
2
+
3
+ When to load `ct-research-agent`, what tasks it owns, and how it hands off
4
+ to neighboring skills in the RCASD-IVTR+C pipeline. The skill operates as the
5
+ `research` protocol within the orchestrator's stage taxonomy and is the first
6
+ skill invoked when an Epic enters its research lifecycle stage.
7
+
8
+ ## Primary Triggers
9
+
10
+ Load this skill when the orchestrator (or the user) requests any of the
11
+ following — the dispatch matrix in `manifest.json` enumerates the canonical
12
+ keyword set.
13
+
14
+ | Trigger phrase | Routing decision |
15
+ |----------------|------------------|
16
+ | "research <topic>" | Direct invocation — proceed with multi-source pull |
17
+ | "investigate <area>" | Same as research; emphasize codebase + web blend |
18
+ | "explore options for <decision>" | Treat as research → ct-consensus-voter |
19
+ | "what does the literature say about X" | Web + Context7 lookup, no codebase |
20
+ | "audit current <subsystem> implementation" | Codebase-only research; skip web |
21
+ | "compare libraries for <use-case>" | Web + Context7 with `--research` flag |
22
+ | "due-diligence on <vendor/tool>" | Web + reputation + license scan |
23
+
24
+ The orchestrator's `cleo orchestrate spawn` prompt always carries a
25
+ `stage: research` hint when the task's `pipelineStage` field equals
26
+ `research`. The skill SHOULD honor that hint and SHOULD NOT advance the
27
+ pipeline stage on its own — that is the orchestrator's responsibility once
28
+ the research manifest entry is appended.
29
+
30
+ ## Anti-Triggers (Do NOT Load)
31
+
32
+ The skill MUST NOT be loaded for the following requests because they belong
33
+ to sibling skills that have narrower context budgets and stricter contracts.
34
+
35
+ | Request | Correct skill |
36
+ |---------|---------------|
37
+ | "fix this failing test" | `ct-task-executor` |
38
+ | "write the spec for X" | `ct-spec-writer` |
39
+ | "validate this implementation against the spec" | `ct-validator` |
40
+ | "decompose this epic into tasks" | `ct-epic-architect` |
41
+ | "look up Next.js 15 middleware API" | `ct-docs-lookup` (single-library fetch) |
42
+ | "decide between A and B" (no investigation needed) | `ct-consensus-voter` |
43
+ | "explain this function" | (no skill — direct read suffices) |
44
+
45
+ A useful heuristic: if the user already knows the answer and just wants it
46
+ written down, route to `ct-spec-writer` or `ct-docs-write`. Research is for
47
+ when the answer is not yet known.
48
+
49
+ ## Routing to Downstream Skills
50
+
51
+ Research outputs typically feed one of these next stages. The manifest
52
+ `chains_to` array enumerates the legal handoffs.
53
+
54
+ 1. **`ct-spec-writer`** — when findings produce testable requirements.
55
+ Pass the research file path and the synthesized recommendations as
56
+ spec input. Use this when the task description contains "spec",
57
+ "contract", "RFC", or "protocol".
58
+ 2. **`ct-epic-architect`** — when findings change the scope estimate of
59
+ an Epic. The architect re-decomposes based on the new evidence.
60
+ 3. **`ct-consensus-voter`** — when research surfaces two-or-more viable
61
+ options with comparable trade-offs. The voter resolves the choice
62
+ under HITL when confidence < 0.5.
63
+ 4. **`ct-task-executor`** — only when the research itself contains an
64
+ actionable next step that does not require a spec (e.g. "bump the
65
+ library version" or "delete deprecated path"). Rare.
66
+
67
+ ## Decision Tree
68
+
69
+ ```
70
+ Is the answer already known and just needs documentation?
71
+ ├── YES → ct-docs-write
72
+ └── NO → continue
73
+ │
74
+ Is this a single-library API question?
75
+ ├── YES → ct-docs-lookup
76
+ └── NO → ct-research-agent (this skill)
77
+ │
78
+ After research, do findings produce requirements?
79
+ ├── YES → ct-spec-writer next
80
+ └── NO → continue
81
+ │
82
+ Do findings produce a decision between options?
83
+ ├── YES → ct-consensus-voter next
84
+ └── NO → return manifest only, no chain
85
+ ```
86
+
87
+ ## Stage Hint Compliance
88
+
89
+ The orchestrator's `pipeline_manifest` table tracks per-task stage
90
+ progression. The research skill MUST append exactly one entry with
91
+ `agent_type: "research"` and MUST NOT mutate stage fields directly. If
92
+ findings indicate a stage advance is warranted, the skill SHOULD include
93
+ that suggestion in `needs_followup` so the orchestrator can act on it.
@@ -36,8 +36,27 @@ python ${CLAUDE_SKILL_DIR}/scripts/audit_body.py <skill-dir>
36
36
 
37
37
  # Manifest alignment check:
38
38
  python ${CLAUDE_SKILL_DIR}/scripts/check_manifest.py <skill-dir> <manifest.json>
39
+
40
+ # Progressive-disclosure depth check (T9684 — CI gate):
41
+ python ${CLAUDE_SKILL_DIR}/scripts/check_depth.py <skill-dir>
42
+
43
+ # Repo-wide depth sweep:
44
+ python ${CLAUDE_SKILL_DIR}/scripts/check_depth.py <repo-root> --all
39
45
  ```
40
46
 
47
+ **Depth rule (T9684):** A skill PASSES when ANY of:
48
+
49
+ - SKILL.md body has ≥ 100 content lines, OR
50
+ - `references/` subdir has ≥ 3 markdown files, OR
51
+ - `manifest.json` `references[]` array enumerates ≥ 3 files (all on disk)
52
+
53
+ Pre-existing stubs are allowlisted with follow-up task IDs in
54
+ `scripts/check_depth.py::ALLOWLIST`. Gold-standard skills:
55
+ `ct-orchestrator` (9 refs) and `ct-skill-creator` (7 refs).
56
+
57
+ The depth check runs on every PR touching `packages/skills/skills/**`
58
+ via `.github/workflows/skills-depth-check.yml`.
59
+
41
60
  **Iteration rule**: If errors > 0, fix them in the skill's SKILL.md, re-run `validate.py`.
42
61
  Repeat until errors = 0. Do not proceed to Phase 2 while errors remain.
43
62
 
@@ -0,0 +1,306 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ CLEO Skill Depth Check — progressive-disclosure-depth rule (T9684).
4
+
5
+ Fails when a skill's SKILL.md is below the depth threshold AND it has
6
+ no references/ subdir with at least the manifest-declared reference
7
+ files. The gold standard is ct-orchestrator (9 references) and
8
+ ct-skill-creator (7 references); the rule is calibrated to flag
9
+ stubs without forcing every skill to that depth.
10
+
11
+ Rule logic:
12
+ PASS when ANY of:
13
+ - SKILL.md body has >= MIN_BODY_LINES content lines
14
+ - references/ subdir exists with >= MIN_REF_FILES files
15
+ - manifest.json references[] array populated with file paths that
16
+ all exist on disk
17
+
18
+ FAIL when:
19
+ - SKILL.md body < MIN_BODY_LINES lines AND
20
+ - references/ missing or has < MIN_REF_FILES files AND
21
+ - manifest.json references[] empty or files missing
22
+
23
+ Error message points at the gold standard and lists expected files
24
+ from the manifest entry (when present) so the fix is obvious.
25
+
26
+ Usage:
27
+ check_depth.py <skill-directory>
28
+ check_depth.py <skill-directory> --manifest path/to/manifest.json
29
+ check_depth.py <skill-directory> --all # walk all skills under a root
30
+ check_depth.py <skill-directory> --json
31
+ """
32
+ import sys
33
+ import re
34
+ import json
35
+ import argparse
36
+ from pathlib import Path
37
+
38
+
39
+ # Calibration knobs — set against the post-T9567 state.
40
+ # Adjust here, then re-run the audit to verify all 19 active skills pass.
41
+ MIN_BODY_LINES = 100
42
+ MIN_REF_FILES = 3
43
+ GOLD_STANDARDS = ("ct-orchestrator", "ct-skill-creator")
44
+
45
+ # Allowlist — pre-existing stub skills exempted at T9567 (E-SKILLS-DEPTH-BACKFILL).
46
+ # Each entry MUST have a follow-up task ID. Remove the entry once that task lands
47
+ # a depth backfill. New entries require owner approval — do not add silently.
48
+ ALLOWLIST: dict[str, str] = {
49
+ "ct-codebase-mapper": "T9567-followup: pre-existing; depth-backfill deferred",
50
+ "ct-master-tac": "T9567-followup: pre-existing; depth-backfill deferred",
51
+ "ct-memory": "T9567-followup: pre-existing; depth-backfill deferred",
52
+ "ct-stickynote": "T9567-followup: ephemeral note skill; minimal-by-design",
53
+ }
54
+
55
+
56
+ def count_body_lines(skill_md_path: Path) -> int:
57
+ """Count content lines in the SKILL.md body (excluding frontmatter)."""
58
+ if not skill_md_path.exists():
59
+ return 0
60
+ raw = skill_md_path.read_text(encoding="utf-8")
61
+ if not raw.startswith("---"):
62
+ # No frontmatter — count whole file
63
+ return len(raw.split("\n"))
64
+ parts = raw.split("---", 2)
65
+ if len(parts) < 3:
66
+ return 0
67
+ body = parts[2].strip()
68
+ if not body:
69
+ return 0
70
+ return len(body.split("\n"))
71
+
72
+
73
+ def manifest_references_for(skill_name: str, manifest_path: Path) -> list[str]:
74
+ """Return the references array from manifest.json for the given skill,
75
+ or empty list if not present."""
76
+ if not manifest_path.exists():
77
+ return []
78
+ try:
79
+ data = json.loads(manifest_path.read_text(encoding="utf-8"))
80
+ except json.JSONDecodeError:
81
+ return []
82
+ for entry in data.get("skills", []):
83
+ if entry.get("name") == skill_name:
84
+ return entry.get("references", []) or []
85
+ return []
86
+
87
+
88
+ def repo_root_of(skill_dir: Path) -> Path:
89
+ """Walk up from skill_dir to the repo root (heuristic: contains
90
+ `packages/skills/skills/manifest.json`)."""
91
+ cur = skill_dir.resolve()
92
+ for _ in range(10):
93
+ if (cur / "packages" / "skills" / "skills" / "manifest.json").exists():
94
+ return cur
95
+ if cur.parent == cur:
96
+ break
97
+ cur = cur.parent
98
+ return skill_dir # fallback
99
+
100
+
101
+ def check_depth(skill_path: Path, manifest_path: Path | None = None) -> tuple[bool, dict]:
102
+ """Run the depth check on a single skill directory.
103
+
104
+ Returns (passed, report_dict).
105
+ """
106
+ skill_dir = Path(skill_path).resolve()
107
+ skill_name = skill_dir.name
108
+ skill_md = skill_dir / "SKILL.md"
109
+ refs_dir = skill_dir / "references"
110
+
111
+ report: dict = {
112
+ "skill_name": skill_name,
113
+ "path": str(skill_dir),
114
+ "body_lines": 0,
115
+ "ref_files_on_disk": 0,
116
+ "manifest_references": [],
117
+ "manifest_references_missing": [],
118
+ "thresholds": {
119
+ "min_body_lines": MIN_BODY_LINES,
120
+ "min_ref_files": MIN_REF_FILES,
121
+ },
122
+ "passed": False,
123
+ "reasons": [],
124
+ "remediation": [],
125
+ }
126
+
127
+ # Threshold A: body length
128
+ body_lines = count_body_lines(skill_md)
129
+ report["body_lines"] = body_lines
130
+ body_passes = body_lines >= MIN_BODY_LINES
131
+
132
+ # Threshold B: references/ subdir
133
+ ref_files_on_disk = 0
134
+ if refs_dir.is_dir():
135
+ ref_files_on_disk = len([p for p in refs_dir.iterdir() if p.is_file() and p.suffix == ".md"])
136
+ report["ref_files_on_disk"] = ref_files_on_disk
137
+ refs_dir_passes = ref_files_on_disk >= MIN_REF_FILES
138
+
139
+ # Threshold C: manifest.json references populated and on disk
140
+ manifest_passes = False
141
+ if manifest_path is None:
142
+ # Auto-locate from repo root
143
+ root = repo_root_of(skill_dir)
144
+ manifest_path = root / "packages" / "skills" / "skills" / "manifest.json"
145
+
146
+ if manifest_path.exists():
147
+ manifest_refs = manifest_references_for(skill_name, manifest_path)
148
+ report["manifest_references"] = manifest_refs
149
+ if manifest_refs:
150
+ root = repo_root_of(skill_dir)
151
+ base = root / "packages" / "skills"
152
+ missing = []
153
+ for rel in manifest_refs:
154
+ ref_abs = base / rel
155
+ if not ref_abs.exists():
156
+ # Try relative to skill_dir as fallback
157
+ fallback = skill_dir / Path(rel).relative_to(Path(rel).parts[0]) \
158
+ if Path(rel).parts else None
159
+ if fallback is None or not fallback.exists():
160
+ missing.append(rel)
161
+ report["manifest_references_missing"] = missing
162
+ manifest_passes = (len(manifest_refs) >= MIN_REF_FILES and not missing)
163
+
164
+ # Decide pass/fail — ANY threshold passes ⇒ depth check passes.
165
+ passed = body_passes or refs_dir_passes or manifest_passes
166
+ report["passed"] = passed
167
+
168
+ if body_passes:
169
+ report["reasons"].append(f"body_lines={body_lines} >= {MIN_BODY_LINES}")
170
+ if refs_dir_passes:
171
+ report["reasons"].append(f"references/ has {ref_files_on_disk} files >= {MIN_REF_FILES}")
172
+ if manifest_passes:
173
+ report["reasons"].append(
174
+ f"manifest references[] populated with {len(report['manifest_references'])} files (all on disk)"
175
+ )
176
+
177
+ # Allowlist override — exempted skills pass with a reason captured.
178
+ if not passed and skill_name in ALLOWLIST:
179
+ passed = True
180
+ report["passed"] = True
181
+ report["allowlisted"] = True
182
+ report["allowlist_reason"] = ALLOWLIST[skill_name]
183
+ report["reasons"].append(f"allowlisted: {ALLOWLIST[skill_name]}")
184
+
185
+ if not passed:
186
+ report["reasons"].append("none of the three thresholds met")
187
+ report["remediation"] = [
188
+ f"Expand SKILL.md body to >= {MIN_BODY_LINES} content lines (currently {body_lines}), OR",
189
+ f"Add references/ subdir with >= {MIN_REF_FILES} markdown files (currently {ref_files_on_disk}), OR",
190
+ "Populate manifest.json references[] array for this skill with file paths.",
191
+ f"Gold-standard examples: {', '.join(GOLD_STANDARDS)}.",
192
+ ]
193
+ if report["manifest_references_missing"]:
194
+ report["remediation"].append(
195
+ "Manifest references[] lists files that do not exist on disk: "
196
+ + ", ".join(report["manifest_references_missing"])
197
+ )
198
+
199
+ return passed, report
200
+
201
+
202
+ def _print_report(report: dict) -> None:
203
+ """Print a single skill's depth report."""
204
+ name = report["skill_name"]
205
+ status = "PASS" if report["passed"] else "FAIL"
206
+ icon = "✅" if report["passed"] else "❌"
207
+ print(f"\n{icon} {status} {name}")
208
+ print(f" body_lines={report['body_lines']} (min {MIN_BODY_LINES})")
209
+ print(f" ref_files_on_disk={report['ref_files_on_disk']} (min {MIN_REF_FILES})")
210
+ print(f" manifest_references={len(report['manifest_references'])} files")
211
+ if report["manifest_references_missing"]:
212
+ print(f" manifest_references_missing={report['manifest_references_missing']}")
213
+ for r in report["reasons"]:
214
+ print(f" - {r}")
215
+ if not report["passed"]:
216
+ print(" remediation:")
217
+ for r in report["remediation"]:
218
+ print(f" * {r}")
219
+
220
+
221
+ def walk_all_skills(root: Path) -> list[Path]:
222
+ """Find all skill directories under packages/skills/skills/.
223
+ Skips manifest.json, _shared/, and any dir without SKILL.md."""
224
+ base = root if (root / "SKILL.md").exists() else (root / "packages" / "skills" / "skills")
225
+ if not base.is_dir():
226
+ return []
227
+ skills = []
228
+ for entry in sorted(base.iterdir()):
229
+ if not entry.is_dir():
230
+ continue
231
+ if entry.name.startswith("_") or entry.name.startswith("."):
232
+ continue
233
+ if (entry / "SKILL.md").exists():
234
+ skills.append(entry)
235
+ return skills
236
+
237
+
238
+ def main() -> int:
239
+ parser = argparse.ArgumentParser(
240
+ description="CLEO Skill Depth Check (T9684) — progressive-disclosure-depth"
241
+ )
242
+ parser.add_argument("skill_dir", help="Path to the skill directory (or repo root if --all)")
243
+ parser.add_argument("--manifest", help="Path to manifest.json (auto-located if omitted)")
244
+ parser.add_argument(
245
+ "--all", action="store_true",
246
+ help="Walk every skill under packages/skills/skills/",
247
+ )
248
+ parser.add_argument("--json", action="store_true", help="Output JSON instead of text")
249
+ args = parser.parse_args()
250
+
251
+ arg_path = Path(args.skill_dir).resolve()
252
+ manifest = Path(args.manifest).resolve() if args.manifest else None
253
+
254
+ if args.all:
255
+ skills = walk_all_skills(arg_path)
256
+ if not skills:
257
+ print(f"Error: no skill directories found under {arg_path}", file=sys.stderr)
258
+ return 1
259
+ all_reports = []
260
+ total_fail = 0
261
+ for s in skills:
262
+ passed, report = check_depth(s, manifest)
263
+ all_reports.append(report)
264
+ if not passed:
265
+ total_fail += 1
266
+ if args.json:
267
+ print(json.dumps({
268
+ "summary": {
269
+ "total": len(all_reports),
270
+ "passed": len(all_reports) - total_fail,
271
+ "failed": total_fail,
272
+ "thresholds": {
273
+ "min_body_lines": MIN_BODY_LINES,
274
+ "min_ref_files": MIN_REF_FILES,
275
+ },
276
+ },
277
+ "skills": all_reports,
278
+ }, indent=2))
279
+ else:
280
+ print(f"=== CLEO Skill Depth Check (all skills under {arg_path}) ===")
281
+ for r in all_reports:
282
+ _print_report(r)
283
+ print(f"\n=== SUMMARY ===")
284
+ print(f"Total skills: {len(all_reports)}")
285
+ print(f"Passed: {len(all_reports) - total_fail}")
286
+ print(f"Failed: {total_fail}")
287
+ return 1 if total_fail > 0 else 0
288
+
289
+ # Single-skill mode
290
+ if not arg_path.is_dir():
291
+ print(f"Error: '{args.skill_dir}' is not a directory", file=sys.stderr)
292
+ return 1
293
+ if not (arg_path / "SKILL.md").exists():
294
+ print(f"Error: '{args.skill_dir}' has no SKILL.md", file=sys.stderr)
295
+ return 1
296
+
297
+ passed, report = check_depth(arg_path, manifest)
298
+ if args.json:
299
+ print(json.dumps(report, indent=2))
300
+ else:
301
+ _print_report(report)
302
+ return 0 if passed else 1
303
+
304
+
305
+ if __name__ == "__main__":
306
+ sys.exit(main())
@@ -6,6 +6,10 @@ tier: 2
6
6
  core: false
7
7
  category: recommended
8
8
  protocol: specification
9
+ loomStage: specification
10
+ adrRefs:
11
+ - ADR-014
12
+ - ADR-023
9
13
  dependencies: []
10
14
  sharedResources:
11
15
  - subagent-protocol-base
@@ -136,9 +140,70 @@ Non-compliant implementations SHOULD {remediation}.
136
140
 
137
141
  ---
138
142
 
143
+ ## Through SDK (preferred)
144
+
145
+ Specifications are first-class docs SSoT records — created via
146
+ `cleo docs add --type spec`, auto-attached to the parent task, and
147
+ addressable by a stable slug. This is the canonical write path; the
148
+ legacy "write to `docs/specs/<NAME>.md` and commit" pattern is
149
+ deprecated below.
150
+
151
+ ### Write the spec attached to its parent task
152
+
153
+ ```bash
154
+ cleo docs add T1234 docs/specs/auth-protocol.md \
155
+ --type spec \
156
+ --slug auth-protocol-v2 \
157
+ --desc "Auth protocol v2 — RFC 2119 requirements"
158
+ ```
159
+
160
+ - `--type spec` is the canonical taxonomy value for a specification.
161
+ Other allowed values: `adr | research | handoff | note | llm-readme`.
162
+ - `--slug` is the kebab-case retrieval handle. Use the spec topic +
163
+ version (e.g. `auth-protocol-v2`, `release-pipeline-v3`). The CLI
164
+ returns `E_SLUG_TAKEN` with 3 alternatives on collision — pick one
165
+ rather than silently overwriting.
166
+ - The owner ID (`T1234`) auto-attaches the spec to its parent task so
167
+ downstream stages (`ct-validator`, decomposition, implementation)
168
+ can discover the spec via `cleo docs list --task T1234 --type spec`.
169
+
170
+ ### Publish the spec to a git-tracked path (when the spec must ship on disk)
171
+
172
+ ```bash
173
+ cleo docs publish --for T1234 --to docs/specs/auth-protocol.md
174
+ ```
175
+
176
+ Atomic tmp-then-rename. The published file lands in the next commit;
177
+ the SSoT blob remains canonical and continues to track future versions.
178
+
179
+ ### Fetch the spec back by slug
180
+
181
+ ```bash
182
+ cleo docs fetch auth-protocol-v2 # latest version
183
+ cleo docs versions --for T1234 # every SHA version
184
+ ```
185
+
186
+ ### Discover sibling specs in this project
187
+
188
+ ```bash
189
+ cleo docs list --type spec --project # every spec in the project
190
+ cleo docs list --task T1234 --type spec # specs attached to T1234
191
+ ```
192
+
193
+ ## Deprecated: Direct filesystem write
194
+
195
+ The legacy "write to `docs/specs/{{SPEC_NAME}}.md` and commit" pattern
196
+ is deprecated. The on-disk file drifts from the SSoT, the spec has no
197
+ slug for downstream skills to retrieve it by, and the task↔spec
198
+ linkage exists only as a path convention. Migrate to
199
+ `cleo docs add --type spec --slug <name>` for every new spec — and use
200
+ `cleo docs sync --from docs/specs/<name>.md --for <taskId>` to
201
+ back-fill existing on-disk specs into the SSoT.
202
+
139
203
  ## Output Location
140
204
 
141
- Specifications go in: `docs/specs/{{SPEC_NAME}}.md`
205
+ Spec blobs live in the docs SSoT; published copies on disk go in
206
+ `docs/specs/{{SPEC_NAME}}.md`.
142
207
 
143
208
  ---
144
209
 
@@ -187,3 +252,23 @@ Specifications go in: `docs/specs/{{SPEC_NAME}}.md`
187
252
  - [ ] Manifest entry appended
188
253
  - [ ] Task completed via `{{TASK_COMPLETE_CMD}}`
189
254
  - [ ] Return summary message only
255
+
256
+ ---
257
+
258
+ ## See also / References
259
+
260
+ This skill binds to the **specification** LOOM lifecycle stage. Governing ADRs:
261
+
262
+ - [ADR-014 — RCASD rename and protocol validation](../../../../.cleo/adrs/ADR-014-rcasd-rename-and-protocol-validation.md) — defines the specification stage's role inside the RCASD-IVTR+C lifecycle.
263
+ - [ADR-023 — protocol validation dispatch](../../../../.cleo/adrs/ADR-023-protocol-validation-dispatch.md) — defines how specifications are validated before decomposition.
264
+
265
+ LOOM coverage matrix: [docs/skills/loom-coverage-matrix.md](../../../../docs/skills/loom-coverage-matrix.md).
266
+
267
+ ## See references/
268
+
269
+ Progressive disclosure — load on demand only:
270
+
271
+ - `references/rfc2119-language.md` — keyword semantics, positive/negative examples, decision rubric
272
+ - `references/spec-templates.md` — protocol, API, architecture, requirements scaffolds + naming + versioning
273
+ - `references/traceability-matrix.md` — three-way trace from source to REQ to test, drift detection
274
+ - `references/anti-patterns.md` — ten failure modes seen in past spec drafts