@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.
- package/dist/discovery.d.ts +69 -0
- package/dist/discovery.d.ts.map +1 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1643 -2349
- package/dist/index.js.map +4 -4
- package/dist/init.d.ts +51 -0
- package/dist/init.d.ts.map +1 -1
- package/dist/internal.d.ts +9 -1
- package/dist/internal.d.ts.map +1 -1
- package/dist/lifecycle/default-chain.d.ts +8 -2
- package/dist/lifecycle/default-chain.d.ts.map +1 -1
- package/dist/lifecycle/index.d.ts +1 -0
- package/dist/lifecycle/index.d.ts.map +1 -1
- package/dist/lifecycle/stage-guidance.d.ts +140 -0
- package/dist/lifecycle/stage-guidance.d.ts.map +1 -0
- package/dist/orchestration/protocol-validators.d.ts +122 -3
- package/dist/orchestration/protocol-validators.d.ts.map +1 -1
- package/dist/paths.d.ts +91 -0
- package/dist/paths.d.ts.map +1 -1
- package/dist/scaffold.d.ts +31 -1
- package/dist/scaffold.d.ts.map +1 -1
- package/dist/skills/dispatch.d.ts +1 -1
- package/dist/skills/skill-paths.d.ts +9 -6
- package/dist/skills/skill-paths.d.ts.map +1 -1
- package/dist/validation/protocols/_shared.d.ts +40 -0
- package/dist/validation/protocols/_shared.d.ts.map +1 -0
- package/dist/validation/protocols/architecture-decision.d.ts +23 -0
- package/dist/validation/protocols/architecture-decision.d.ts.map +1 -0
- package/dist/validation/protocols/artifact-publish.d.ts +22 -0
- package/dist/validation/protocols/artifact-publish.d.ts.map +1 -0
- package/dist/validation/protocols/consensus.d.ts +11 -17
- package/dist/validation/protocols/consensus.d.ts.map +1 -1
- package/dist/validation/protocols/contribution.d.ts +12 -17
- package/dist/validation/protocols/contribution.d.ts.map +1 -1
- package/dist/validation/protocols/decomposition.d.ts +18 -21
- package/dist/validation/protocols/decomposition.d.ts.map +1 -1
- package/dist/validation/protocols/implementation.d.ts +9 -17
- package/dist/validation/protocols/implementation.d.ts.map +1 -1
- package/dist/validation/protocols/provenance.d.ts +23 -0
- package/dist/validation/protocols/provenance.d.ts.map +1 -0
- package/dist/validation/protocols/release.d.ts +25 -0
- package/dist/validation/protocols/release.d.ts.map +1 -0
- package/dist/validation/protocols/research.d.ts +9 -17
- package/dist/validation/protocols/research.d.ts.map +1 -1
- package/dist/validation/protocols/specification.d.ts +7 -17
- package/dist/validation/protocols/specification.d.ts.map +1 -1
- package/dist/validation/protocols/testing.d.ts +22 -0
- package/dist/validation/protocols/testing.d.ts.map +1 -0
- package/dist/validation/protocols/validation.d.ts +22 -0
- package/dist/validation/protocols/validation.d.ts.map +1 -0
- package/package.json +7 -7
- package/src/__tests__/injection-mvi-tiers.test.js +54 -90
- package/src/__tests__/injection-mvi-tiers.test.js.map +1 -1
- package/src/discovery.ts +235 -0
- package/src/hooks/handlers/__tests__/hook-automation-e2e.test.js +3 -1
- package/src/hooks/handlers/__tests__/hook-automation-e2e.test.js.map +1 -1
- package/src/index.ts +16 -0
- package/src/init.ts +196 -0
- package/src/internal.ts +31 -1
- package/src/lifecycle/default-chain.ts +11 -2
- package/src/lifecycle/index.ts +10 -0
- package/src/lifecycle/stage-guidance.ts +282 -0
- package/src/metrics/__tests__/provider-detection.test.js +19 -7
- package/src/metrics/__tests__/provider-detection.test.js.map +1 -1
- package/src/orchestration/__tests__/protocol-validators.test.js +228 -8
- package/src/orchestration/__tests__/protocol-validators.test.js.map +1 -1
- package/src/orchestration/__tests__/protocol-validators.test.ts +259 -7
- package/src/orchestration/protocol-validators.ts +419 -4
- package/src/paths.ts +110 -0
- package/src/scaffold.ts +240 -4
- package/src/skills/dispatch.ts +6 -6
- package/src/skills/skill-paths.ts +27 -23
- package/src/validation/protocols/_shared.ts +88 -0
- package/src/validation/protocols/architecture-decision.ts +52 -0
- package/src/validation/protocols/artifact-publish.ts +49 -0
- package/src/validation/protocols/consensus.ts +44 -74
- package/src/validation/protocols/contribution.ts +28 -65
- package/src/validation/protocols/decomposition.ts +37 -64
- package/src/validation/protocols/implementation.ts +25 -65
- package/src/validation/protocols/protocols-markdown/architecture-decision.md +303 -0
- package/src/validation/protocols/protocols-markdown/artifact-publish.md +600 -0
- package/src/validation/protocols/protocols-markdown/consensus.md +322 -0
- package/src/validation/protocols/protocols-markdown/contribution.md +388 -0
- package/src/validation/protocols/protocols-markdown/decomposition.md +421 -0
- package/src/validation/protocols/protocols-markdown/implementation.md +357 -0
- package/src/validation/protocols/protocols-markdown/provenance.md +613 -0
- package/src/validation/protocols/protocols-markdown/release.md +783 -0
- package/src/validation/protocols/protocols-markdown/research.md +261 -0
- package/src/validation/protocols/protocols-markdown/specification.md +300 -0
- package/src/validation/protocols/protocols-markdown/testing.md +287 -0
- package/src/validation/protocols/protocols-markdown/validation.md +242 -0
- package/src/validation/protocols/provenance.ts +50 -0
- package/src/validation/protocols/release.ts +44 -0
- package/src/validation/protocols/research.ts +25 -87
- package/src/validation/protocols/specification.ts +27 -89
- package/src/validation/protocols/testing.ts +46 -0
- package/src/validation/protocols/validation.ts +46 -0
- package/dist/validation/protocols/release-protocol.d.ts +0 -27
- package/dist/validation/protocols/release-protocol.d.ts.map +0 -1
- package/dist/validation/protocols/testing-protocol.d.ts +0 -27
- package/dist/validation/protocols/testing-protocol.d.ts.map +0 -1
- package/dist/validation/protocols/validation-protocol.d.ts +0 -27
- package/dist/validation/protocols/validation-protocol.d.ts.map +0 -1
- package/schemas/agent-configs.schema.json +0 -120
- package/schemas/agent-registry.schema.json +0 -132
- package/schemas/archive.schema.json +0 -450
- package/schemas/brain-decision.schema.json +0 -69
- package/schemas/brain-learning.schema.json +0 -57
- package/schemas/brain-pattern.schema.json +0 -72
- package/schemas/critical-path.schema.json +0 -246
- package/schemas/deps-cache.schema.json +0 -97
- package/schemas/doctor-output.schema.json +0 -283
- package/schemas/error.schema.json +0 -161
- package/schemas/global-config.schema.json +0 -219
- package/schemas/grade.schema.json +0 -49
- package/schemas/log.schema.json +0 -250
- package/schemas/metrics.schema.json +0 -328
- package/schemas/migrations.schema.json +0 -150
- package/schemas/nexus-registry.schema.json +0 -90
- package/schemas/operation-constitution.schema.json +0 -438
- package/schemas/output.schema.json +0 -164
- package/schemas/projects-registry.schema.json +0 -107
- package/schemas/protocol-frontmatter.schema.json +0 -72
- package/schemas/rcasd-consensus-report.schema.json +0 -10
- package/schemas/rcasd-evidence.schema.json +0 -42
- package/schemas/rcasd-gate-result.schema.json +0 -46
- package/schemas/rcasd-hitl-resolution.schema.json +0 -10
- package/schemas/rcasd-index.schema.json +0 -10
- package/schemas/rcasd-manifest.schema.json +0 -10
- package/schemas/rcasd-research-output.schema.json +0 -10
- package/schemas/rcasd-spec-frontmatter.schema.json +0 -10
- package/schemas/rcasd-stage-transition.schema.json +0 -38
- package/schemas/releases.schema.json +0 -267
- package/schemas/skills-manifest.schema.json +0 -91
- package/schemas/spec-index.schema.json +0 -196
- package/schemas/system-flow-atlas.schema.json +0 -125
- package/src/conduit/__tests__/dual-api-e2e.test.d.ts.map +0 -1
- package/src/conduit/__tests__/dual-api-e2e.test.js +0 -178
- package/src/conduit/__tests__/dual-api-e2e.test.js.map +0 -1
- package/src/conduit/__tests__/dual-api-e2e.test.ts +0 -212
- package/src/validation/protocols/release-protocol.ts +0 -80
- package/src/validation/protocols/testing-protocol.ts +0 -93
- 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: {
|
|
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":"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,
|
|
1
|
+
{"version":3,"file":"provider-detection.test.js","sourceRoot":"","sources":["provider-detection.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -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
|
|
512
|
+
it('dispatches to all 12 protocols', () => {
|
|
321
513
|
for (const protocol of PROTOCOL_TYPES) {
|
|
322
|
-
|
|
323
|
-
const
|
|
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
|
-
//
|
|
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: '
|
|
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
|
|
370
|
-
expect(PROTOCOL_TYPES).toHaveLength(
|
|
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');
|