@cleocode/skills 2026.5.83 → 2026.5.84
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 +1 -1
- package/skills/_shared/__tests__/lifecycle-protocol-reconcile.test.ts +112 -0
- package/skills/_shared/__tests__/loom-adr-links.test.ts +163 -0
- package/skills/_shared/__tests__/loom-stage-coverage.test.ts +167 -0
- package/skills/ct-adr-recorder/SKILL.md +18 -0
- package/skills/ct-consensus-voter/SKILL.md +14 -0
- package/skills/ct-contribution/SKILL.md +80 -0
- package/skills/ct-epic-architect/SKILL.md +15 -0
- package/skills/ct-ivt-looper/SKILL.md +32 -0
- package/skills/ct-release-orchestrator/SKILL.md +16 -0
- package/skills/ct-research-agent/SKILL.md +15 -0
- package/skills/ct-spec-writer/SKILL.md +15 -0
- package/skills/ct-task-executor/SKILL.md +15 -0
- package/skills/ct-validator/SKILL.md +35 -0
- package/skills/manifest.json +36 -8
package/package.json
CHANGED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lifecycle-protocol reconcile gate (T9672).
|
|
3
|
+
*
|
|
4
|
+
* Asserts the SET equality:
|
|
5
|
+
*
|
|
6
|
+
* SET( cleo lifecycle stages )
|
|
7
|
+
* ==
|
|
8
|
+
* SET( manifest.dispatch_matrix.by_protocol keys )
|
|
9
|
+
* − { "artifact-publish", "provenance", "agent-protocol" } # cross-cutting
|
|
10
|
+
*
|
|
11
|
+
* No normalization, no dashed-alias allowance. This is the strict gate that
|
|
12
|
+
* lands together with the manifest rename `architecture-decision` →
|
|
13
|
+
* `architecture_decision` so that a future drift fails CI.
|
|
14
|
+
*
|
|
15
|
+
* @task T9672
|
|
16
|
+
* @epic T9568
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { readFileSync } from 'node:fs';
|
|
20
|
+
import { dirname, resolve } from 'node:path';
|
|
21
|
+
import { fileURLToPath } from 'node:url';
|
|
22
|
+
import { describe, expect, it } from 'vitest';
|
|
23
|
+
|
|
24
|
+
const thisDir = dirname(fileURLToPath(import.meta.url));
|
|
25
|
+
const manifestPath = resolve(thisDir, '../../manifest.json');
|
|
26
|
+
|
|
27
|
+
interface Manifest {
|
|
28
|
+
dispatch_matrix: {
|
|
29
|
+
by_protocol: Record<string, string>;
|
|
30
|
+
by_keyword: Record<string, string>;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const manifest: Manifest = JSON.parse(readFileSync(manifestPath, 'utf-8'));
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* The canonical 10 LOOM lifecycle stages emitted by `cleo lifecycle --help`.
|
|
38
|
+
* Underscored form is authoritative — see `packages/core/src/lifecycle/`.
|
|
39
|
+
*/
|
|
40
|
+
const LIFECYCLE_STAGES = [
|
|
41
|
+
'research',
|
|
42
|
+
'consensus',
|
|
43
|
+
'architecture_decision',
|
|
44
|
+
'specification',
|
|
45
|
+
'decomposition',
|
|
46
|
+
'implementation',
|
|
47
|
+
'validation',
|
|
48
|
+
'testing',
|
|
49
|
+
'release',
|
|
50
|
+
'contribution',
|
|
51
|
+
] as const;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Cross-cutting protocols that live in `dispatch_matrix.by_protocol` but are
|
|
55
|
+
* not LOOM lifecycle stages.
|
|
56
|
+
*/
|
|
57
|
+
const CROSS_CUTTING_PROTOCOLS = new Set([
|
|
58
|
+
'artifact-publish',
|
|
59
|
+
'provenance',
|
|
60
|
+
'agent-protocol',
|
|
61
|
+
]);
|
|
62
|
+
|
|
63
|
+
describe('lifecycle ↔ protocol reconcile (T9672)', () => {
|
|
64
|
+
const byProtocolKeys = new Set(Object.keys(manifest.dispatch_matrix.by_protocol));
|
|
65
|
+
const stageOnlyKeys = new Set(
|
|
66
|
+
[...byProtocolKeys].filter((k) => !CROSS_CUTTING_PROTOCOLS.has(k)),
|
|
67
|
+
);
|
|
68
|
+
const expected = new Set<string>(LIFECYCLE_STAGES);
|
|
69
|
+
|
|
70
|
+
it('every cleo lifecycle stage is present as a strict (underscored) key in dispatch_matrix.by_protocol', () => {
|
|
71
|
+
for (const stage of LIFECYCLE_STAGES) {
|
|
72
|
+
expect(
|
|
73
|
+
byProtocolKeys.has(stage),
|
|
74
|
+
`lifecycle stage "${stage}" missing from dispatch_matrix.by_protocol — keys: ${[...byProtocolKeys].sort().join(', ')}`,
|
|
75
|
+
).toBe(true);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('dispatch_matrix.by_protocol contains no surplus stage-like keys', () => {
|
|
80
|
+
for (const key of stageOnlyKeys) {
|
|
81
|
+
expect(
|
|
82
|
+
expected.has(key),
|
|
83
|
+
`dispatch_matrix.by_protocol has key "${key}" which is not a cleo lifecycle stage and not a known cross-cutting protocol`,
|
|
84
|
+
).toBe(true);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('the dashed legacy form "architecture-decision" is NOT a dispatch_matrix.by_protocol key', () => {
|
|
89
|
+
expect(byProtocolKeys.has('architecture-decision')).toBe(false);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('the dashed legacy form "architecture-decision" IS preserved as a keyword alias', () => {
|
|
93
|
+
const keywordKeys = Object.keys(manifest.dispatch_matrix.by_keyword);
|
|
94
|
+
const adrLine = keywordKeys.find((k) => k.includes('adr') && k.includes('formalize'));
|
|
95
|
+
expect(
|
|
96
|
+
adrLine,
|
|
97
|
+
`expected the ct-adr-recorder keyword dispatch line to exist; keys: ${keywordKeys.join(', ')}`,
|
|
98
|
+
).toBeDefined();
|
|
99
|
+
expect(
|
|
100
|
+
(adrLine ?? '').includes('architecture-decision'),
|
|
101
|
+
`architecture-decision keyword alias must be retained in ${adrLine}`,
|
|
102
|
+
).toBe(true);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('the underscored canonical form "architecture_decision" maps to ct-adr-recorder', () => {
|
|
106
|
+
expect(manifest.dispatch_matrix.by_protocol.architecture_decision).toBe('ct-adr-recorder');
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('the by_protocol stage-only key set is exactly equal to the lifecycle stage set', () => {
|
|
110
|
+
expect([...stageOnlyKeys].sort()).toEqual([...expected].sort());
|
|
111
|
+
});
|
|
112
|
+
});
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ADR-link gate for LOOM-stage skills (T9665).
|
|
3
|
+
*
|
|
4
|
+
* Enforces two invariants on every canonical LOOM-stage skill in
|
|
5
|
+
* `packages/skills/skills/manifest.json`:
|
|
6
|
+
*
|
|
7
|
+
* 1. The skill entry declares a non-empty `adrRefs[]` array of ADR IDs.
|
|
8
|
+
* 2. Every ADR id in the array resolves to a real file under `.cleo/adrs/`.
|
|
9
|
+
*
|
|
10
|
+
* The mapping of (stage -> required adrRefs minimum) is the authoritative
|
|
11
|
+
* source-of-truth defined in `docs/skills/loom-coverage-matrix.md` under
|
|
12
|
+
* the "ADR Bindings Section". When that doc updates, this test updates.
|
|
13
|
+
*
|
|
14
|
+
* @task T9665
|
|
15
|
+
* @epic T9568
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
19
|
+
import { dirname, resolve } from 'node:path';
|
|
20
|
+
import { fileURLToPath } from 'node:url';
|
|
21
|
+
import { describe, expect, it } from 'vitest';
|
|
22
|
+
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// Setup
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
const thisDir = dirname(fileURLToPath(import.meta.url));
|
|
28
|
+
const manifestPath = resolve(thisDir, '../../manifest.json');
|
|
29
|
+
const adrDir = resolve(thisDir, '../../../../../.cleo/adrs');
|
|
30
|
+
|
|
31
|
+
interface SkillEntry {
|
|
32
|
+
name: string;
|
|
33
|
+
loomStage?: string;
|
|
34
|
+
adrRefs?: string[];
|
|
35
|
+
[key: string]: unknown;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface Manifest {
|
|
39
|
+
skills: SkillEntry[];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const manifest: Manifest = JSON.parse(readFileSync(manifestPath, 'utf-8'));
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* The canonical 10 LOOM lifecycle stages — underscored form is authoritative.
|
|
46
|
+
* Must match `cleo lifecycle --help` output.
|
|
47
|
+
*/
|
|
48
|
+
const CANONICAL_LOOM_STAGES = [
|
|
49
|
+
'research',
|
|
50
|
+
'consensus',
|
|
51
|
+
'architecture_decision',
|
|
52
|
+
'specification',
|
|
53
|
+
'decomposition',
|
|
54
|
+
'implementation',
|
|
55
|
+
'validation',
|
|
56
|
+
'testing',
|
|
57
|
+
'release',
|
|
58
|
+
'contribution',
|
|
59
|
+
] as const;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Required ADR-id minimums per stage. The matrix doc may extend each entry;
|
|
63
|
+
* this gate only enforces the floor.
|
|
64
|
+
*/
|
|
65
|
+
const REQUIRED_ADR_REFS: Record<(typeof CANONICAL_LOOM_STAGES)[number], string[]> = {
|
|
66
|
+
research: ['ADR-023', 'ADR-070'],
|
|
67
|
+
consensus: ['ADR-015', 'ADR-023'],
|
|
68
|
+
architecture_decision: ['ADR-053', 'ADR-070'],
|
|
69
|
+
specification: ['ADR-014', 'ADR-023'],
|
|
70
|
+
decomposition: ['ADR-066', 'ADR-073'],
|
|
71
|
+
implementation: ['ADR-070', 'ADR-062'],
|
|
72
|
+
validation: ['ADR-051', 'ADR-023'],
|
|
73
|
+
testing: ['ADR-051', 'ADR-061'],
|
|
74
|
+
release: ['ADR-053', 'ADR-063', 'ADR-065'],
|
|
75
|
+
contribution: ['ADR-015', 'ADR-053'],
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Build a set of ADR-id prefixes from .cleo/adrs/. Each filename starts
|
|
80
|
+
* with `ADR-NNN-...`; we extract the prefix before the second dash.
|
|
81
|
+
*/
|
|
82
|
+
function loadAdrIdSet(): Set<string> {
|
|
83
|
+
const files = readdirSync(adrDir);
|
|
84
|
+
const ids = new Set<string>();
|
|
85
|
+
for (const file of files) {
|
|
86
|
+
const match = file.match(/^(ADR-\d{3})-/);
|
|
87
|
+
if (match) ids.add(match[1]);
|
|
88
|
+
}
|
|
89
|
+
return ids;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const adrIds = loadAdrIdSet();
|
|
93
|
+
const skillsByStage = new Map<string, SkillEntry>();
|
|
94
|
+
for (const s of manifest.skills) {
|
|
95
|
+
if (typeof s.loomStage === 'string') {
|
|
96
|
+
skillsByStage.set(s.loomStage, s);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// ---------------------------------------------------------------------------
|
|
101
|
+
// Gate 1: every LOOM-stage skill declares a non-empty adrRefs[]
|
|
102
|
+
// ---------------------------------------------------------------------------
|
|
103
|
+
|
|
104
|
+
describe('LOOM ADR links — adrRefs[] declared on every LOOM-stage skill', () => {
|
|
105
|
+
for (const stage of CANONICAL_LOOM_STAGES) {
|
|
106
|
+
it(`stage "${stage}" skill carries a non-empty adrRefs[]`, () => {
|
|
107
|
+
const skill = skillsByStage.get(stage);
|
|
108
|
+
expect(skill, `no skill found with loomStage="${stage}"`).toBeDefined();
|
|
109
|
+
expect(
|
|
110
|
+
Array.isArray(skill?.adrRefs),
|
|
111
|
+
`skill ${skill?.name} adrRefs is not an array`,
|
|
112
|
+
).toBe(true);
|
|
113
|
+
expect(
|
|
114
|
+
(skill?.adrRefs ?? []).length,
|
|
115
|
+
`skill ${skill?.name} adrRefs is empty`,
|
|
116
|
+
).toBeGreaterThan(0);
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
// ---------------------------------------------------------------------------
|
|
122
|
+
// Gate 2: every adrRef resolves to a real file under .cleo/adrs/
|
|
123
|
+
// ---------------------------------------------------------------------------
|
|
124
|
+
|
|
125
|
+
describe('LOOM ADR links — every adrRefs entry resolves to .cleo/adrs/<id>-*.md', () => {
|
|
126
|
+
it('.cleo/adrs/ exists and was loaded', () => {
|
|
127
|
+
expect(existsSync(adrDir), `.cleo/adrs/ not found at ${adrDir}`).toBe(true);
|
|
128
|
+
expect(adrIds.size).toBeGreaterThan(0);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
for (const stage of CANONICAL_LOOM_STAGES) {
|
|
132
|
+
const skill = skillsByStage.get(stage);
|
|
133
|
+
const refs = skill?.adrRefs ?? [];
|
|
134
|
+
for (const ref of refs) {
|
|
135
|
+
it(`stage "${stage}" skill ${skill?.name} references real ADR file "${ref}"`, () => {
|
|
136
|
+
expect(
|
|
137
|
+
adrIds.has(ref),
|
|
138
|
+
`${ref} not found under .cleo/adrs/ — known prefixes: ${[...adrIds].sort().join(', ')}`,
|
|
139
|
+
).toBe(true);
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// ---------------------------------------------------------------------------
|
|
146
|
+
// Gate 3: required ADR floor per stage is met
|
|
147
|
+
// ---------------------------------------------------------------------------
|
|
148
|
+
|
|
149
|
+
describe('LOOM ADR links — required ADR floor met per stage', () => {
|
|
150
|
+
for (const stage of CANONICAL_LOOM_STAGES) {
|
|
151
|
+
const required = REQUIRED_ADR_REFS[stage];
|
|
152
|
+
const skill = skillsByStage.get(stage);
|
|
153
|
+
const refs = new Set(skill?.adrRefs ?? []);
|
|
154
|
+
for (const requiredAdr of required) {
|
|
155
|
+
it(`stage "${stage}" includes required ADR "${requiredAdr}" in adrRefs`, () => {
|
|
156
|
+
expect(
|
|
157
|
+
refs.has(requiredAdr),
|
|
158
|
+
`skill ${skill?.name} for stage ${stage} missing required ADR ${requiredAdr} (has: ${[...refs].join(', ')})`,
|
|
159
|
+
).toBe(true);
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
});
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LOOM-stage coverage gate (T9664).
|
|
3
|
+
*
|
|
4
|
+
* Enforces that every canonical LOOM lifecycle stage emitted by `cleo lifecycle`
|
|
5
|
+
* has a bound skill in `packages/skills/skills/manifest.json` and that the
|
|
6
|
+
* skill's entry declares a `loomStage` field equal to the lifecycle stage name
|
|
7
|
+
* in underscored canonical form (the `cleo lifecycle` source-of-truth form).
|
|
8
|
+
*
|
|
9
|
+
* Why this exists:
|
|
10
|
+
* - The lifecycle CLI is the runtime source of truth for stage names.
|
|
11
|
+
* - The manifest's `dispatch_matrix.by_protocol` is the dispatch routing table.
|
|
12
|
+
* - Historically those two surfaces drifted (T9568 audit found
|
|
13
|
+
* `architecture-decision` dashed in manifest vs `architecture_decision`
|
|
14
|
+
* underscored in lifecycle CLI).
|
|
15
|
+
* - This test pins the contract so future drift fails CI instead of silently
|
|
16
|
+
* reaching agents at spawn time.
|
|
17
|
+
*
|
|
18
|
+
* @task T9664
|
|
19
|
+
* @epic T9568
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { readFileSync } from 'node:fs';
|
|
23
|
+
import { dirname, resolve } from 'node:path';
|
|
24
|
+
import { fileURLToPath } from 'node:url';
|
|
25
|
+
import { describe, expect, it } from 'vitest';
|
|
26
|
+
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
// Setup
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
|
|
31
|
+
const thisDir = dirname(fileURLToPath(import.meta.url));
|
|
32
|
+
const manifestPath = resolve(thisDir, '../../manifest.json');
|
|
33
|
+
|
|
34
|
+
interface SkillEntry {
|
|
35
|
+
name: string;
|
|
36
|
+
protocol?: string;
|
|
37
|
+
loomStage?: string;
|
|
38
|
+
status?: string;
|
|
39
|
+
// intentionally permissive — other fields ignored for this gate
|
|
40
|
+
[key: string]: unknown;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
interface Manifest {
|
|
44
|
+
dispatch_matrix: {
|
|
45
|
+
by_protocol: Record<string, string>;
|
|
46
|
+
};
|
|
47
|
+
skills: SkillEntry[];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const manifest: Manifest = JSON.parse(readFileSync(manifestPath, 'utf-8'));
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* The canonical 10 LOOM lifecycle stages emitted by `cleo lifecycle` (see
|
|
54
|
+
* `packages/core/src/lifecycle/`). Underscored form is authoritative.
|
|
55
|
+
* Update this constant ONLY when the lifecycle CLI itself adds or removes
|
|
56
|
+
* a stage — never to silence a drift failure.
|
|
57
|
+
*/
|
|
58
|
+
const CANONICAL_LOOM_STAGES = [
|
|
59
|
+
'research',
|
|
60
|
+
'consensus',
|
|
61
|
+
'architecture_decision',
|
|
62
|
+
'specification',
|
|
63
|
+
'decomposition',
|
|
64
|
+
'implementation',
|
|
65
|
+
'validation',
|
|
66
|
+
'testing',
|
|
67
|
+
'release',
|
|
68
|
+
'contribution',
|
|
69
|
+
] as const;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Cross-cutting protocols that live in `dispatch_matrix.by_protocol` but are
|
|
73
|
+
* NOT LOOM lifecycle stages. They route by capability, not by lifecycle
|
|
74
|
+
* position, and are excluded from the 10-stage gate.
|
|
75
|
+
*/
|
|
76
|
+
const CROSS_CUTTING_PROTOCOLS = new Set([
|
|
77
|
+
'artifact-publish',
|
|
78
|
+
'provenance',
|
|
79
|
+
'agent-protocol',
|
|
80
|
+
]);
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Skill-name lookup keyed by the `name` field.
|
|
84
|
+
*/
|
|
85
|
+
const skillByName = new Map(manifest.skills.map((s) => [s.name, s]));
|
|
86
|
+
|
|
87
|
+
// ---------------------------------------------------------------------------
|
|
88
|
+
// Gate 1: every canonical stage has a binding in dispatch_matrix.by_protocol
|
|
89
|
+
// ---------------------------------------------------------------------------
|
|
90
|
+
|
|
91
|
+
describe('LOOM stage coverage — dispatch_matrix.by_protocol', () => {
|
|
92
|
+
for (const stage of CANONICAL_LOOM_STAGES) {
|
|
93
|
+
it(`stage "${stage}" is bound to a skill in dispatch_matrix.by_protocol`, () => {
|
|
94
|
+
// We accept either the underscored form (canonical) or the dashed
|
|
95
|
+
// legacy alias (architecture-decision) until T9672 reconciles. After
|
|
96
|
+
// T9672, only the underscored key is required.
|
|
97
|
+
const dashed = stage.replace(/_/g, '-');
|
|
98
|
+
const skillName =
|
|
99
|
+
manifest.dispatch_matrix.by_protocol[stage] ??
|
|
100
|
+
manifest.dispatch_matrix.by_protocol[dashed];
|
|
101
|
+
expect(
|
|
102
|
+
skillName,
|
|
103
|
+
`LOOM stage "${stage}" has no skill binding in manifest.dispatch_matrix.by_protocol (checked both "${stage}" and "${dashed}")`,
|
|
104
|
+
).toBeTruthy();
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// ---------------------------------------------------------------------------
|
|
110
|
+
// Gate 2: every protocol-bound skill carries a matching loomStage frontmatter
|
|
111
|
+
// ---------------------------------------------------------------------------
|
|
112
|
+
|
|
113
|
+
describe('LOOM stage coverage — loomStage field on bound skills', () => {
|
|
114
|
+
for (const stage of CANONICAL_LOOM_STAGES) {
|
|
115
|
+
it(`bound skill for "${stage}" declares loomStage === "${stage}"`, () => {
|
|
116
|
+
const dashed = stage.replace(/_/g, '-');
|
|
117
|
+
const skillName =
|
|
118
|
+
manifest.dispatch_matrix.by_protocol[stage] ??
|
|
119
|
+
manifest.dispatch_matrix.by_protocol[dashed];
|
|
120
|
+
const skill = skillName ? skillByName.get(skillName) : undefined;
|
|
121
|
+
expect(skill, `skill "${skillName}" not found in manifest.skills[]`).toBeDefined();
|
|
122
|
+
expect(
|
|
123
|
+
skill?.loomStage,
|
|
124
|
+
`skill "${skillName}" missing loomStage field; expected "${stage}"`,
|
|
125
|
+
).toBe(stage);
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// ---------------------------------------------------------------------------
|
|
131
|
+
// Gate 3: every skill that has loomStage uses a canonical (underscored) value
|
|
132
|
+
// ---------------------------------------------------------------------------
|
|
133
|
+
|
|
134
|
+
describe('LOOM stage coverage — loomStage values are canonical', () => {
|
|
135
|
+
const stagesSet = new Set<string>(CANONICAL_LOOM_STAGES);
|
|
136
|
+
const skillsWithLoomStage = manifest.skills.filter((s) => typeof s.loomStage === 'string');
|
|
137
|
+
|
|
138
|
+
it('at least 10 skills carry a loomStage field (one per LOOM stage)', () => {
|
|
139
|
+
expect(skillsWithLoomStage.length).toBeGreaterThanOrEqual(CANONICAL_LOOM_STAGES.length);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
for (const skill of skillsWithLoomStage) {
|
|
143
|
+
it(`skill "${skill.name}" loomStage value "${skill.loomStage}" is a canonical LOOM stage`, () => {
|
|
144
|
+
expect(stagesSet.has(skill.loomStage as string)).toBe(true);
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// ---------------------------------------------------------------------------
|
|
150
|
+
// Gate 4: dispatch_matrix.by_protocol keys minus cross-cutting == LOOM stages
|
|
151
|
+
// (the union of the 10 lifecycle stages — checked allowing dashed alias for
|
|
152
|
+
// architecture_decision until T9672 lands the reconcile)
|
|
153
|
+
// ---------------------------------------------------------------------------
|
|
154
|
+
|
|
155
|
+
describe('LOOM stage coverage — dispatch_matrix.by_protocol key set', () => {
|
|
156
|
+
it('by_protocol keys minus cross-cutting protocols cover every canonical LOOM stage', () => {
|
|
157
|
+
const keys = new Set(Object.keys(manifest.dispatch_matrix.by_protocol));
|
|
158
|
+
const stageKeys = [...keys].filter((k) => !CROSS_CUTTING_PROTOCOLS.has(k));
|
|
159
|
+
const normalized = new Set(stageKeys.map((k) => k.replace(/-/g, '_')));
|
|
160
|
+
for (const stage of CANONICAL_LOOM_STAGES) {
|
|
161
|
+
expect(
|
|
162
|
+
normalized.has(stage),
|
|
163
|
+
`LOOM stage "${stage}" not represented in dispatch_matrix.by_protocol (normalized keys: ${[...normalized].join(', ')})`,
|
|
164
|
+
).toBe(true);
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
});
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: ct-adr-recorder
|
|
3
3
|
description: "Records Architecture Decision Records from accepted consensus verdicts. Use when promoting a consensus outcome to a formal ADR: drafts the document in the proposed-then-accepted HITL lifecycle, links to the originating consensus manifest, persists the decision to the canonical SQLite decisions table, and triggers downstream invalidation when an accepted ADR is later superseded. Triggers on phrases like 'write ADR', 'record architecture decision', 'formalize this decision', 'lock in the choice', 'create ADR-XXX', or when a consensus task reaches completed status and needs formalization."
|
|
4
|
+
protocol: architecture_decision
|
|
5
|
+
loomStage: architecture_decision
|
|
6
|
+
adrRefs:
|
|
7
|
+
- ADR-053
|
|
8
|
+
- ADR-070
|
|
4
9
|
---
|
|
5
10
|
|
|
6
11
|
# ADR Recorder
|
|
@@ -173,3 +178,16 @@ Exit code 0 = valid. Exit code 65 = `HANDOFF_REQUIRED`. Exit code 18 = `CASCADE_
|
|
|
173
178
|
6. Superseding an accepted ADR MUST trigger the downstream cascade over linked specs, decomps, and impls.
|
|
174
179
|
7. Agents MUST NOT retry the HITL handoff on a loop; wait for the human reviewer.
|
|
175
180
|
8. Always validate via `cleo check protocol --protocolType architecture-decision` before exiting.
|
|
181
|
+
|
|
182
|
+
## See also / References
|
|
183
|
+
|
|
184
|
+
This skill binds to the **architecture_decision** LOOM lifecycle stage (underscored canonical form — `cleo lifecycle` source of truth). Governing ADRs:
|
|
185
|
+
|
|
186
|
+
- [ADR-053 — playbook runtime](../../../../.cleo/adrs/ADR-053-playbook-runtime.md) — defines the lifecycle state machine; the ADR stage is one of its 10 nodes.
|
|
187
|
+
- [ADR-070 — three-tier orchestration](../../../../.cleo/adrs/ADR-070-three-tier-orchestration.md) — defines the Orchestrator HITL gate that owns the `proposed → accepted` ADR transition.
|
|
188
|
+
|
|
189
|
+
### Naming Note (T9672)
|
|
190
|
+
|
|
191
|
+
The stage's canonical name is **`architecture_decision`** (underscored) because the `cleo lifecycle` CLI emits it that way. Historically `packages/skills/skills/manifest.json` `dispatch_matrix.by_protocol` used the dashed form `architecture-decision` — T9672 reconciled that key to the underscored form. The dashed form is retained only as a keyword alias under `dispatch_matrix.by_keyword` so legacy dispatch paths continue to resolve. Frontmatter on this skill (`protocol: architecture_decision`, `loomStage: architecture_decision`) uses the underscored form.
|
|
192
|
+
|
|
193
|
+
LOOM coverage matrix: [docs/skills/loom-coverage-matrix.md](../../../../docs/skills/loom-coverage-matrix.md).
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: ct-consensus-voter
|
|
3
3
|
description: "Runs structured multi-agent voting for decision tasks with confidence scores, conflict detection, and HITL escalation when the threshold is not met. Use when two or more agents must vote on options: architecture choices, tool selection, policy decisions, when a task carries agent_type:analysis, or on phrases like 'reach consensus', 'vote on options', 'resolve the debate', 'pick the best approach'. Produces a voting matrix JSON, enforces the 0.5 threshold, flags ties within 0.1 confidence as contested and escalates to human tiebreak."
|
|
4
|
+
protocol: consensus
|
|
5
|
+
loomStage: consensus
|
|
6
|
+
adrRefs:
|
|
7
|
+
- ADR-015
|
|
8
|
+
- ADR-023
|
|
4
9
|
---
|
|
5
10
|
|
|
6
11
|
# Consensus Voter
|
|
@@ -156,3 +161,12 @@ This skill typically hands off to ct-adr-recorder on a `PROVEN` verdict so the d
|
|
|
156
161
|
6. Manifest entry MUST set `agent_type: "analysis"` and include the verdict.
|
|
157
162
|
7. On PROVEN, hand off to ct-adr-recorder; on CONTESTED or INSUFFICIENT_EVIDENCE, hand off to HITL.
|
|
158
163
|
8. Always validate via `cleo check protocol --protocolType consensus`.
|
|
164
|
+
|
|
165
|
+
## See also / References
|
|
166
|
+
|
|
167
|
+
This skill binds to the **consensus** LOOM lifecycle stage. Governing ADRs:
|
|
168
|
+
|
|
169
|
+
- [ADR-015 — multi-contributor architecture](../../../../.cleo/adrs/ADR-015-multi-contributor-architecture.md) — defines the consensus framework that this skill implements.
|
|
170
|
+
- [ADR-023 — protocol validation dispatch](../../../../.cleo/adrs/ADR-023-protocol-validation-dispatch.md) — defines how consensus output is validated before downstream stages consume it.
|
|
171
|
+
|
|
172
|
+
LOOM coverage matrix: [docs/skills/loom-coverage-matrix.md](../../../../docs/skills/loom-coverage-matrix.md).
|
|
@@ -11,6 +11,10 @@ tier: 3
|
|
|
11
11
|
core: false
|
|
12
12
|
category: meta
|
|
13
13
|
protocol: contribution
|
|
14
|
+
loomStage: contribution
|
|
15
|
+
adrRefs:
|
|
16
|
+
- ADR-015
|
|
17
|
+
- ADR-053
|
|
14
18
|
dependencies: []
|
|
15
19
|
sharedResources:
|
|
16
20
|
- subagent-protocol-base
|
|
@@ -519,3 +523,79 @@ jq -s '[.[] | select(.epicId == "T2204")] | .[0]' .cleo/contributions/CONTRIBUTI
|
|
|
519
523
|
| [contribution.schema.json](../../schemas/contribution.schema.json) | **Authoritative** for JSON Schema |
|
|
520
524
|
| [CONTRIBUTION-PROTOCOL-GUIDE.md](../../docs/guides/CONTRIBUTION-PROTOCOL-GUIDE.md) | Usage guide with examples |
|
|
521
525
|
| [CONSENSUS-FRAMEWORK-SPEC.md](../../docs/specs/CONSENSUS-FRAMEWORK-SPEC.md) | Consensus voting thresholds |
|
|
526
|
+
|
|
527
|
+
---
|
|
528
|
+
|
|
529
|
+
## LOOM Stage Binding (T9670)
|
|
530
|
+
|
|
531
|
+
`ct-contribution` is bound to LOOM lifecycle stage **`contribution`** — the terminal node of the RCASD-IVTR+C pipeline. Use this skill to formalize an Epic's contribution back to canon after its work has converged.
|
|
532
|
+
|
|
533
|
+
### Stage-Transition Contract
|
|
534
|
+
|
|
535
|
+
The contribution stage is entered from one of two upstream stages depending on the Epic's `kind`:
|
|
536
|
+
|
|
537
|
+
| Upstream stage | Epic kind | Entry condition |
|
|
538
|
+
|---|---|---|
|
|
539
|
+
| **`release`** | most epics (work, bug, experiment) | Release tag pushed; release manifest recorded |
|
|
540
|
+
| **`testing`** | epics whose `kind` is `release` or that gate on IVTR | IVT loop converged; `ivtLoopConverged: true` recorded |
|
|
541
|
+
| **`specification`** | spec-only epics (no code) | Specification accepted; HITL signoff recorded |
|
|
542
|
+
|
|
543
|
+
```
|
|
544
|
+
research → consensus → architecture_decision → specification → decomposition
|
|
545
|
+
↓
|
|
546
|
+
implementation
|
|
547
|
+
↓
|
|
548
|
+
validation
|
|
549
|
+
↓
|
|
550
|
+
testing ← (some epics return here)
|
|
551
|
+
↓
|
|
552
|
+
release
|
|
553
|
+
↓
|
|
554
|
+
contribution ← (this skill)
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
The transition is enforced by the playbook runtime defined in **ADR-053**. The runtime is a deterministic state machine; `contribution` is its terminal accepting state. Once entered, the Epic is closed in canon.
|
|
558
|
+
|
|
559
|
+
### Acceptance-Gate Evidence
|
|
560
|
+
|
|
561
|
+
The contribution stage's completion gate is satisfied by emitting **at least one** of the following ADR-051 evidence atoms, recorded via `cleo verify <epicId> --gate contribution --evidence "<atoms>"`:
|
|
562
|
+
|
|
563
|
+
| Atom kind | Format | Meaning |
|
|
564
|
+
|---|---|---|
|
|
565
|
+
| `decision:` | `decision:D-<slug>` | A BRAIN decision id that records the contribution outcome. |
|
|
566
|
+
| `files:` | `files:path/a.md,path/b.md` | A list of contribution-format JSON / markdown deliverables produced by `/contribution submit`. |
|
|
567
|
+
| `note:` | `note:<freeform>` | Owner-attested closure rationale; preferred when the contribution is non-textual (e.g. a tag push referenced by SHA in the note). |
|
|
568
|
+
|
|
569
|
+
Example:
|
|
570
|
+
|
|
571
|
+
```bash
|
|
572
|
+
cleo verify T9568 --gate contribution \
|
|
573
|
+
--evidence "decision:D-loom-coverage-001;files:.cleo/contributions/T9568-final.json"
|
|
574
|
+
cleo complete T9568
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
The gate validator (ADR-051 §2.4) rejects an empty evidence string with `E_EVIDENCE_MISSING`. Stale evidence (modified files after `verify` but before `complete`) fails with `E_EVIDENCE_STALE`.
|
|
578
|
+
|
|
579
|
+
### Open Follow-Up
|
|
580
|
+
|
|
581
|
+
A future ADR dedicated to the contribution stage's lifecycle gates (covering automated rollup signals from `cleo saga rollup`, multi-Epic contribution aggregation, and the contribution → "saga close" promotion path) is on the roadmap. File via:
|
|
582
|
+
|
|
583
|
+
```bash
|
|
584
|
+
cleo add --kind work --type task --severity P2 \
|
|
585
|
+
--title "T-LOOM-GAP-ADR-CONTRIBUTION: dedicated ADR for contribution stage gates" \
|
|
586
|
+
--relates T9670 \
|
|
587
|
+
--acceptance "ADR drafted under .cleo/adrs/|Cross-referenced from ct-contribution SKILL.md|Validator gate updated"
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
Until that ADR lands, contribution gates derive from ADR-015 (multi-contributor architecture) and ADR-053 (playbook runtime) — both already referenced in this skill's `adrRefs`.
|
|
591
|
+
|
|
592
|
+
---
|
|
593
|
+
|
|
594
|
+
## See also / References
|
|
595
|
+
|
|
596
|
+
This skill binds to the **contribution** LOOM lifecycle stage (the final stage of the RCASD-IVTR+C pipeline). Governing ADRs:
|
|
597
|
+
|
|
598
|
+
- [ADR-015 — multi-contributor architecture](../../../../.cleo/adrs/ADR-015-multi-contributor-architecture.md) — defines the multi-contributor consensus mechanics that this stage formalizes for an Epic's downstream return path.
|
|
599
|
+
- [ADR-053 — playbook runtime](../../../../.cleo/adrs/ADR-053-playbook-runtime.md) — defines the lifecycle state machine; contribution is its terminal node.
|
|
600
|
+
|
|
601
|
+
LOOM coverage matrix: [docs/skills/loom-coverage-matrix.md](../../../../docs/skills/loom-coverage-matrix.md).
|
|
@@ -6,6 +6,10 @@ tier: 1
|
|
|
6
6
|
core: false
|
|
7
7
|
category: recommended
|
|
8
8
|
protocol: decomposition
|
|
9
|
+
loomStage: decomposition
|
|
10
|
+
adrRefs:
|
|
11
|
+
- ADR-066
|
|
12
|
+
- ADR-073
|
|
9
13
|
dependencies: []
|
|
10
14
|
sharedResources:
|
|
11
15
|
- subagent-protocol-base
|
|
@@ -329,3 +333,14 @@ Recommendation: [Your recommendation]
|
|
|
329
333
|
| 6 | Validation | Escape `$` as `\$`, check fields |
|
|
330
334
|
|
|
331
335
|
**Shell Escaping**: Always `\$` in `--notes`/`--description`. See [shell-escaping.md](references/shell-escaping.md).
|
|
336
|
+
|
|
337
|
+
---
|
|
338
|
+
|
|
339
|
+
## See also / References
|
|
340
|
+
|
|
341
|
+
This skill binds to the **decomposition** LOOM lifecycle stage. Governing ADRs:
|
|
342
|
+
|
|
343
|
+
- [ADR-066 — task taxonomy consolidation](../../../../.cleo/adrs/ADR-066-task-taxonomy-consolidation.md) — defines the Type/Kind/Severity axes the decomposer must populate on every leaf task.
|
|
344
|
+
- [ADR-073 — above-epic naming](../../../../.cleo/adrs/ADR-073-above-epic-naming.md) — defines the Saga/Epic/Task/Subtask hierarchy that decomposition produces.
|
|
345
|
+
|
|
346
|
+
LOOM coverage matrix: [docs/skills/loom-coverage-matrix.md](../../../../docs/skills/loom-coverage-matrix.md).
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: ct-ivt-looper
|
|
3
3
|
description: "Runs a project-agnostic autonomous Implement-then-Validate-then-Test compliance loop on any git worktree. Detects the project's test framework (vitest, jest, mocha, pytest, unittest, go-test, cargo-test, rspec, phpunit, bats, or other) and iterates until the implementation satisfies its specification, recording convergence metrics to the manifest. Use when given an implementation task that must ship verified: the IVT loop is the autonomous compliance layer enforced before any release or PR. Triggers on phrases like 'implement and verify', 'run the IVT loop', 'ship this task', 'complete implementation with tests', 'verify against spec', or any implementation task with acceptance criteria. Works in any git worktree regardless of language or framework, never hardcoded to one project's tooling."
|
|
4
|
+
protocol: testing
|
|
5
|
+
loomStage: testing
|
|
6
|
+
adrRefs:
|
|
7
|
+
- ADR-051
|
|
8
|
+
- ADR-061
|
|
4
9
|
---
|
|
5
10
|
|
|
6
11
|
# IVT Looper
|
|
@@ -76,6 +81,24 @@ escalate_to_hitl() # IVT-007: exit code 65
|
|
|
76
81
|
|
|
77
82
|
The loop is a *single* stage from the lifecycle's point of view. Implement, Validate, and Test are not three separate tasks — they are three phases of one autonomous run that either converges or escalates.
|
|
78
83
|
|
|
84
|
+
## Out of Scope (T9675)
|
|
85
|
+
|
|
86
|
+
`ct-ivt-looper` operates on the **`testing`** LOOM lifecycle stage (stage 8). It performs the **dynamic** Implement-then-Validate-then-Test loop with framework detection and iterate-until-green convergence semantics.
|
|
87
|
+
|
|
88
|
+
This skill does NOT:
|
|
89
|
+
|
|
90
|
+
- Audit static artifacts for schema/compliance/RFC-2119 keyword usage, ADR-document structure, or JSON Schema conformance. Those belong to **`ct-validator`** at the `validation` stage (stage 7). When the question is "is this document/manifest well-formed?" rather than "does this code converge on its spec?", chain to `ct-validator` rather than expanding scope here.
|
|
91
|
+
- Promote a green loop to release. Release sequencing belongs to **`ct-release-orchestrator`** at stage 9.
|
|
92
|
+
|
|
93
|
+
### Chain handoffs
|
|
94
|
+
|
|
95
|
+
| Direction | When | Handoff |
|
|
96
|
+
|---|---|---|
|
|
97
|
+
| `ct-ivt-looper` → `ct-validator` | Loop converged; need to audit the resulting artifacts (e.g. final manifest, spec back-references) against schema/compliance | Emit the convergence manifest entry, then dispatch the `validation` stage |
|
|
98
|
+
| `ct-validator` → `ct-ivt-looper` | Spec is valid but implementation needs dynamic verification | Receive a dispatch from the `validation` stage; iterate the IVT loop on the worktree |
|
|
99
|
+
|
|
100
|
+
Governance: see **ADR-051** (programmatic gate integrity) which defines the evidence atoms (`tool:test`, `test-run:<json>`) the loop emits and that downstream `cleo verify --gate testsPassed` re-validates, and **ADR-061** (project-agnostic verify tools) which defines the canonical tool-resolution layer.
|
|
101
|
+
|
|
79
102
|
## Framework Detection
|
|
80
103
|
|
|
81
104
|
Framework detection is project-agnostic: the skill walks the worktree, inspects config files, and selects the correct test command. No language or framework is special-cased above another. The full detection table lives in [references/frameworks.md](references/frameworks.md). In summary: detection reads the project manifest (`package.json`, `pyproject.toml`, `Cargo.toml`, `go.mod`, `Gemfile`, `composer.json`, or `.cleo/project-context.json#testing.command`) and selects one of: `vitest`, `jest`, `mocha`, `pytest`, `unittest`, `go-test`, `cargo-test`, `rspec`, `phpunit`, `bats`, `other`.
|
|
@@ -179,3 +202,12 @@ cleo check protocol \
|
|
|
179
202
|
6. Record `framework`, `testsRun`, `testsPassed`, `testsFailed`, `ivtLoopConverged`, `ivtLoopIterations` in the manifest.
|
|
180
203
|
7. On non-convergence, exit 65 and leave the worktree untouched.
|
|
181
204
|
8. Validate every run via `cleo check protocol --protocolType testing`.
|
|
205
|
+
|
|
206
|
+
## See also / References
|
|
207
|
+
|
|
208
|
+
This skill binds to the **testing** LOOM lifecycle stage. Governing ADRs:
|
|
209
|
+
|
|
210
|
+
- [ADR-051 — programmatic gate integrity](../../../../.cleo/adrs/ADR-051-programmatic-gate-integrity.md) — defines the evidence atoms (`tool:test`, `test-run:<json>`) that the IVT loop emits and that downstream `cleo verify --gate testsPassed` re-validates.
|
|
211
|
+
- [ADR-061 — project-agnostic verify tools](../../../../.cleo/adrs/ADR-061-project-agnostic-verify-tools.md) — defines the canonical tool-resolution layer (`test`, `build`, `lint`, `typecheck`) that the loop walks for framework-agnostic execution.
|
|
212
|
+
|
|
213
|
+
LOOM coverage matrix: [docs/skills/loom-coverage-matrix.md](../../../../docs/skills/loom-coverage-matrix.md).
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: ct-release-orchestrator
|
|
3
3
|
description: "Orchestrates the full release pipeline: version bump, then changelog, then commit, then tag, then conditionally forks to artifact-publish and provenance based on release config. Parent protocol that composes ct-artifact-publisher and ct-provenance-keeper as sub-protocols: not every release publishes artifacts (source-only releases skip it), and artifact publishers delegate signing and attestation to provenance. Use when shipping a new version, running cleo release ship, or promoting a completed epic to released status."
|
|
4
|
+
protocol: release
|
|
5
|
+
loomStage: release
|
|
6
|
+
adrRefs:
|
|
7
|
+
- ADR-053
|
|
8
|
+
- ADR-063
|
|
9
|
+
- ADR-065
|
|
4
10
|
---
|
|
5
11
|
|
|
6
12
|
# Release Orchestrator
|
|
@@ -132,3 +138,13 @@ For source-only releases, pass `--no-artifacts` to skip the artifact-publish han
|
|
|
132
138
|
6. `released` entries are immutable; hotfixes go into new entries.
|
|
133
139
|
7. Manifest entry MUST set `agent_type: "documentation"` and record the full chain via `record_release()`.
|
|
134
140
|
8. Always validate via `cleo check protocol --protocolType release` before declaring the release done.
|
|
141
|
+
|
|
142
|
+
## See also / References
|
|
143
|
+
|
|
144
|
+
This skill binds to the **release** LOOM lifecycle stage. Governing ADRs:
|
|
145
|
+
|
|
146
|
+
- [ADR-053 — project-agnostic release pipeline](../../../../.cleo/adrs/ADR-053-project-agnostic-release-pipeline.md) — defines the language-agnostic version bump → changelog → tag flow.
|
|
147
|
+
- [ADR-063 — release pipeline](../../../../.cleo/adrs/ADR-063-release-pipeline.md) — defines the 12-step `cleo release ship` integration with CI.
|
|
148
|
+
- [ADR-065 — PR-required release flow](../../../../.cleo/adrs/ADR-065-pr-required-release-flow.md) — defines the PR-gated path; direct pushes to `main` are prohibited.
|
|
149
|
+
|
|
150
|
+
LOOM coverage matrix: [docs/skills/loom-coverage-matrix.md](../../../../docs/skills/loom-coverage-matrix.md).
|
|
@@ -6,6 +6,10 @@ tier: 2
|
|
|
6
6
|
core: false
|
|
7
7
|
category: recommended
|
|
8
8
|
protocol: research
|
|
9
|
+
loomStage: research
|
|
10
|
+
adrRefs:
|
|
11
|
+
- ADR-023
|
|
12
|
+
- ADR-070
|
|
9
13
|
dependencies: []
|
|
10
14
|
sharedResources:
|
|
11
15
|
- subagent-protocol-base
|
|
@@ -224,3 +228,14 @@ If research cannot proceed (access denied, topic too broad, etc.):
|
|
|
224
228
|
- **Prioritized** - Most important first
|
|
225
229
|
- **Justified** - Tied to specific findings
|
|
226
230
|
- **Feasible** - Achievable within project constraints
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## See also / References
|
|
235
|
+
|
|
236
|
+
This skill binds to the **research** LOOM lifecycle stage. Governing ADRs:
|
|
237
|
+
|
|
238
|
+
- [ADR-023 — protocol validation dispatch](../../../../.cleo/adrs/ADR-023-protocol-validation-dispatch.md) — defines how research output is validated before downstream stages consume it.
|
|
239
|
+
- [ADR-070 — three-tier orchestration](../../../../.cleo/adrs/ADR-070-three-tier-orchestration.md) — defines the Orchestrator → Phase Lead → Worker tiers; research runs as a leaf Worker.
|
|
240
|
+
|
|
241
|
+
LOOM coverage matrix: [docs/skills/loom-coverage-matrix.md](../../../../docs/skills/loom-coverage-matrix.md).
|
|
@@ -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
|
|
@@ -187,3 +191,14 @@ Specifications go in: `docs/specs/{{SPEC_NAME}}.md`
|
|
|
187
191
|
- [ ] Manifest entry appended
|
|
188
192
|
- [ ] Task completed via `{{TASK_COMPLETE_CMD}}`
|
|
189
193
|
- [ ] Return summary message only
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## See also / References
|
|
198
|
+
|
|
199
|
+
This skill binds to the **specification** LOOM lifecycle stage. Governing ADRs:
|
|
200
|
+
|
|
201
|
+
- [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.
|
|
202
|
+
- [ADR-023 — protocol validation dispatch](../../../../.cleo/adrs/ADR-023-protocol-validation-dispatch.md) — defines how specifications are validated before decomposition.
|
|
203
|
+
|
|
204
|
+
LOOM coverage matrix: [docs/skills/loom-coverage-matrix.md](../../../../docs/skills/loom-coverage-matrix.md).
|
|
@@ -6,6 +6,10 @@ tier: 2
|
|
|
6
6
|
core: true
|
|
7
7
|
category: core
|
|
8
8
|
protocol: implementation
|
|
9
|
+
loomStage: implementation
|
|
10
|
+
adrRefs:
|
|
11
|
+
- ADR-070
|
|
12
|
+
- ADR-062
|
|
9
13
|
dependencies: []
|
|
10
14
|
sharedResources:
|
|
11
15
|
- subagent-protocol-base
|
|
@@ -294,3 +298,14 @@ cleo session gc --include-active
|
|
|
294
298
|
| Partial deliverables | Missing outputs | Complete all or report partial |
|
|
295
299
|
| Undocumented changes | Lost context | Write detailed output file |
|
|
296
300
|
| Silent failures | Orchestrator unaware | Report via manifest status |
|
|
301
|
+
|
|
302
|
+
---
|
|
303
|
+
|
|
304
|
+
## See also / References
|
|
305
|
+
|
|
306
|
+
This skill binds to the **implementation** LOOM lifecycle stage. Governing ADRs:
|
|
307
|
+
|
|
308
|
+
- [ADR-070 — three-tier orchestration](../../../../.cleo/adrs/ADR-070-three-tier-orchestration.md) — defines the Worker tier that ct-task-executor occupies.
|
|
309
|
+
- [ADR-062 — worktree merge, not cherry-pick](../../../../.cleo/adrs/ADR-062-worktree-merge-not-cherry-pick.md) — defines the integration path that preserves the executor's commit SHAs end-to-end.
|
|
310
|
+
|
|
311
|
+
LOOM coverage matrix: [docs/skills/loom-coverage-matrix.md](../../../../docs/skills/loom-coverage-matrix.md).
|
|
@@ -6,6 +6,10 @@ tier: 2
|
|
|
6
6
|
core: false
|
|
7
7
|
category: recommended
|
|
8
8
|
protocol: validation
|
|
9
|
+
loomStage: validation
|
|
10
|
+
adrRefs:
|
|
11
|
+
- ADR-051
|
|
12
|
+
- ADR-023
|
|
9
13
|
dependencies: []
|
|
10
14
|
sharedResources:
|
|
11
15
|
- subagent-protocol-base
|
|
@@ -41,6 +45,26 @@ Context injection for compliance validation tasks spawned via cleo-subagent. Pro
|
|
|
41
45
|
|
|
42
46
|
---
|
|
43
47
|
|
|
48
|
+
## Out of Scope (T9675)
|
|
49
|
+
|
|
50
|
+
`ct-validator` operates on the **`validation`** LOOM lifecycle stage (stage 7). It performs **static** schema, compliance, and audit checks against artifacts that already exist on disk (specs, ADRs, JSON files, RFC 2119 keyword usage, manifest schemas).
|
|
51
|
+
|
|
52
|
+
This skill does NOT:
|
|
53
|
+
|
|
54
|
+
- Run a test suite, framework detection, or iterative IVT loop. Those belong to **`ct-ivt-looper`** at the `testing` stage (stage 8). When dynamic verification is required — e.g. "does the implementation actually pass its tests?" — chain to `ct-ivt-looper` rather than expanding scope here.
|
|
55
|
+
- Modify code or apply fixes. The validator reports; downstream skills remediate.
|
|
56
|
+
|
|
57
|
+
### Chain handoffs
|
|
58
|
+
|
|
59
|
+
| Direction | When | Handoff |
|
|
60
|
+
|---|---|---|
|
|
61
|
+
| `ct-validator` → `ct-ivt-looper` | Spec is valid but implementation needs dynamic verification | Emit a manifest entry, then dispatch the `testing` stage |
|
|
62
|
+
| `ct-ivt-looper` → `ct-validator` | IVT loop converged; need to audit the resulting artifacts against schema/compliance | Dispatch the `validation` stage after the test convergence record |
|
|
63
|
+
|
|
64
|
+
Governance: see **ADR-051** (programmatic gate integrity) which defines the evidence atoms each stage emits and that the other stage may re-validate, and **ADR-023** (protocol validation dispatch) which routes between them.
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
44
68
|
## Validation Methodology
|
|
45
69
|
|
|
46
70
|
### Standard Workflow
|
|
@@ -214,3 +238,14 @@ When invoked by orchestrator, expect these context tokens:
|
|
|
214
238
|
| Vague findings | Unclear remediation | Specific issue + file/line + fix |
|
|
215
239
|
| Missing severity | Can't prioritize | Always classify: critical/warning/suggestion |
|
|
216
240
|
| No remediation | Findings not actionable | Always provide fix for FAIL/PARTIAL |
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
## See also / References
|
|
245
|
+
|
|
246
|
+
This skill binds to the **validation** LOOM lifecycle stage. Governing ADRs:
|
|
247
|
+
|
|
248
|
+
- [ADR-051 — programmatic gate integrity](../../../../.cleo/adrs/ADR-051-programmatic-gate-integrity.md) — defines the evidence-atom grammar that the validator emits and re-validates.
|
|
249
|
+
- [ADR-023 — protocol validation dispatch](../../../../.cleo/adrs/ADR-023-protocol-validation-dispatch.md) — defines the protocol-validation routing layer that dispatches to this skill.
|
|
250
|
+
|
|
251
|
+
LOOM coverage matrix: [docs/skills/loom-coverage-matrix.md](../../../../docs/skills/loom-coverage-matrix.md).
|
package/skills/manifest.json
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://cleo-dev.com/schemas/v1/skills-manifest.schema.json",
|
|
3
3
|
"_meta": {
|
|
4
|
-
"schemaVersion": "2.
|
|
5
|
-
"lastUpdated": "2026-
|
|
4
|
+
"schemaVersion": "2.7.0",
|
|
5
|
+
"lastUpdated": "2026-05-19",
|
|
6
6
|
"totalSkills": 22,
|
|
7
|
-
"generatedFrom": "T260 — lifecycle pipeline rework: dedicated skills for ADR, IVT loop, consensus, release, artifact-publish, provenance",
|
|
8
|
-
"architectureNote": "Universal Subagent Architecture: All spawns use provider-neutral delegation with skill/protocol injection. Pipeline stages and cross-cutting protocols each have a dedicated skill — no overloading."
|
|
7
|
+
"generatedFrom": "T260 — lifecycle pipeline rework: dedicated skills for ADR, IVT loop, consensus, release, artifact-publish, provenance. T9664/T9665/T9672 (epic T9568) — adds loomStage + adrRefs fields on every LOOM-stage skill entry (underscored canonical form matching `cleo lifecycle` stage names; ADR refs point at .cleo/adrs/). T9672 reconciled dispatch_matrix.by_protocol key 'architecture-decision' → 'architecture_decision' matching the lifecycle CLI source of truth; the dashed form is retained as a keyword alias.",
|
|
8
|
+
"architectureNote": "Universal Subagent Architecture: All spawns use provider-neutral delegation with skill/protocol injection. Pipeline stages and cross-cutting protocols each have a dedicated skill — no overloading.",
|
|
9
|
+
"loomStageContract": "Each of the 10 LOOM lifecycle stages (research, consensus, architecture_decision, specification, decomposition, implementation, validation, testing, release, contribution) MUST have a bound skill whose manifest entry declares loomStage matching the lifecycle CLI stage name. The legacy `protocol` field is retained as an alias; dispatch_matrix.by_protocol is the authoritative routing table. See docs/skills/loom-coverage-matrix.md.",
|
|
10
|
+
"adrRefsContract": "Each LOOM-stage skill MUST declare an adrRefs[] array naming the ADR file(s) that govern the stage. Every referenced ADR-NNN MUST resolve to a file under .cleo/adrs/. The Vitest gate at packages/skills/skills/_shared/__tests__/loom-adr-links.test.ts enforces both conditions."
|
|
9
11
|
},
|
|
10
12
|
"dispatch_matrix": {
|
|
11
13
|
"_comment": "Maps task types/keywords to skill NAMES. Provider adapter decides HOW to execute.",
|
|
@@ -33,7 +35,7 @@
|
|
|
33
35
|
"spec|rfc|protocol|contract": "ct-spec-writer",
|
|
34
36
|
"validate|verify|audit|compliance": "ct-validator",
|
|
35
37
|
"consensus|vote|verdict|resolve the debate": "ct-consensus-voter",
|
|
36
|
-
"adr|architecture decision|formalize|lock in the choice": "ct-adr-recorder",
|
|
38
|
+
"adr|architecture decision|architecture-decision|formalize|lock in the choice": "ct-adr-recorder",
|
|
37
39
|
"release|version|ship|changelog|cut release": "ct-release-orchestrator",
|
|
38
40
|
"artifact|publish|registry|npm publish|docker push": "ct-artifact-publisher",
|
|
39
41
|
"provenance|attestation|sbom|sigstore|slsa": "ct-provenance-keeper"
|
|
@@ -41,7 +43,7 @@
|
|
|
41
43
|
"by_protocol": {
|
|
42
44
|
"research": "ct-research-agent",
|
|
43
45
|
"consensus": "ct-consensus-voter",
|
|
44
|
-
"
|
|
46
|
+
"architecture_decision": "ct-adr-recorder",
|
|
45
47
|
"specification": "ct-spec-writer",
|
|
46
48
|
"decomposition": "ct-epic-architect",
|
|
47
49
|
"implementation": "ct-task-executor",
|
|
@@ -120,6 +122,9 @@
|
|
|
120
122
|
"status": "active",
|
|
121
123
|
"tier": 0,
|
|
122
124
|
"token_budget": 8000,
|
|
125
|
+
"protocol": "implementation",
|
|
126
|
+
"loomStage": "implementation",
|
|
127
|
+
"adrRefs": ["ADR-070", "ADR-062"],
|
|
123
128
|
"references": [],
|
|
124
129
|
"capabilities": {
|
|
125
130
|
"inputs": ["TASK_ID", "TASK_NAME", "TASK_INSTRUCTIONS", "DELIVERABLES_LIST", "ACCEPTANCE_CRITERIA"],
|
|
@@ -148,6 +153,9 @@
|
|
|
148
153
|
"status": "active",
|
|
149
154
|
"tier": 1,
|
|
150
155
|
"token_budget": 8000,
|
|
156
|
+
"protocol": "decomposition",
|
|
157
|
+
"loomStage": "decomposition",
|
|
158
|
+
"adrRefs": ["ADR-066", "ADR-073"],
|
|
151
159
|
"references": ["skills/ct-epic-architect/references/bug-epic-example.md", "skills/ct-epic-architect/references/commands.md", "skills/ct-epic-architect/references/feature-epic-example.md"],
|
|
152
160
|
"capabilities": {
|
|
153
161
|
"inputs": ["TASK_ID", "FEATURE_NAME", "EPIC_ID", "SESSION_ID"],
|
|
@@ -176,6 +184,9 @@
|
|
|
176
184
|
"status": "active",
|
|
177
185
|
"tier": 1,
|
|
178
186
|
"token_budget": 8000,
|
|
187
|
+
"protocol": "research",
|
|
188
|
+
"loomStage": "research",
|
|
189
|
+
"adrRefs": ["ADR-023", "ADR-070"],
|
|
179
190
|
"references": [],
|
|
180
191
|
"capabilities": {
|
|
181
192
|
"inputs": ["TASK_ID", "TOPIC", "RESEARCH_QUESTIONS"],
|
|
@@ -204,6 +215,9 @@
|
|
|
204
215
|
"status": "active",
|
|
205
216
|
"tier": 1,
|
|
206
217
|
"token_budget": 8000,
|
|
218
|
+
"protocol": "specification",
|
|
219
|
+
"loomStage": "specification",
|
|
220
|
+
"adrRefs": ["ADR-014", "ADR-023"],
|
|
207
221
|
"references": [],
|
|
208
222
|
"capabilities": {
|
|
209
223
|
"inputs": ["TASK_ID", "SPEC_NAME", "spec_topic"],
|
|
@@ -232,6 +246,9 @@
|
|
|
232
246
|
"status": "active",
|
|
233
247
|
"tier": 1,
|
|
234
248
|
"token_budget": 6000,
|
|
249
|
+
"protocol": "validation",
|
|
250
|
+
"loomStage": "validation",
|
|
251
|
+
"adrRefs": ["ADR-051", "ADR-023"],
|
|
235
252
|
"references": [],
|
|
236
253
|
"capabilities": {
|
|
237
254
|
"inputs": ["TASK_ID", "VALIDATION_TARGET", "VALIDATION_CRITERIA"],
|
|
@@ -239,7 +256,7 @@
|
|
|
239
256
|
"dependencies": [],
|
|
240
257
|
"dispatch_triggers": ["validate", "verify", "check compliance", "audit"],
|
|
241
258
|
"compatible_subagent_types": ["general-purpose"],
|
|
242
|
-
"chains_to": [],
|
|
259
|
+
"chains_to": ["ct-ivt-looper"],
|
|
243
260
|
"dispatch_keywords": {
|
|
244
261
|
"primary": ["validate", "verify", "audit", "compliance"],
|
|
245
262
|
"secondary": ["check", "conformance", "standards", "requirements"]
|
|
@@ -400,6 +417,9 @@
|
|
|
400
417
|
"status": "active",
|
|
401
418
|
"tier": 2,
|
|
402
419
|
"token_budget": 6000,
|
|
420
|
+
"protocol": "contribution",
|
|
421
|
+
"loomStage": "contribution",
|
|
422
|
+
"adrRefs": ["ADR-015", "ADR-053"],
|
|
403
423
|
"references": [],
|
|
404
424
|
"capabilities": {
|
|
405
425
|
"inputs": ["TASK_ID", "contribution_type", "context"],
|
|
@@ -484,7 +504,9 @@
|
|
|
484
504
|
"status": "active",
|
|
485
505
|
"tier": 2,
|
|
486
506
|
"token_budget": 8000,
|
|
487
|
-
"protocol": "
|
|
507
|
+
"protocol": "architecture_decision",
|
|
508
|
+
"loomStage": "architecture_decision",
|
|
509
|
+
"adrRefs": ["ADR-053", "ADR-070"],
|
|
488
510
|
"references": [
|
|
489
511
|
"skills/ct-adr-recorder/references/cascade.md",
|
|
490
512
|
"skills/ct-adr-recorder/references/examples.md"
|
|
@@ -523,6 +545,8 @@
|
|
|
523
545
|
"tier": 2,
|
|
524
546
|
"token_budget": 10000,
|
|
525
547
|
"protocol": "testing",
|
|
548
|
+
"loomStage": "testing",
|
|
549
|
+
"adrRefs": ["ADR-051", "ADR-061"],
|
|
526
550
|
"references": [
|
|
527
551
|
"skills/ct-ivt-looper/references/escalation.md",
|
|
528
552
|
"skills/ct-ivt-looper/references/frameworks.md",
|
|
@@ -562,6 +586,8 @@
|
|
|
562
586
|
"tier": 2,
|
|
563
587
|
"token_budget": 6000,
|
|
564
588
|
"protocol": "consensus",
|
|
589
|
+
"loomStage": "consensus",
|
|
590
|
+
"adrRefs": ["ADR-015", "ADR-023"],
|
|
565
591
|
"references": ["skills/ct-consensus-voter/references/matrix-examples.md"],
|
|
566
592
|
"capabilities": {
|
|
567
593
|
"inputs": ["task-id", "question", "candidate-options"],
|
|
@@ -597,6 +623,8 @@
|
|
|
597
623
|
"tier": 2,
|
|
598
624
|
"token_budget": 8000,
|
|
599
625
|
"protocol": "release",
|
|
626
|
+
"loomStage": "release",
|
|
627
|
+
"adrRefs": ["ADR-053", "ADR-063", "ADR-065"],
|
|
600
628
|
"references": [
|
|
601
629
|
"skills/ct-release-orchestrator/references/composition.md",
|
|
602
630
|
"skills/ct-release-orchestrator/references/release-types.md"
|