@cleocode/core 2026.4.5 → 2026.4.6

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 (144) hide show
  1. package/dist/discovery.d.ts +69 -0
  2. package/dist/discovery.d.ts.map +1 -0
  3. package/dist/index.d.ts +3 -2
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +1643 -2349
  6. package/dist/index.js.map +4 -4
  7. package/dist/init.d.ts +51 -0
  8. package/dist/init.d.ts.map +1 -1
  9. package/dist/internal.d.ts +9 -1
  10. package/dist/internal.d.ts.map +1 -1
  11. package/dist/lifecycle/default-chain.d.ts +8 -2
  12. package/dist/lifecycle/default-chain.d.ts.map +1 -1
  13. package/dist/lifecycle/index.d.ts +1 -0
  14. package/dist/lifecycle/index.d.ts.map +1 -1
  15. package/dist/lifecycle/stage-guidance.d.ts +140 -0
  16. package/dist/lifecycle/stage-guidance.d.ts.map +1 -0
  17. package/dist/orchestration/protocol-validators.d.ts +122 -3
  18. package/dist/orchestration/protocol-validators.d.ts.map +1 -1
  19. package/dist/paths.d.ts +91 -0
  20. package/dist/paths.d.ts.map +1 -1
  21. package/dist/scaffold.d.ts +31 -1
  22. package/dist/scaffold.d.ts.map +1 -1
  23. package/dist/skills/dispatch.d.ts +1 -1
  24. package/dist/skills/skill-paths.d.ts +9 -6
  25. package/dist/skills/skill-paths.d.ts.map +1 -1
  26. package/dist/validation/protocols/_shared.d.ts +40 -0
  27. package/dist/validation/protocols/_shared.d.ts.map +1 -0
  28. package/dist/validation/protocols/architecture-decision.d.ts +23 -0
  29. package/dist/validation/protocols/architecture-decision.d.ts.map +1 -0
  30. package/dist/validation/protocols/artifact-publish.d.ts +22 -0
  31. package/dist/validation/protocols/artifact-publish.d.ts.map +1 -0
  32. package/dist/validation/protocols/consensus.d.ts +11 -17
  33. package/dist/validation/protocols/consensus.d.ts.map +1 -1
  34. package/dist/validation/protocols/contribution.d.ts +12 -17
  35. package/dist/validation/protocols/contribution.d.ts.map +1 -1
  36. package/dist/validation/protocols/decomposition.d.ts +18 -21
  37. package/dist/validation/protocols/decomposition.d.ts.map +1 -1
  38. package/dist/validation/protocols/implementation.d.ts +9 -17
  39. package/dist/validation/protocols/implementation.d.ts.map +1 -1
  40. package/dist/validation/protocols/provenance.d.ts +23 -0
  41. package/dist/validation/protocols/provenance.d.ts.map +1 -0
  42. package/dist/validation/protocols/release.d.ts +25 -0
  43. package/dist/validation/protocols/release.d.ts.map +1 -0
  44. package/dist/validation/protocols/research.d.ts +9 -17
  45. package/dist/validation/protocols/research.d.ts.map +1 -1
  46. package/dist/validation/protocols/specification.d.ts +7 -17
  47. package/dist/validation/protocols/specification.d.ts.map +1 -1
  48. package/dist/validation/protocols/testing.d.ts +22 -0
  49. package/dist/validation/protocols/testing.d.ts.map +1 -0
  50. package/dist/validation/protocols/validation.d.ts +22 -0
  51. package/dist/validation/protocols/validation.d.ts.map +1 -0
  52. package/package.json +7 -7
  53. package/src/__tests__/injection-mvi-tiers.test.js +54 -90
  54. package/src/__tests__/injection-mvi-tiers.test.js.map +1 -1
  55. package/src/discovery.ts +235 -0
  56. package/src/hooks/handlers/__tests__/hook-automation-e2e.test.js +3 -1
  57. package/src/hooks/handlers/__tests__/hook-automation-e2e.test.js.map +1 -1
  58. package/src/index.ts +16 -0
  59. package/src/init.ts +196 -0
  60. package/src/internal.ts +31 -1
  61. package/src/lifecycle/default-chain.ts +11 -2
  62. package/src/lifecycle/index.ts +10 -0
  63. package/src/lifecycle/stage-guidance.ts +282 -0
  64. package/src/metrics/__tests__/provider-detection.test.js +19 -7
  65. package/src/metrics/__tests__/provider-detection.test.js.map +1 -1
  66. package/src/orchestration/__tests__/protocol-validators.test.js +228 -8
  67. package/src/orchestration/__tests__/protocol-validators.test.js.map +1 -1
  68. package/src/orchestration/__tests__/protocol-validators.test.ts +259 -7
  69. package/src/orchestration/protocol-validators.ts +419 -4
  70. package/src/paths.ts +110 -0
  71. package/src/scaffold.ts +240 -4
  72. package/src/skills/dispatch.ts +6 -6
  73. package/src/skills/skill-paths.ts +27 -23
  74. package/src/validation/protocols/_shared.ts +88 -0
  75. package/src/validation/protocols/architecture-decision.ts +52 -0
  76. package/src/validation/protocols/artifact-publish.ts +49 -0
  77. package/src/validation/protocols/consensus.ts +44 -74
  78. package/src/validation/protocols/contribution.ts +28 -65
  79. package/src/validation/protocols/decomposition.ts +37 -64
  80. package/src/validation/protocols/implementation.ts +25 -65
  81. package/src/validation/protocols/protocols-markdown/architecture-decision.md +303 -0
  82. package/src/validation/protocols/protocols-markdown/artifact-publish.md +600 -0
  83. package/src/validation/protocols/protocols-markdown/consensus.md +322 -0
  84. package/src/validation/protocols/protocols-markdown/contribution.md +388 -0
  85. package/src/validation/protocols/protocols-markdown/decomposition.md +421 -0
  86. package/src/validation/protocols/protocols-markdown/implementation.md +357 -0
  87. package/src/validation/protocols/protocols-markdown/provenance.md +613 -0
  88. package/src/validation/protocols/protocols-markdown/release.md +783 -0
  89. package/src/validation/protocols/protocols-markdown/research.md +261 -0
  90. package/src/validation/protocols/protocols-markdown/specification.md +300 -0
  91. package/src/validation/protocols/protocols-markdown/testing.md +287 -0
  92. package/src/validation/protocols/protocols-markdown/validation.md +242 -0
  93. package/src/validation/protocols/provenance.ts +50 -0
  94. package/src/validation/protocols/release.ts +44 -0
  95. package/src/validation/protocols/research.ts +25 -87
  96. package/src/validation/protocols/specification.ts +27 -89
  97. package/src/validation/protocols/testing.ts +46 -0
  98. package/src/validation/protocols/validation.ts +46 -0
  99. package/dist/validation/protocols/release-protocol.d.ts +0 -27
  100. package/dist/validation/protocols/release-protocol.d.ts.map +0 -1
  101. package/dist/validation/protocols/testing-protocol.d.ts +0 -27
  102. package/dist/validation/protocols/testing-protocol.d.ts.map +0 -1
  103. package/dist/validation/protocols/validation-protocol.d.ts +0 -27
  104. package/dist/validation/protocols/validation-protocol.d.ts.map +0 -1
  105. package/schemas/agent-configs.schema.json +0 -120
  106. package/schemas/agent-registry.schema.json +0 -132
  107. package/schemas/archive.schema.json +0 -450
  108. package/schemas/brain-decision.schema.json +0 -69
  109. package/schemas/brain-learning.schema.json +0 -57
  110. package/schemas/brain-pattern.schema.json +0 -72
  111. package/schemas/critical-path.schema.json +0 -246
  112. package/schemas/deps-cache.schema.json +0 -97
  113. package/schemas/doctor-output.schema.json +0 -283
  114. package/schemas/error.schema.json +0 -161
  115. package/schemas/global-config.schema.json +0 -219
  116. package/schemas/grade.schema.json +0 -49
  117. package/schemas/log.schema.json +0 -250
  118. package/schemas/metrics.schema.json +0 -328
  119. package/schemas/migrations.schema.json +0 -150
  120. package/schemas/nexus-registry.schema.json +0 -90
  121. package/schemas/operation-constitution.schema.json +0 -438
  122. package/schemas/output.schema.json +0 -164
  123. package/schemas/projects-registry.schema.json +0 -107
  124. package/schemas/protocol-frontmatter.schema.json +0 -72
  125. package/schemas/rcasd-consensus-report.schema.json +0 -10
  126. package/schemas/rcasd-evidence.schema.json +0 -42
  127. package/schemas/rcasd-gate-result.schema.json +0 -46
  128. package/schemas/rcasd-hitl-resolution.schema.json +0 -10
  129. package/schemas/rcasd-index.schema.json +0 -10
  130. package/schemas/rcasd-manifest.schema.json +0 -10
  131. package/schemas/rcasd-research-output.schema.json +0 -10
  132. package/schemas/rcasd-spec-frontmatter.schema.json +0 -10
  133. package/schemas/rcasd-stage-transition.schema.json +0 -38
  134. package/schemas/releases.schema.json +0 -267
  135. package/schemas/skills-manifest.schema.json +0 -91
  136. package/schemas/spec-index.schema.json +0 -196
  137. package/schemas/system-flow-atlas.schema.json +0 -125
  138. package/src/conduit/__tests__/dual-api-e2e.test.d.ts.map +0 -1
  139. package/src/conduit/__tests__/dual-api-e2e.test.js +0 -178
  140. package/src/conduit/__tests__/dual-api-e2e.test.js.map +0 -1
  141. package/src/conduit/__tests__/dual-api-e2e.test.ts +0 -212
  142. package/src/validation/protocols/release-protocol.ts +0 -80
  143. package/src/validation/protocols/testing-protocol.ts +0 -93
  144. package/src/validation/protocols/validation-protocol.ts +0 -93
@@ -0,0 +1,282 @@
1
+ /**
2
+ * Stage-Aware LLM Prompt Guidance (Phase 2, refactored Phase 4).
3
+ *
4
+ * **Architecture (SSoT)**: The protocol text for each RCASD-IVTR+C stage is
5
+ * authoritatively encoded in the `@cleocode/skills` package at
6
+ * `packages/skills/skills/ct-*` — specifically:
7
+ *
8
+ * - Tier 0 (always loaded): `ct-cleo`, `ct-orchestrator`, `ct-task-executor`
9
+ * - Stage-specific (per RCASD-IVTR+C stage): see `STAGE_SKILL_MAP` below
10
+ *
11
+ * This module is a **thin loader** — it does NOT embed protocol text. It
12
+ * resolves stage → skill names via `STAGE_SKILL_MAP`, then calls the
13
+ * existing `prepareSpawnMulti()` helper (from `../skills/dispatch.ts`) to
14
+ * compose the full prompt from real `SKILL.md` files.
15
+ *
16
+ * If the skill files are unavailable at runtime (e.g. fresh install, skills
17
+ * package missing), we fall back to a minimal structured description built
18
+ * from `STAGE_DEFINITIONS` alone — never hand-authored prose.
19
+ *
20
+ * @task Phase 2 — Pi harness registration + stage guidance injection
21
+ * @task Phase 4 — SSoT alignment: pull from skill files, not inline text
22
+ */
23
+
24
+ import { findSkill } from '../skills/discovery.js';
25
+ import { prepareSpawnMulti } from '../skills/dispatch.js';
26
+ import { STAGE_DEFINITIONS, type Stage } from './stages.js';
27
+
28
+ // ============================================================================
29
+ // SSoT: Stage → Skill mapping
30
+ // ============================================================================
31
+
32
+ /**
33
+ * Canonical mapping from RCASD-IVTR+C pipeline stages to the primary skill
34
+ * that encodes the protocol for that stage.
35
+ *
36
+ * Each pipeline stage maps to exactly one **dedicated** skill that owns
37
+ * the protocol contract for that stage. The keys here are the canonical
38
+ * `Stage` union from `./stages.ts`; the values are skill names that MUST
39
+ * exist under `packages/skills/skills/` and be registered in the global
40
+ * manifest at `packages/skills/skills/manifest.json`.
41
+ *
42
+ * **Relationship to manifest.json:** This table is the runtime authority
43
+ * for stage→skill resolution by `buildStageGuidance()`. The manifest's
44
+ * `dispatch_matrix.by_protocol` table is the broader catalog (it also
45
+ * covers cross-cutting protocols like contribution, artifact-publish, and
46
+ * provenance which are NOT pipeline stages and therefore are not in this
47
+ * map). When the two tables overlap, they MUST agree on the skill name.
48
+ *
49
+ * **Cross-cutting protocols** (`contribution`, `artifact-publish`,
50
+ * `provenance`) compose with pipeline stages but are not themselves
51
+ * stages — see `lifecycle/default-chain.ts#DEFAULT_PROTOCOL_STAGE_MAP`
52
+ * for the cross-cutting → host-stage mapping.
53
+ *
54
+ * @task T260 — replace overloaded ct-validator/ct-dev-workflow assignments
55
+ * with dedicated skills (ct-adr-recorder, ct-consensus-voter,
56
+ * ct-ivt-looper, ct-release-orchestrator). Validation stays on
57
+ * ct-validator now that consensus duties are split out.
58
+ */
59
+ export const STAGE_SKILL_MAP: Record<Stage, string> = {
60
+ research: 'ct-research-agent',
61
+ consensus: 'ct-consensus-voter',
62
+ architecture_decision: 'ct-adr-recorder',
63
+ specification: 'ct-spec-writer',
64
+ decomposition: 'ct-epic-architect',
65
+ implementation: 'ct-task-executor',
66
+ validation: 'ct-validator',
67
+ testing: 'ct-ivt-looper',
68
+ release: 'ct-release-orchestrator',
69
+ };
70
+
71
+ /**
72
+ * Tier 0 skills — always loaded alongside the stage-specific skill.
73
+ *
74
+ * - `ct-cleo`: CLEO task management protocol (session, task, memory CLI)
75
+ * - `ct-orchestrator`: pipeline-aware orchestration constraints (ORC-001..009)
76
+ *
77
+ * `ct-task-executor` is NOT unconditionally included because most stages
78
+ * already resolve to a more-specific executor skill; it's only added when
79
+ * no stage-specific skill exists.
80
+ */
81
+ export const TIER_0_SKILLS: readonly string[] = ['ct-cleo', 'ct-orchestrator'];
82
+
83
+ // ============================================================================
84
+ // Public types
85
+ // ============================================================================
86
+
87
+ /**
88
+ * Structured guidance for a single pipeline stage.
89
+ *
90
+ * Pi extensions consume the `.prompt` field directly (render via
91
+ * `{ systemPrompt }` in `before_agent_start`). The structured fields are
92
+ * exposed for TUI widgets, badges, and logging.
93
+ */
94
+ export interface StageGuidance {
95
+ /** The canonical stage identifier. */
96
+ stage: Stage;
97
+ /** Human-readable stage name (e.g. "Research"). */
98
+ name: string;
99
+ /** Numeric stage order (1-9). */
100
+ order: number;
101
+ /** Primary skill loaded for this stage (SSoT from STAGE_SKILL_MAP). */
102
+ primarySkill: string;
103
+ /** All skills loaded (Tier 0 + stage primary). */
104
+ loadedSkills: readonly string[];
105
+ /** Gates that must pass before the stage can be completed. */
106
+ requiredGates: readonly string[];
107
+ /** Artifacts the agent is expected to produce before advancing. */
108
+ expectedArtifacts: readonly string[];
109
+ /** Composed prompt text, ready for LLM system prompt injection. */
110
+ prompt: string;
111
+ /** Source of the prompt: 'skills' (real SKILL.md files) or 'fallback'. */
112
+ source: 'skills' | 'fallback';
113
+ }
114
+
115
+ // ============================================================================
116
+ // Fallback prompt builder — used only when skills are unavailable
117
+ // ============================================================================
118
+
119
+ /**
120
+ * Build a minimal stage prompt from canonical metadata when the skill files
121
+ * cannot be resolved. Uses only `STAGE_DEFINITIONS` — no hand-authored prose.
122
+ *
123
+ * This intentionally produces a compact prompt that tells the agent what
124
+ * stage it's in and where to find the authoritative protocol. It is NOT a
125
+ * substitute for the real skill content.
126
+ */
127
+ function buildFallbackPrompt(stage: Stage, primarySkill: string): string {
128
+ const def = STAGE_DEFINITIONS[stage];
129
+ const lines: string[] = [];
130
+ lines.push(`## CLEO Pipeline Stage: ${def.name} (stage ${def.order}/9)`);
131
+ lines.push('');
132
+ lines.push(`**Description**: ${def.description}`);
133
+ lines.push(`**Category**: ${def.category}`);
134
+ lines.push(`**Skippable**: ${def.skippable ? 'yes' : 'no'}`);
135
+ lines.push('');
136
+ lines.push('### Required Gates');
137
+ for (const gate of def.requiredGates) {
138
+ lines.push(`- \`${gate}\``);
139
+ }
140
+ lines.push('');
141
+ lines.push('### Expected Artifacts');
142
+ for (const artifact of def.expectedArtifacts) {
143
+ lines.push(`- \`${artifact}\``);
144
+ }
145
+ lines.push('');
146
+ lines.push('### Primary Skill');
147
+ lines.push(
148
+ `- **${primarySkill}** — protocol unavailable at runtime. Install \`@cleocode/skills\` or run \`cleo admin scaffold-hub\` to provision.`,
149
+ );
150
+ lines.push('');
151
+ lines.push('### Tier 0 Skills (always loaded)');
152
+ for (const s of TIER_0_SKILLS) {
153
+ lines.push(`- ${s}`);
154
+ }
155
+ lines.push('');
156
+ lines.push(
157
+ '> Fallback guidance — the full `SKILL.md` files could not be resolved. ' +
158
+ 'Check skill installation with `cleo skills list`.',
159
+ );
160
+ return lines.join('\n');
161
+ }
162
+
163
+ // ============================================================================
164
+ // Public API
165
+ // ============================================================================
166
+
167
+ /**
168
+ * Build structured stage guidance for a given pipeline stage.
169
+ *
170
+ * Resolves the primary skill for the stage from `STAGE_SKILL_MAP`, composes
171
+ * a prompt from the real `SKILL.md` files via `prepareSpawnMulti()`, and
172
+ * returns a `StageGuidance` object suitable for Pi extension injection or
173
+ * direct CLI rendering.
174
+ *
175
+ * If the skills cannot be resolved, returns a fallback prompt built from
176
+ * `STAGE_DEFINITIONS` metadata only — no hand-authored protocol text.
177
+ *
178
+ * @param stage - The canonical pipeline stage identifier
179
+ * @param cwd - Optional project root override for skill resolution
180
+ * @returns Structured guidance with `.prompt` ready for LLM injection
181
+ *
182
+ * @example
183
+ * ```typescript
184
+ * const guidance = buildStageGuidance('implementation');
185
+ * // guidance.primarySkill === 'ct-task-executor'
186
+ * // guidance.loadedSkills === ['ct-task-executor', 'ct-cleo', 'ct-orchestrator']
187
+ * // guidance.prompt starts with "## Skills Loaded (3 total)"
188
+ * ```
189
+ */
190
+ export function buildStageGuidance(stage: Stage, cwd?: string): StageGuidance {
191
+ const def = STAGE_DEFINITIONS[stage];
192
+ const primarySkill = STAGE_SKILL_MAP[stage];
193
+
194
+ // Primary first, then Tier 0 (de-dup if the primary is itself tier 0)
195
+ const skillNames = [primarySkill, ...TIER_0_SKILLS.filter((s) => s !== primarySkill)];
196
+
197
+ // Verify every skill can actually be found before calling prepareSpawnMulti
198
+ const allFound = skillNames.every((name) => findSkill(name, cwd) !== null);
199
+
200
+ if (!allFound) {
201
+ return {
202
+ stage,
203
+ name: def.name,
204
+ order: def.order,
205
+ primarySkill,
206
+ loadedSkills: skillNames,
207
+ requiredGates: def.requiredGates,
208
+ expectedArtifacts: def.expectedArtifacts,
209
+ prompt: buildFallbackPrompt(stage, primarySkill),
210
+ source: 'fallback',
211
+ };
212
+ }
213
+
214
+ try {
215
+ const composition = prepareSpawnMulti(skillNames, {}, cwd);
216
+ return {
217
+ stage,
218
+ name: def.name,
219
+ order: def.order,
220
+ primarySkill,
221
+ loadedSkills: skillNames,
222
+ requiredGates: def.requiredGates,
223
+ expectedArtifacts: def.expectedArtifacts,
224
+ prompt: composition.prompt,
225
+ source: 'skills',
226
+ };
227
+ } catch {
228
+ return {
229
+ stage,
230
+ name: def.name,
231
+ order: def.order,
232
+ primarySkill,
233
+ loadedSkills: skillNames,
234
+ requiredGates: def.requiredGates,
235
+ expectedArtifacts: def.expectedArtifacts,
236
+ prompt: buildFallbackPrompt(stage, primarySkill),
237
+ source: 'fallback',
238
+ };
239
+ }
240
+ }
241
+
242
+ /**
243
+ * Format stage guidance as a Markdown-wrapped system prompt.
244
+ *
245
+ * Since `buildStageGuidance()` now returns the already-composed prompt in
246
+ * `.prompt`, this helper simply passes it through with a header banner
247
+ * identifying which skills are loaded and which stage is active.
248
+ *
249
+ * @param guidance - The structured guidance from `buildStageGuidance()`
250
+ * @returns Markdown text suitable for LLM system prompt injection
251
+ *
252
+ * @example
253
+ * ```typescript
254
+ * const g = buildStageGuidance('research');
255
+ * const text = formatStageGuidance(g);
256
+ * return { systemPrompt: text }; // Pi before_agent_start hook return
257
+ * ```
258
+ */
259
+ export function formatStageGuidance(guidance: StageGuidance): string {
260
+ const banner = [
261
+ `## CLEO Stage-Aware Injection`,
262
+ `**Stage**: ${guidance.name} (${guidance.order}/9) — primary skill: \`${guidance.primarySkill}\``,
263
+ `**Loaded skills**: ${guidance.loadedSkills.join(', ')}`,
264
+ `**Source**: ${guidance.source}`,
265
+ `**Required gates**: ${guidance.requiredGates.map((g) => `\`${g}\``).join(', ')}`,
266
+ '',
267
+ '---',
268
+ '',
269
+ ].join('\n');
270
+ return banner + guidance.prompt;
271
+ }
272
+
273
+ /**
274
+ * Convenience wrapper: build AND format in a single call.
275
+ *
276
+ * @param stage - The canonical pipeline stage identifier
277
+ * @param cwd - Optional project root override for skill resolution
278
+ * @returns Markdown text ready for LLM system prompt injection
279
+ */
280
+ export function renderStageGuidance(stage: Stage, cwd?: string): string {
281
+ return formatStageGuidance(buildStageGuidance(stage, cwd));
282
+ }
@@ -11,27 +11,39 @@ function makeDetection(overrides) {
11
11
  pathGlobal: '',
12
12
  pathProject: '',
13
13
  instructFile: 'CLAUDE.md',
14
- configKey: 'mcpServers',
15
- configFormat: 'json',
16
- configPathGlobal: '',
17
- configPathProject: '.claude.json',
18
14
  pathSkills: '',
19
15
  pathProjectSkills: '',
20
16
  detection: { methods: ['binary'], binary: 'claude' },
21
- supportedTransports: ['stdio'],
22
- supportsHeaders: false,
23
17
  priority: 'high',
24
18
  status: 'active',
25
19
  agentSkillsCompatible: true,
26
20
  capabilities: {
21
+ mcp: {
22
+ configKey: 'mcpServers',
23
+ configFormat: 'json',
24
+ configPathGlobal: '',
25
+ configPathProject: '.claude.json',
26
+ supportedTransports: ['stdio'],
27
+ supportsHeaders: false,
28
+ },
29
+ harness: null,
27
30
  skills: { agentsGlobalPath: null, agentsProjectPath: null, precedence: 'agents-first' },
28
- hooks: { supported: [], hookConfigPath: null, hookFormat: null },
31
+ hooks: {
32
+ supported: [],
33
+ hookConfigPath: null,
34
+ hookConfigPathProject: null,
35
+ hookFormat: null,
36
+ nativeEventCatalog: 'canonical',
37
+ canInjectSystemPrompt: false,
38
+ canBlockTools: false,
39
+ },
29
40
  spawn: {
30
41
  supportsSubagents: true,
31
42
  supportsProgrammaticSpawn: true,
32
43
  supportsInterAgentComms: false,
33
44
  supportsParallelSpawn: true,
34
45
  spawnMechanism: 'cli',
46
+ spawnCommand: null,
35
47
  },
36
48
  },
37
49
  },
@@ -1 +1 @@
1
- {"version":3,"file":"provider-detection.test.js","sourceRoot":"","sources":["provider-detection.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"provider-detection.test.js","sourceRoot":"","sources":["provider-detection.test.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,4BAA4B,EAAE,MAAM,0BAA0B,CAAC;AAExE,SAAS,aAAa,CAAC,SAAmC;IACxD,OAAO;QACL,QAAQ,EAAE;YACR,EAAE,EAAE,aAAa;YACjB,QAAQ,EAAE,aAAa;YACvB,MAAM,EAAE,WAAW;YACnB,SAAS,EAAE,QAAQ;YACnB,OAAO,EAAE,CAAC,QAAQ,CAAC;YACnB,UAAU,EAAE,EAAE;YACd,WAAW,EAAE,EAAE;YACf,YAAY,EAAE,WAAW;YACzB,UAAU,EAAE,EAAE;YACd,iBAAiB,EAAE,EAAE;YACrB,SAAS,EAAE,EAAE,OAAO,EAAE,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE;YACpD,QAAQ,EAAE,MAAM;YAChB,MAAM,EAAE,QAAQ;YAChB,qBAAqB,EAAE,IAAI;YAC3B,YAAY,EAAE;gBACZ,GAAG,EAAE;oBACH,SAAS,EAAE,YAAY;oBACvB,YAAY,EAAE,MAAM;oBACpB,gBAAgB,EAAE,EAAE;oBACpB,iBAAiB,EAAE,cAAc;oBACjC,mBAAmB,EAAE,CAAC,OAAO,CAAC;oBAC9B,eAAe,EAAE,KAAK;iBACvB;gBACD,OAAO,EAAE,IAAI;gBACb,MAAM,EAAE,EAAE,gBAAgB,EAAE,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE,UAAU,EAAE,cAAc,EAAE;gBACvF,KAAK,EAAE;oBACL,SAAS,EAAE,EAAE;oBACb,cAAc,EAAE,IAAI;oBACpB,qBAAqB,EAAE,IAAI;oBAC3B,UAAU,EAAE,IAAI;oBAChB,kBAAkB,EAAE,WAAW;oBAC/B,qBAAqB,EAAE,KAAK;oBAC5B,aAAa,EAAE,KAAK;iBACrB;gBACD,KAAK,EAAE;oBACL,iBAAiB,EAAE,IAAI;oBACvB,yBAAyB,EAAE,IAAI;oBAC/B,uBAAuB,EAAE,KAAK;oBAC9B,qBAAqB,EAAE,IAAI;oBAC3B,cAAc,EAAE,KAAK;oBACrB,YAAY,EAAE,IAAI;iBACnB;aACF;SACF;QACD,SAAS,EAAE,IAAI;QACf,OAAO,EAAE,CAAC,QAAQ,CAAC;QACnB,eAAe,EAAE,KAAK;QACtB,GAAG,SAAS;KACb,CAAC;AACJ,CAAC;AAED,QAAQ,CAAC,8BAA8B,EAAE,GAAG,EAAE;IAC5C,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;QACzD,MAAM,UAAU,GAAG;YACjB,aAAa,CAAC,EAAE,CAAC;YACjB,aAAa,CAAC;gBACZ,QAAQ,EAAE;oBACR,GAAG,aAAa,CAAC,EAAE,CAAC,CAAC,QAAQ;oBAC7B,EAAE,EAAE,UAAU;oBACd,QAAQ,EAAE,UAAU;oBACpB,MAAM,EAAE,UAAU;oBAClB,SAAS,EAAE,UAAU;oBACrB,OAAO,EAAE,CAAC,WAAW,CAAC;oBACtB,YAAY,EAAE,WAAW;iBAC1B;aACF,CAAC;SACH,CAAC;QAEF,MAAM,MAAM,GAAG,4BAA4B,CAAC,UAAU,EAAE;YACtD,IAAI,EAAE,CAAC,eAAe,EAAE,yBAAyB,CAAC;YAClD,GAAG,EAAE,EAAE;SACR,CAAC,CAAC;QAEH,MAAM,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAClD,MAAM,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kDAAkD,EAAE,GAAG,EAAE;QAC1D,MAAM,UAAU,GAAG;YACjB,aAAa,CAAC,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC;YACxC,aAAa,CAAC;gBACZ,QAAQ,EAAE;oBACR,GAAG,aAAa,CAAC,EAAE,CAAC,CAAC,QAAQ;oBAC7B,EAAE,EAAE,QAAQ;oBACZ,QAAQ,EAAE,QAAQ;oBAClB,MAAM,EAAE,QAAQ;oBAChB,SAAS,EAAE,QAAQ;oBACnB,OAAO,EAAE,CAAC,WAAW,CAAC;iBACvB;gBACD,eAAe,EAAE,KAAK;aACvB,CAAC;SACH,CAAC;QAEF,MAAM,MAAM,GAAG,4BAA4B,CAAC,UAAU,EAAE;YACtD,IAAI,EAAE,CAAC,eAAe,EAAE,qBAAqB,CAAC;YAC9C,GAAG,EAAE,EAAE;SACR,CAAC,CAAC;QAEH,MAAM,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QACrD,MAAM,CAAC,MAAM,CAAC,qBAAqB,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qEAAqE,EAAE,GAAG,EAAE;QAC7E,MAAM,UAAU,GAAG;YACjB,aAAa,CAAC,EAAE,CAAC;YACjB,aAAa,CAAC;gBACZ,QAAQ,EAAE;oBACR,GAAG,aAAa,CAAC,EAAE,CAAC,CAAC,QAAQ;oBAC7B,EAAE,EAAE,QAAQ;oBACZ,QAAQ,EAAE,QAAQ;oBAClB,MAAM,EAAE,QAAQ;oBAChB,SAAS,EAAE,QAAQ;oBACnB,OAAO,EAAE,CAAC,WAAW,CAAC;iBACvB;aACF,CAAC;SACH,CAAC;QAEF,MAAM,MAAM,GAAG,4BAA4B,CAAC,UAAU,EAAE;YACtD,IAAI,EAAE,CAAC,eAAe,EAAE,qBAAqB,CAAC;YAC9C,GAAG,EAAE,EAAE;SACR,CAAC,CAAC;QAEH,MAAM,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,aAAa,EAAE,CAAC;QACjD,MAAM,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,OAAO,CAAC,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAC,CAAC;IACtE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -8,7 +8,7 @@
8
8
  import { ExitCode } from '@cleocode/contracts';
9
9
  import { describe, expect, it } from 'vitest';
10
10
  import { CleoError } from '../../errors.js';
11
- import { PROTOCOL_EXIT_CODES, PROTOCOL_TYPES, validateArtifactPublishProtocol, validateConsensusProtocol, validateContributionProtocol, validateDecompositionProtocol, validateImplementationProtocol, validateProtocol, validateProvenanceProtocol, validateReleaseProtocol, validateResearchProtocol, validateSpecificationProtocol, } from '../protocol-validators.js';
11
+ import { PROTOCOL_EXIT_CODES, PROTOCOL_TYPES, validateArchitectureDecisionProtocol, validateArtifactPublishProtocol, validateConsensusProtocol, validateContributionProtocol, validateDecompositionProtocol, validateImplementationProtocol, validateProtocol, validateProvenanceProtocol, validateReleaseProtocol, validateResearchProtocol, validateSpecificationProtocol, validateTestingProtocol, validateValidationProtocol, } from '../protocol-validators.js';
12
12
  // ============================================================
13
13
  // Helper: create valid manifest entry for a protocol
14
14
  // ============================================================
@@ -314,13 +314,222 @@ describe('Provenance Protocol', () => {
314
314
  });
315
315
  });
316
316
  // ============================================================
317
+ // 10. ARCHITECTURE DECISION RECORD PROTOCOL (exit code 84)
318
+ // ============================================================
319
+ describe('Architecture Decision Record Protocol', () => {
320
+ const adrEntry = (overrides = {}) => ({
321
+ ...validEntry('decision'),
322
+ consensus_manifest_id: 'manifest_consensus_2026_04_07',
323
+ ...overrides,
324
+ });
325
+ it('passes a complete ADR entry', () => {
326
+ const result = validateArchitectureDecisionProtocol(adrEntry(), {
327
+ status: 'proposed',
328
+ adrContent: '## Context\n## Options Evaluated\n## Decision\n## Rationale\n## Consequences\n',
329
+ persistedInDb: true,
330
+ });
331
+ expect(result.valid).toBe(true);
332
+ expect(result.protocol).toBe('architecture-decision');
333
+ });
334
+ it('fails without a consensus manifest link (ADR-001)', () => {
335
+ const result = validateArchitectureDecisionProtocol(adrEntry({ consensus_manifest_id: undefined }));
336
+ expect(result.valid).toBe(false);
337
+ expect(result.violations).toContainEqual(expect.objectContaining({ requirement: 'ADR-001' }));
338
+ });
339
+ it('fails when accepted without HITL review (ADR-003)', () => {
340
+ const result = validateArchitectureDecisionProtocol(adrEntry(), {
341
+ status: 'accepted',
342
+ hitlReviewed: false,
343
+ adrContent: '## Context\n## Options\n## Decision\n## Rationale\n## Consequences\n',
344
+ persistedInDb: true,
345
+ });
346
+ expect(result.valid).toBe(false);
347
+ expect(result.violations).toContainEqual(expect.objectContaining({ requirement: 'ADR-003' }));
348
+ });
349
+ it('fails when required sections are missing (ADR-004)', () => {
350
+ const result = validateArchitectureDecisionProtocol(adrEntry(), {
351
+ status: 'proposed',
352
+ adrContent: '## Context only',
353
+ persistedInDb: true,
354
+ });
355
+ expect(result.valid).toBe(false);
356
+ expect(result.violations).toContainEqual(expect.objectContaining({ requirement: 'ADR-004' }));
357
+ });
358
+ it('fails superseded ADR without downstream flag (ADR-005)', () => {
359
+ const result = validateArchitectureDecisionProtocol(adrEntry(), {
360
+ status: 'superseded',
361
+ downstreamFlagged: false,
362
+ adrContent: '## Context\n## Options\n## Decision\n## Rationale\n## Consequences\n',
363
+ persistedInDb: true,
364
+ });
365
+ expect(result.valid).toBe(false);
366
+ expect(result.violations).toContainEqual(expect.objectContaining({ requirement: 'ADR-005' }));
367
+ });
368
+ it('fails when not persisted in SQLite (ADR-006)', () => {
369
+ const result = validateArchitectureDecisionProtocol(adrEntry(), {
370
+ status: 'proposed',
371
+ persistedInDb: false,
372
+ adrContent: '## Context\n## Options\n## Decision\n## Rationale\n## Consequences\n',
373
+ });
374
+ expect(result.valid).toBe(false);
375
+ expect(result.violations).toContainEqual(expect.objectContaining({ requirement: 'ADR-006' }));
376
+ });
377
+ it('fails with wrong agent_type (ADR-007)', () => {
378
+ const result = validateArchitectureDecisionProtocol(adrEntry({ agent_type: 'specification' }));
379
+ expect(result.valid).toBe(false);
380
+ expect(result.violations).toContainEqual(expect.objectContaining({ requirement: 'ADR-007' }));
381
+ });
382
+ });
383
+ // ============================================================
384
+ // 11. VALIDATION PROTOCOL (exit code 80)
385
+ // ============================================================
386
+ describe('Validation Protocol', () => {
387
+ it('passes a complete validation entry', () => {
388
+ const result = validateValidationProtocol(validEntry('validation'), {
389
+ specMatchConfirmed: true,
390
+ testSuitePassed: true,
391
+ protocolComplianceChecked: true,
392
+ });
393
+ expect(result.valid).toBe(true);
394
+ expect(result.protocol).toBe('validation');
395
+ });
396
+ it('fails when spec-match not confirmed (VALID-001)', () => {
397
+ const result = validateValidationProtocol(validEntry('validation'), {
398
+ specMatchConfirmed: false,
399
+ });
400
+ expect(result.valid).toBe(false);
401
+ expect(result.violations).toContainEqual(expect.objectContaining({ requirement: 'VALID-001' }));
402
+ });
403
+ it('fails when test suite failed (VALID-002)', () => {
404
+ const result = validateValidationProtocol(validEntry('validation'), {
405
+ testSuitePassed: false,
406
+ });
407
+ expect(result.valid).toBe(false);
408
+ expect(result.violations).toContainEqual(expect.objectContaining({ requirement: 'VALID-002' }));
409
+ });
410
+ it('fails without key_findings (VALID-005)', () => {
411
+ const result = validateValidationProtocol(validEntry('validation', { key_findings: [] }));
412
+ expect(result.valid).toBe(false);
413
+ expect(result.violations).toContainEqual(expect.objectContaining({ requirement: 'VALID-005' }));
414
+ });
415
+ it('fails with wrong agent_type (VALID-006)', () => {
416
+ const result = validateValidationProtocol(validEntry('implementation'));
417
+ expect(result.valid).toBe(false);
418
+ expect(result.violations).toContainEqual(expect.objectContaining({ requirement: 'VALID-006' }));
419
+ });
420
+ });
421
+ // ============================================================
422
+ // 12. TESTING PROTOCOL (project-agnostic IVT loop)
423
+ // ============================================================
424
+ describe('Testing Protocol', () => {
425
+ it('passes a complete testing entry with any framework', () => {
426
+ const result = validateTestingProtocol(validEntry('testing'), {
427
+ framework: 'vitest',
428
+ testsRun: 42,
429
+ testsPassed: 42,
430
+ testsFailed: 0,
431
+ ivtLoopConverged: true,
432
+ });
433
+ expect(result.valid).toBe(true);
434
+ expect(result.protocol).toBe('testing');
435
+ });
436
+ it('is project-agnostic — accepts any supported framework', () => {
437
+ const frameworks = [
438
+ 'vitest',
439
+ 'jest',
440
+ 'pytest',
441
+ 'go-test',
442
+ 'cargo-test',
443
+ 'rspec',
444
+ 'bats',
445
+ 'other',
446
+ ];
447
+ for (const framework of frameworks) {
448
+ const result = validateTestingProtocol(validEntry('testing'), {
449
+ framework,
450
+ testsRun: 1,
451
+ testsPassed: 1,
452
+ testsFailed: 0,
453
+ ivtLoopConverged: true,
454
+ });
455
+ expect(result.valid).toBe(true);
456
+ }
457
+ });
458
+ it('fails without a detected framework (TEST-001)', () => {
459
+ const result = validateTestingProtocol(validEntry('testing'), {
460
+ testsRun: 10,
461
+ testsPassed: 10,
462
+ testsFailed: 0,
463
+ ivtLoopConverged: true,
464
+ });
465
+ expect(result.valid).toBe(false);
466
+ expect(result.violations).toContainEqual(expect.objectContaining({ requirement: 'TEST-001' }));
467
+ });
468
+ it('fails when tests failed (TEST-004)', () => {
469
+ const result = validateTestingProtocol(validEntry('testing'), {
470
+ framework: 'vitest',
471
+ testsRun: 10,
472
+ testsPassed: 8,
473
+ testsFailed: 2,
474
+ ivtLoopConverged: false,
475
+ });
476
+ expect(result.valid).toBe(false);
477
+ expect(result.violations).toContainEqual(expect.objectContaining({ requirement: 'TEST-004' }));
478
+ });
479
+ it('fails when IVT loop has not converged (TEST-005)', () => {
480
+ const result = validateTestingProtocol(validEntry('testing'), {
481
+ framework: 'vitest',
482
+ ivtLoopConverged: false,
483
+ });
484
+ expect(result.valid).toBe(false);
485
+ expect(result.violations).toContainEqual(expect.objectContaining({ requirement: 'TEST-005' }));
486
+ });
487
+ it('warns on coverage below threshold (non-blocking)', () => {
488
+ const result = validateTestingProtocol(validEntry('testing'), {
489
+ framework: 'vitest',
490
+ testsRun: 10,
491
+ testsPassed: 10,
492
+ testsFailed: 0,
493
+ ivtLoopConverged: true,
494
+ coveragePercent: 70,
495
+ coverageThreshold: 80,
496
+ });
497
+ expect(result.violations).toContainEqual(expect.objectContaining({ requirement: 'TEST-004', severity: 'warning' }));
498
+ });
499
+ it('fails with wrong agent_type (TEST-007)', () => {
500
+ const result = validateTestingProtocol(validEntry('implementation'), {
501
+ framework: 'vitest',
502
+ ivtLoopConverged: true,
503
+ });
504
+ expect(result.valid).toBe(false);
505
+ expect(result.violations).toContainEqual(expect.objectContaining({ requirement: 'TEST-007' }));
506
+ });
507
+ });
508
+ // ============================================================
317
509
  // UNIFIED DISPATCHER
318
510
  // ============================================================
319
511
  describe('validateProtocol (unified dispatcher)', () => {
320
- it('dispatches to all 9 protocols', () => {
512
+ it('dispatches to all 12 protocols', () => {
321
513
  for (const protocol of PROTOCOL_TYPES) {
322
- const agentType = protocol === 'consensus' ? 'analysis' : protocol;
323
- const entry = validEntry(agentType);
514
+ // Map protocol name to expected agent_type
515
+ const agentTypeMap = {
516
+ research: 'research',
517
+ consensus: 'analysis',
518
+ 'architecture-decision': 'decision',
519
+ specification: 'specification',
520
+ decomposition: 'decomposition',
521
+ implementation: 'implementation',
522
+ contribution: 'contribution',
523
+ validation: 'validation',
524
+ testing: 'testing',
525
+ release: 'release',
526
+ 'artifact-publish': 'artifact-publish',
527
+ provenance: 'provenance',
528
+ };
529
+ const entry = {
530
+ ...validEntry(agentTypeMap[protocol]),
531
+ consensus_manifest_id: 'mf_consensus_test',
532
+ };
324
533
  // All should not throw without strict mode
325
534
  const result = validateProtocol(protocol, entry);
326
535
  expect(result.protocol).toBe(protocol);
@@ -331,7 +540,7 @@ describe('validateProtocol (unified dispatcher)', () => {
331
540
  expect(() => validateProtocol('research', entry, {}, true)).toThrow(CleoError);
332
541
  });
333
542
  it('uses correct exit codes for each protocol', () => {
334
- // Verify exit code mapping covers range 60-67
543
+ // Pipeline codes 60-67 + dedicated codes for cross-cutting protocols
335
544
  const exitCodes = new Set(Object.values(PROTOCOL_EXIT_CODES));
336
545
  expect(exitCodes.has(ExitCode.PROTOCOL_MISSING)).toBe(true); // 60
337
546
  expect(exitCodes.has(ExitCode.INVALID_RETURN_MESSAGE)).toBe(true); // 61
@@ -341,6 +550,10 @@ describe('validateProtocol (unified dispatcher)', () => {
341
550
  expect(exitCodes.has(ExitCode.HANDOFF_REQUIRED)).toBe(true); // 65
342
551
  expect(exitCodes.has(ExitCode.RESUME_FAILED)).toBe(true); // 66
343
552
  expect(exitCodes.has(ExitCode.CONCURRENT_SESSION)).toBe(true); // 67
553
+ expect(exitCodes.has(ExitCode.LIFECYCLE_GATE_FAILED)).toBe(true); // 80 (validation)
554
+ expect(exitCodes.has(ExitCode.PROVENANCE_REQUIRED)).toBe(true); // 84 (ADR)
555
+ expect(exitCodes.has(ExitCode.ARTIFACT_PUBLISH_FAILED)).toBe(true); // 88
556
+ expect(exitCodes.has(ExitCode.ATTESTATION_INVALID)).toBe(true); // 94
344
557
  });
345
558
  it('strict mode throws with correct exit code per protocol', () => {
346
559
  const testCases = [
@@ -351,7 +564,11 @@ describe('validateProtocol (unified dispatcher)', () => {
351
564
  { protocol: 'implementation', expectedCode: ExitCode.AUTONOMOUS_BOUNDARY },
352
565
  { protocol: 'contribution', expectedCode: ExitCode.HANDOFF_REQUIRED },
353
566
  { protocol: 'release', expectedCode: ExitCode.RESUME_FAILED },
354
- { protocol: 'artifact-publish', expectedCode: ExitCode.CONCURRENT_SESSION },
567
+ { protocol: 'testing', expectedCode: ExitCode.CONCURRENT_SESSION },
568
+ { protocol: 'validation', expectedCode: ExitCode.LIFECYCLE_GATE_FAILED },
569
+ { protocol: 'architecture-decision', expectedCode: ExitCode.PROVENANCE_REQUIRED },
570
+ { protocol: 'artifact-publish', expectedCode: ExitCode.ARTIFACT_PUBLISH_FAILED },
571
+ { protocol: 'provenance', expectedCode: ExitCode.ATTESTATION_INVALID },
355
572
  ];
356
573
  for (const { protocol, expectedCode } of testCases) {
357
574
  const entry = validEntry('wrong-type');
@@ -366,14 +583,17 @@ describe('validateProtocol (unified dispatcher)', () => {
366
583
  }
367
584
  }
368
585
  });
369
- it('PROTOCOL_TYPES contains all 9 protocols', () => {
370
- expect(PROTOCOL_TYPES).toHaveLength(9);
586
+ it('PROTOCOL_TYPES contains all 12 protocols', () => {
587
+ expect(PROTOCOL_TYPES).toHaveLength(12);
371
588
  expect(PROTOCOL_TYPES).toContain('research');
372
589
  expect(PROTOCOL_TYPES).toContain('consensus');
590
+ expect(PROTOCOL_TYPES).toContain('architecture-decision');
373
591
  expect(PROTOCOL_TYPES).toContain('specification');
374
592
  expect(PROTOCOL_TYPES).toContain('decomposition');
375
593
  expect(PROTOCOL_TYPES).toContain('implementation');
376
594
  expect(PROTOCOL_TYPES).toContain('contribution');
595
+ expect(PROTOCOL_TYPES).toContain('validation');
596
+ expect(PROTOCOL_TYPES).toContain('testing');
377
597
  expect(PROTOCOL_TYPES).toContain('release');
378
598
  expect(PROTOCOL_TYPES).toContain('artifact-publish');
379
599
  expect(PROTOCOL_TYPES).toContain('provenance');