@llm-dev-ops/agentics-cli 2.7.36 → 2.7.38
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/adapters/base-adapter.d.ts.map +1 -1
- package/dist/adapters/base-adapter.js.map +1 -1
- package/dist/agents/repo-agent-runner.d.ts.map +1 -1
- package/dist/agents/repo-agent-runner.js +0 -2
- package/dist/agents/repo-agent-runner.js.map +1 -1
- package/dist/agents/system-prompts.d.ts.map +1 -1
- package/dist/agents/system-prompts.js +0 -19
- package/dist/agents/system-prompts.js.map +1 -1
- package/dist/cli/index.js +1 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/commands/agents.d.ts +4 -24
- package/dist/commands/agents.d.ts.map +1 -1
- package/dist/commands/agents.js +30 -106
- package/dist/commands/agents.js.map +1 -1
- package/dist/mcp/agent-event-parser.d.ts +1 -11
- package/dist/mcp/agent-event-parser.d.ts.map +1 -1
- package/dist/mcp/agent-event-parser.js +7 -153
- package/dist/mcp/agent-event-parser.js.map +1 -1
- package/dist/mcp/mcp-server.js +0 -58
- package/dist/mcp/mcp-server.js.map +1 -1
- package/dist/pipeline/auto-chain.d.ts.map +1 -1
- package/dist/pipeline/auto-chain.js +27 -169
- package/dist/pipeline/auto-chain.js.map +1 -1
- package/dist/pipeline/local-fallback/phase5a-local-fallback.d.ts +21 -18
- package/dist/pipeline/local-fallback/phase5a-local-fallback.d.ts.map +1 -1
- package/dist/pipeline/local-fallback/phase5a-local-fallback.js +92 -397
- package/dist/pipeline/local-fallback/phase5a-local-fallback.js.map +1 -1
- package/dist/pipeline/phase2/phases/adr-generator.d.ts +29 -1
- package/dist/pipeline/phase2/phases/adr-generator.d.ts.map +1 -1
- package/dist/pipeline/phase2/phases/adr-generator.js +709 -1399
- package/dist/pipeline/phase2/phases/adr-generator.js.map +1 -1
- package/dist/pipeline/phase2/phases/ddd-generator.d.ts.map +1 -1
- package/dist/pipeline/phase2/phases/ddd-generator.js +7 -42
- package/dist/pipeline/phase2/phases/ddd-generator.js.map +1 -1
- package/dist/pipeline/phase2/phases/research-dossier.d.ts.map +1 -1
- package/dist/pipeline/phase2/phases/research-dossier.js +2 -33
- package/dist/pipeline/phase2/phases/research-dossier.js.map +1 -1
- package/dist/pipeline/phase2/phases/sparc-specification.d.ts.map +1 -1
- package/dist/pipeline/phase2/phases/sparc-specification.js +2 -27
- package/dist/pipeline/phase2/phases/sparc-specification.js.map +1 -1
- package/dist/pipeline/phase2/types.d.ts +19 -57
- package/dist/pipeline/phase2/types.d.ts.map +1 -1
- package/dist/pipeline/phase4-adrs/adr-index-extractor.d.ts +75 -0
- package/dist/pipeline/phase4-adrs/adr-index-extractor.d.ts.map +1 -0
- package/dist/pipeline/phase4-adrs/adr-index-extractor.js +200 -0
- package/dist/pipeline/phase4-adrs/adr-index-extractor.js.map +1 -0
- package/dist/pipeline/phase4-adrs/phase4-adrs-coordinator.d.ts.map +1 -1
- package/dist/pipeline/phase4-adrs/phase4-adrs-coordinator.js +70 -68
- package/dist/pipeline/phase4-adrs/phase4-adrs-coordinator.js.map +1 -1
- package/dist/pipeline/phase7/deliverables-registry.js +1 -1
- package/dist/pipeline/phase7/deliverables-registry.js.map +1 -1
- package/dist/pipeline/phases/adr-ddd-generator.d.ts.map +1 -1
- package/dist/pipeline/phases/adr-ddd-generator.js +48 -2
- package/dist/pipeline/phases/adr-ddd-generator.js.map +1 -1
- package/dist/pipeline/phases/prompt-generator.js +191 -80
- package/dist/pipeline/phases/prompt-generator.js.map +1 -1
- package/dist/pipeline/ruflo-phase-executor.d.ts +2 -5
- package/dist/pipeline/ruflo-phase-executor.d.ts.map +1 -1
- package/dist/pipeline/ruflo-phase-executor.js +72 -23
- package/dist/pipeline/ruflo-phase-executor.js.map +1 -1
- package/dist/pipeline/types.d.ts +14 -1
- package/dist/pipeline/types.d.ts.map +1 -1
- package/dist/routing/domain-boundary.d.ts +4 -20
- package/dist/routing/domain-boundary.d.ts.map +1 -1
- package/dist/routing/domain-boundary.js +6 -81
- package/dist/routing/domain-boundary.js.map +1 -1
- package/dist/routing/graph-router.d.ts.map +1 -1
- package/dist/routing/graph-router.js +0 -22
- package/dist/routing/graph-router.js.map +1 -1
- package/dist/synthesis/ask-artifact-writer.d.ts +1 -1
- package/dist/synthesis/ask-artifact-writer.d.ts.map +1 -1
- package/dist/synthesis/ask-artifact-writer.js +9 -9
- package/dist/synthesis/ask-artifact-writer.js.map +1 -1
- package/dist/synthesis/simulation-artifact-generator.d.ts +1 -27
- package/dist/synthesis/simulation-artifact-generator.d.ts.map +1 -1
- package/dist/synthesis/simulation-artifact-generator.js +38 -128
- package/dist/synthesis/simulation-artifact-generator.js.map +1 -1
- package/docs/ecosystem.graph.json +15 -65
- package/package.json +1 -1
- package/dist/cli/ui/heartbeat.d.ts +0 -88
- package/dist/cli/ui/heartbeat.d.ts.map +0 -1
- package/dist/cli/ui/heartbeat.js +0 -158
- package/dist/cli/ui/heartbeat.js.map +0 -1
- package/dist/config/qe-gating.d.ts +0 -81
- package/dist/config/qe-gating.d.ts.map +0 -1
- package/dist/config/qe-gating.js +0 -138
- package/dist/config/qe-gating.js.map +0 -1
- package/dist/pipeline/phase5-build/qe-gating-executor.d.ts +0 -73
- package/dist/pipeline/phase5-build/qe-gating-executor.d.ts.map +0 -1
- package/dist/pipeline/phase5-build/qe-gating-executor.js +0 -134
- package/dist/pipeline/phase5-build/qe-gating-executor.js.map +0 -1
- package/dist/synthesis/agent-fleet-decomposer.d.ts +0 -124
- package/dist/synthesis/agent-fleet-decomposer.d.ts.map +0 -1
- package/dist/synthesis/agent-fleet-decomposer.js +0 -696
- package/dist/synthesis/agent-fleet-decomposer.js.map +0 -1
|
@@ -1,696 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ADR-PIPELINE-099 D1.3 — Agent-fleet decomposition strategy for Phase 5a.
|
|
3
|
-
*
|
|
4
|
-
* Walks `<runDir>/agent-responses.json` to extract structured outputs from
|
|
5
|
-
* the copilot fleet (decomposer.sub_objectives, planner.steps,
|
|
6
|
-
* clarifier.assumptions/clarifications) and combines them with scenario
|
|
7
|
-
* domain entities, success criteria, and risk register entries to produce
|
|
8
|
-
* a `BuildableUnit[]` shape — one unit per decomposer sub-objective (or
|
|
9
|
-
* planner step, when the decomposer is silent).
|
|
10
|
-
*
|
|
11
|
-
* Each unit carries:
|
|
12
|
-
* - the source agent + index that produced it (for trace)
|
|
13
|
-
* - acceptance criteria (the Definition of Done for that prompt)
|
|
14
|
-
* - dependencies (named by id of other units)
|
|
15
|
-
* - complexity score (used by the prompt template to set scope)
|
|
16
|
-
* - load-bearing assumptions surfaced from the clarifier
|
|
17
|
-
* - guardrails surfaced from scenario.constraints + risk_register
|
|
18
|
-
*
|
|
19
|
-
* The downstream `phase5aGenerateFromAgentFleet` strategy converts each
|
|
20
|
-
* unit into one `prompts/impl-NNN-<slug>.md` file. The contract is:
|
|
21
|
-
* ≥6 units when the decomposer produced ≥6 sub_objectives; otherwise
|
|
22
|
-
* tier-3 returns whatever it has and the caller back-fills with tier-4.
|
|
23
|
-
*
|
|
24
|
-
* No external deps — pure file IO + JSON parsing.
|
|
25
|
-
*/
|
|
26
|
-
import * as fs from 'node:fs';
|
|
27
|
-
import * as path from 'node:path';
|
|
28
|
-
/**
|
|
29
|
-
* Decompose Phase-1 fleet output into buildable units. Returns an empty
|
|
30
|
-
* units array (with whatever context is available) when no agent
|
|
31
|
-
* decomposition is recoverable — the caller decides whether to fall through
|
|
32
|
-
* to the success-criterion fallback.
|
|
33
|
-
*/
|
|
34
|
-
export function decomposeAgentFleet(runDir, scenarioQuery) {
|
|
35
|
-
const agentRows = readAgentResponses(runDir);
|
|
36
|
-
const decomposer = pickAgent(agentRows, 'copilot', 'decomposer');
|
|
37
|
-
const planner = pickAgent(agentRows, 'copilot', 'planner');
|
|
38
|
-
const clarifier = pickAgent(agentRows, 'copilot', 'clarifier');
|
|
39
|
-
const scenario = readScenario(runDir);
|
|
40
|
-
const riskAssessment = readRiskAssessment(runDir);
|
|
41
|
-
// ── Build context ────────────────────────────────────────────────────────
|
|
42
|
-
const assumptions = extractStringArray(clarifier, ['assumptions']) ?? [];
|
|
43
|
-
const clarifications = extractStringArray(clarifier, ['clarifications']) ?? [];
|
|
44
|
-
const successCriteria = extractStringArray(scenario, ['success_criteria', 'successCriteria']) ?? [];
|
|
45
|
-
const constraints = extractStringArray(scenario, ['constraints']) ?? [];
|
|
46
|
-
const domainEntities = extractStringArray(scenario, ['domain_entities', 'domainEntities']) ?? [];
|
|
47
|
-
const systems = extractStringArray(scenario, ['systems']) ?? [];
|
|
48
|
-
const risks = extractRiskRegister(riskAssessment);
|
|
49
|
-
const context = {
|
|
50
|
-
assumptions: assumptions.slice(0, 8),
|
|
51
|
-
clarifications: clarifications.slice(0, 6),
|
|
52
|
-
successCriteria,
|
|
53
|
-
constraints,
|
|
54
|
-
domainEntities,
|
|
55
|
-
systems,
|
|
56
|
-
risks: risks.slice(0, 6),
|
|
57
|
-
scenarioQuery,
|
|
58
|
-
};
|
|
59
|
-
const subObjectives = extractObjectArray(decomposer, ['sub_objectives', 'subObjectives']) ?? [];
|
|
60
|
-
const plannerSteps = extractObjectArray(planner, ['steps']) ?? [];
|
|
61
|
-
const units = [];
|
|
62
|
-
// Tier-3 happy path: decomposer produced sub-objectives. Use them.
|
|
63
|
-
if (subObjectives.length > 0) {
|
|
64
|
-
subObjectives.forEach((so, idx) => {
|
|
65
|
-
const title = String(so.title ?? `Build unit ${idx + 1}`).trim();
|
|
66
|
-
const slug = slugify(title) || `unit-${idx + 1}`;
|
|
67
|
-
const id = `unit-${pad3(idx + 1)}-${slug}`;
|
|
68
|
-
const complexity = parseComplexity(so.complexity);
|
|
69
|
-
const acceptance = (Array.isArray(so.acceptance_criteria)
|
|
70
|
-
? so.acceptance_criteria.map(String).filter(Boolean)
|
|
71
|
-
: []);
|
|
72
|
-
const deps = resolveDeps(so.dependencies, units);
|
|
73
|
-
units.push({
|
|
74
|
-
id,
|
|
75
|
-
slug,
|
|
76
|
-
title,
|
|
77
|
-
description: String(so.description ?? title),
|
|
78
|
-
complexity,
|
|
79
|
-
acceptanceCriteria: acceptance,
|
|
80
|
-
dependencies: deps,
|
|
81
|
-
tier: classifyTier(title, so.description ?? ''),
|
|
82
|
-
sourceAgent: 'copilot/decomposer',
|
|
83
|
-
sourceIndex: idx,
|
|
84
|
-
});
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
// Supplement with planner steps that have no decomposer counterpart.
|
|
88
|
-
if (plannerSteps.length > 0 && units.length < plannerSteps.length) {
|
|
89
|
-
plannerSteps.forEach((step, idx) => {
|
|
90
|
-
const title = String(step.name ?? step.title ?? `Plan step ${idx + 1}`).trim();
|
|
91
|
-
// De-duplicate against existing units by slug.
|
|
92
|
-
const slug = slugify(title) || `step-${idx + 1}`;
|
|
93
|
-
if (units.some(u => u.slug === slug))
|
|
94
|
-
return;
|
|
95
|
-
const id = `unit-${pad3(units.length + 1)}-${slug}`;
|
|
96
|
-
const acceptance = Array.isArray(step.criteria)
|
|
97
|
-
? step.criteria.map(String).filter(Boolean)
|
|
98
|
-
: (typeof step.criteria === 'string' && step.criteria.trim() ? [step.criteria] : []);
|
|
99
|
-
const deps = resolveDeps(step.dependencies, units);
|
|
100
|
-
units.push({
|
|
101
|
-
id,
|
|
102
|
-
slug,
|
|
103
|
-
title,
|
|
104
|
-
description: String(step.description ?? title),
|
|
105
|
-
complexity: parseComplexity(step.effort),
|
|
106
|
-
acceptanceCriteria: acceptance,
|
|
107
|
-
dependencies: deps,
|
|
108
|
-
tier: classifyTier(title, step.description ?? ''),
|
|
109
|
-
sourceAgent: 'copilot/planner',
|
|
110
|
-
sourceIndex: idx,
|
|
111
|
-
});
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
// Final ordering: foundation → integration → domain → presentation → validation
|
|
115
|
-
const tierOrder = {
|
|
116
|
-
foundation: 0,
|
|
117
|
-
integration: 1,
|
|
118
|
-
domain: 2,
|
|
119
|
-
presentation: 3,
|
|
120
|
-
validation: 4,
|
|
121
|
-
};
|
|
122
|
-
units.sort((a, b) => tierOrder[a.tier] - tierOrder[b.tier]);
|
|
123
|
-
// Re-id after sort so order matches numeric prefix.
|
|
124
|
-
const ordered = units.map((u, idx) => ({
|
|
125
|
-
...u,
|
|
126
|
-
id: `unit-${pad3(idx + 1)}-${u.slug}`,
|
|
127
|
-
}));
|
|
128
|
-
return {
|
|
129
|
-
units: ordered,
|
|
130
|
-
context,
|
|
131
|
-
provenance: {
|
|
132
|
-
decomposerCount: subObjectives.length,
|
|
133
|
-
plannerCount: plannerSteps.length,
|
|
134
|
-
clarifierAssumptionsCount: assumptions.length,
|
|
135
|
-
riskRegisterCount: risks.length,
|
|
136
|
-
},
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
export function renderUnitPromptMarkdown(unit, unitOrder, totalUnits, context,
|
|
140
|
-
/** ADRs that match this unit by keyword overlap. May be empty. */
|
|
141
|
-
governingAdrs,
|
|
142
|
-
/** DDD bounded contexts that match this unit by keyword overlap. May be empty. */
|
|
143
|
-
governingContexts) {
|
|
144
|
-
const lines = [];
|
|
145
|
-
// Frontmatter — declares provenance + dependencies.
|
|
146
|
-
lines.push('---');
|
|
147
|
-
lines.push(`id: ${unit.id}`);
|
|
148
|
-
lines.push(`title: ${escapeYamlString(unit.title)}`);
|
|
149
|
-
lines.push(`order: ${unitOrder}`);
|
|
150
|
-
lines.push(`tier: ${unit.tier}`);
|
|
151
|
-
lines.push(`complexity: ${unit.complexity}`);
|
|
152
|
-
lines.push(`generator: agent-fleet`);
|
|
153
|
-
lines.push(`source_agent: ${unit.sourceAgent}`);
|
|
154
|
-
lines.push(`source_index: ${unit.sourceIndex}`);
|
|
155
|
-
lines.push(`degraded_mode: false`);
|
|
156
|
-
lines.push(`quality: directional`);
|
|
157
|
-
if (unit.dependencies.length === 0) {
|
|
158
|
-
lines.push(`dependency_root: true`);
|
|
159
|
-
}
|
|
160
|
-
else {
|
|
161
|
-
lines.push(`dependencies:`);
|
|
162
|
-
for (const d of unit.dependencies)
|
|
163
|
-
lines.push(` - ${d}`);
|
|
164
|
-
}
|
|
165
|
-
if (governingAdrs.length > 0) {
|
|
166
|
-
lines.push(`adrs:`);
|
|
167
|
-
for (const a of governingAdrs)
|
|
168
|
-
lines.push(` - ${a.id}`);
|
|
169
|
-
}
|
|
170
|
-
if (governingContexts.length > 0) {
|
|
171
|
-
lines.push(`bounded_contexts:`);
|
|
172
|
-
for (const c of governingContexts)
|
|
173
|
-
lines.push(` - ${c.name}`);
|
|
174
|
-
}
|
|
175
|
-
lines.push('---');
|
|
176
|
-
lines.push('');
|
|
177
|
-
lines.push(`# Build Step ${unitOrder} of ${totalUnits}: ${unit.title}`);
|
|
178
|
-
lines.push('');
|
|
179
|
-
lines.push(`**Tier:** ${unit.tier} · **Complexity:** ${unit.complexity}/5 · **Source:** \`${unit.sourceAgent}\` (output index ${unit.sourceIndex})`);
|
|
180
|
-
lines.push('');
|
|
181
|
-
// Project context — every prompt must be self-contained.
|
|
182
|
-
if (context.scenarioQuery) {
|
|
183
|
-
lines.push('## Project Brief');
|
|
184
|
-
lines.push('');
|
|
185
|
-
lines.push(context.scenarioQuery);
|
|
186
|
-
lines.push('');
|
|
187
|
-
}
|
|
188
|
-
// Step description.
|
|
189
|
-
lines.push('## What This Step Builds');
|
|
190
|
-
lines.push('');
|
|
191
|
-
lines.push(unit.description);
|
|
192
|
-
lines.push('');
|
|
193
|
-
// Dependencies — name what's already been built.
|
|
194
|
-
if (unit.dependencies.length > 0) {
|
|
195
|
-
lines.push('## Depends On (already built)');
|
|
196
|
-
lines.push('');
|
|
197
|
-
for (const d of unit.dependencies)
|
|
198
|
-
lines.push(`- \`${d}\` — import its public API`);
|
|
199
|
-
lines.push('');
|
|
200
|
-
}
|
|
201
|
-
else {
|
|
202
|
-
lines.push('## Depends On');
|
|
203
|
-
lines.push('');
|
|
204
|
-
lines.push('_Foundation step — no prior dependencies. Build the core types and interfaces that later steps will import._');
|
|
205
|
-
lines.push('');
|
|
206
|
-
}
|
|
207
|
-
// Governing ADRs — required for ADR-099 D1: every prompt cites its ADR.
|
|
208
|
-
if (governingAdrs.length > 0) {
|
|
209
|
-
lines.push(`## Governing Architecture Decisions (${governingAdrs.length})`);
|
|
210
|
-
lines.push('');
|
|
211
|
-
for (const a of governingAdrs) {
|
|
212
|
-
lines.push(`### ${a.id}: ${a.title}`);
|
|
213
|
-
if (a.decision) {
|
|
214
|
-
lines.push('');
|
|
215
|
-
lines.push(`**Decision:** ${a.decision.split('\n')[0]}`);
|
|
216
|
-
}
|
|
217
|
-
lines.push('');
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
// Governing DDD contexts.
|
|
221
|
-
if (governingContexts.length > 0) {
|
|
222
|
-
lines.push(`## Governing Domain Contexts (${governingContexts.length})`);
|
|
223
|
-
lines.push('');
|
|
224
|
-
for (const c of governingContexts) {
|
|
225
|
-
lines.push(`### ${c.name}`);
|
|
226
|
-
if (c.description) {
|
|
227
|
-
lines.push('');
|
|
228
|
-
lines.push(c.description);
|
|
229
|
-
}
|
|
230
|
-
if (c.aggregates && c.aggregates.length > 0) {
|
|
231
|
-
lines.push('');
|
|
232
|
-
lines.push('**Aggregates:**');
|
|
233
|
-
for (const ag of c.aggregates) {
|
|
234
|
-
lines.push(`- **${ag.name}**${ag.root ? ` (root: ${ag.root})` : ''}`);
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
if (c.commands && c.commands.length > 0) {
|
|
238
|
-
lines.push('');
|
|
239
|
-
lines.push(`**Commands:** ${c.commands.join(', ')}`);
|
|
240
|
-
}
|
|
241
|
-
if (c.queries && c.queries.length > 0) {
|
|
242
|
-
lines.push(`**Queries:** ${c.queries.join(', ')}`);
|
|
243
|
-
}
|
|
244
|
-
if (c.domainEvents && c.domainEvents.length > 0) {
|
|
245
|
-
lines.push(`**Events:** ${c.domainEvents.join(', ')}`);
|
|
246
|
-
}
|
|
247
|
-
lines.push('');
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
// Definition of Done.
|
|
251
|
-
if (unit.acceptanceCriteria.length > 0) {
|
|
252
|
-
lines.push('## Definition of Done (acceptance criteria)');
|
|
253
|
-
lines.push('');
|
|
254
|
-
for (const ac of unit.acceptanceCriteria)
|
|
255
|
-
lines.push(`- ${ac}`);
|
|
256
|
-
lines.push('');
|
|
257
|
-
}
|
|
258
|
-
// Load-bearing assumptions — surface from clarifier so the implementer
|
|
259
|
-
// doesn't unknowingly violate one.
|
|
260
|
-
if (context.assumptions.length > 0) {
|
|
261
|
-
lines.push('## Load-Bearing Assumptions (from copilot/clarifier)');
|
|
262
|
-
lines.push('');
|
|
263
|
-
lines.push('> Violating any of these requires a callout in the impl plan, not silent breakage.');
|
|
264
|
-
lines.push('');
|
|
265
|
-
for (const a of context.assumptions.slice(0, 6))
|
|
266
|
-
lines.push(`- ${a}`);
|
|
267
|
-
lines.push('');
|
|
268
|
-
}
|
|
269
|
-
// Guardrails — constraints + top risks.
|
|
270
|
-
if (context.constraints.length > 0 || context.risks.length > 0) {
|
|
271
|
-
lines.push('## Guardrails');
|
|
272
|
-
lines.push('');
|
|
273
|
-
if (context.constraints.length > 0) {
|
|
274
|
-
lines.push('**Constraints:**');
|
|
275
|
-
for (const c of context.constraints.slice(0, 6))
|
|
276
|
-
lines.push(`- ${c}`);
|
|
277
|
-
lines.push('');
|
|
278
|
-
}
|
|
279
|
-
if (context.risks.length > 0) {
|
|
280
|
-
lines.push('**Risks to mitigate in this slice:**');
|
|
281
|
-
for (const r of context.risks.slice(0, 4)) {
|
|
282
|
-
lines.push(`- ${r.description}${r.severity ? ` _(${r.severity})_` : ''}`);
|
|
283
|
-
}
|
|
284
|
-
lines.push('');
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
// ─────────────────────────────────────────────────────────────────────
|
|
288
|
-
// ADR-PIPELINE-102 — copy-paste-ready Implementation Prompt block.
|
|
289
|
-
// ─────────────────────────────────────────────────────────────────────
|
|
290
|
-
// The body of every impl-prompt is wrapped in a code fence so a developer
|
|
291
|
-
// (or coding agent) can paste it into a chat verbatim and start
|
|
292
|
-
// building. Inside the fence we include: the specific scope, a list of
|
|
293
|
-
// typed signatures pulled from governing ADR components, invariant
|
|
294
|
-
// references from DDD aggregates, TDD requirements, and verification
|
|
295
|
-
// commands.
|
|
296
|
-
//
|
|
297
|
-
// Reference shape: docs/good_implementation_prompts_example.md (each
|
|
298
|
-
// "## Prompt NN" section has its body wrapped in ``` fences with
|
|
299
|
-
// specific Rust trait signatures, struct fields, and `cargo test`
|
|
300
|
-
// verification commands).
|
|
301
|
-
lines.push('## Implementation Prompt');
|
|
302
|
-
lines.push('');
|
|
303
|
-
lines.push('Copy the block below into your coding agent verbatim. It is a complete build instruction grounded in the governing ADRs/DDDs.');
|
|
304
|
-
lines.push('');
|
|
305
|
-
lines.push('```');
|
|
306
|
-
lines.push(buildCopyPasteReadyPromptBody(unit, unitOrder, totalUnits, context, governingAdrs, governingContexts));
|
|
307
|
-
lines.push('```');
|
|
308
|
-
lines.push('');
|
|
309
|
-
// Reference list — short, pointer-only.
|
|
310
|
-
lines.push('## References');
|
|
311
|
-
lines.push('');
|
|
312
|
-
if (governingAdrs.length > 0) {
|
|
313
|
-
lines.push(`- ADRs: ${governingAdrs.map(a => `\`${a.id}\``).join(', ')}`);
|
|
314
|
-
}
|
|
315
|
-
if (governingContexts.length > 0) {
|
|
316
|
-
lines.push(`- Bounded contexts: ${governingContexts.map(c => `\`${c.name}\``).join(', ')}`);
|
|
317
|
-
}
|
|
318
|
-
if (unit.dependencies.length > 0) {
|
|
319
|
-
lines.push(`- Prior units: ${unit.dependencies.map(d => `\`${d}\``).join(', ')}`);
|
|
320
|
-
}
|
|
321
|
-
lines.push(`- Source: \`${unit.sourceAgent}\` (output index ${unit.sourceIndex})`);
|
|
322
|
-
lines.push('');
|
|
323
|
-
return lines.join('\n');
|
|
324
|
-
}
|
|
325
|
-
/**
|
|
326
|
-
* Build the body that goes inside the copy-paste-ready ``` fence. Mirrors
|
|
327
|
-
* the structure of `docs/good_implementation_prompts_example.md`.
|
|
328
|
-
*/
|
|
329
|
-
function buildCopyPasteReadyPromptBody(unit, unitOrder, totalUnits, context, adrs, contexts) {
|
|
330
|
-
const out = [];
|
|
331
|
-
out.push(`Implement Build Step ${unitOrder} of ${totalUnits}: ${unit.title}.`);
|
|
332
|
-
out.push('');
|
|
333
|
-
out.push(unit.description);
|
|
334
|
-
out.push('');
|
|
335
|
-
if (unit.dependencies.length > 0) {
|
|
336
|
-
out.push(`Depends on (already implemented in prior steps): ${unit.dependencies.join(', ')}.`);
|
|
337
|
-
out.push('Import their public API instead of redefining types.');
|
|
338
|
-
out.push('');
|
|
339
|
-
}
|
|
340
|
-
// Specific items to implement, sourced from the governing ADRs/DDDs.
|
|
341
|
-
if (adrs.length > 0 || contexts.length > 0) {
|
|
342
|
-
out.push('Implement the following types, traits, and modules:');
|
|
343
|
-
out.push('');
|
|
344
|
-
let itemIdx = 0;
|
|
345
|
-
for (const adr of adrs) {
|
|
346
|
-
if (!adr.components || adr.components.length === 0)
|
|
347
|
-
continue;
|
|
348
|
-
for (const comp of adr.components) {
|
|
349
|
-
itemIdx++;
|
|
350
|
-
const desc = comp.description ? ` — ${truncate(comp.description, 200)}` : '';
|
|
351
|
-
out.push(`${itemIdx}. ${comp.name}${desc}`);
|
|
352
|
-
if (comp.codeExample && comp.codeExample.code.trim()) {
|
|
353
|
-
out.push('');
|
|
354
|
-
out.push(' ```' + (comp.codeExample.language || ''));
|
|
355
|
-
for (const codeLine of comp.codeExample.code.trim().split('\n')) {
|
|
356
|
-
out.push(` ${codeLine}`);
|
|
357
|
-
}
|
|
358
|
-
out.push(' ```');
|
|
359
|
-
}
|
|
360
|
-
if (comp.performanceCharacteristics && comp.performanceCharacteristics.length > 0) {
|
|
361
|
-
out.push('');
|
|
362
|
-
out.push(' Performance requirements:');
|
|
363
|
-
for (const p of comp.performanceCharacteristics) {
|
|
364
|
-
out.push(` - ${p}`);
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
out.push('');
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
// DDD aggregates — list invariants the implementation must enforce.
|
|
371
|
-
for (const ctx of contexts) {
|
|
372
|
-
if (!ctx.aggregates || ctx.aggregates.length === 0)
|
|
373
|
-
continue;
|
|
374
|
-
for (const ag of ctx.aggregates) {
|
|
375
|
-
itemIdx++;
|
|
376
|
-
const subjects = [];
|
|
377
|
-
if (ag.entities && ag.entities.length > 0)
|
|
378
|
-
subjects.push(`entities: ${ag.entities.join(', ')}`);
|
|
379
|
-
if (ag.valueObjects && ag.valueObjects.length > 0)
|
|
380
|
-
subjects.push(`value objects: ${ag.valueObjects.join(', ')}`);
|
|
381
|
-
const subjectStr = subjects.length > 0 ? ` (${subjects.join('; ')})` : '';
|
|
382
|
-
out.push(`${itemIdx}. ${ag.name} aggregate${ag.root ? ` rooted at ${ag.root}` : ''}${subjectStr}`);
|
|
383
|
-
if (ag.invariants && ag.invariants.length > 0) {
|
|
384
|
-
out.push('');
|
|
385
|
-
out.push(' Invariants to enforce:');
|
|
386
|
-
for (const inv of ag.invariants) {
|
|
387
|
-
out.push(` - ${inv}`);
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
out.push('');
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
if (itemIdx === 0) {
|
|
394
|
-
// No ADR components / aggregates — fall through to a minimum scope
|
|
395
|
-
// pulled from acceptance criteria.
|
|
396
|
-
out.push('Scope this slice to the acceptance criteria below; define the minimum public API, types, and module layout that satisfies them.');
|
|
397
|
-
out.push('');
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
// Acceptance criteria become explicit "must satisfy" bullets.
|
|
401
|
-
if (unit.acceptanceCriteria.length > 0) {
|
|
402
|
-
out.push('Acceptance criteria (each must be observable in tests or runtime behavior):');
|
|
403
|
-
for (const ac of unit.acceptanceCriteria) {
|
|
404
|
-
out.push(`- ${ac}`);
|
|
405
|
-
}
|
|
406
|
-
out.push('');
|
|
407
|
-
}
|
|
408
|
-
// Load-bearing assumptions become guardrails the implementer must respect.
|
|
409
|
-
if (context.assumptions.length > 0) {
|
|
410
|
-
out.push('Load-bearing assumptions (DO NOT silently violate; if you must, raise the conflict):');
|
|
411
|
-
for (const a of context.assumptions.slice(0, 4)) {
|
|
412
|
-
out.push(`- ${a}`);
|
|
413
|
-
}
|
|
414
|
-
out.push('');
|
|
415
|
-
}
|
|
416
|
-
// System constraints — short and explicit.
|
|
417
|
-
if (context.constraints.length > 0) {
|
|
418
|
-
out.push('System constraints to respect:');
|
|
419
|
-
for (const c of context.constraints.slice(0, 4)) {
|
|
420
|
-
out.push(`- ${c}`);
|
|
421
|
-
}
|
|
422
|
-
out.push('');
|
|
423
|
-
}
|
|
424
|
-
// TDD + production-quality bar.
|
|
425
|
-
out.push('Build expectations:');
|
|
426
|
-
out.push('- Write tests first (London-school TDD). Mock dependencies from prior units; do not call them transitively in unit tests.');
|
|
427
|
-
out.push('- Real domain logic — no stubs, no TODO scaffolds, no placeholder bodies.');
|
|
428
|
-
out.push('- Validate ALL external input with the project\'s validation library (e.g. Zod for TS, serde+validator for Rust, pydantic for Python).');
|
|
429
|
-
out.push('- Public API surface MUST be exported so the next build step can import it. Document the exports.');
|
|
430
|
-
out.push('- Structured logging via the project logger — never console.log/print/println in production paths.');
|
|
431
|
-
out.push('- Zero compilation errors. Zero `any`-typed domain values. Zero `unwrap()` without justification.');
|
|
432
|
-
out.push('');
|
|
433
|
-
// Verification — concrete commands for the implementer to run.
|
|
434
|
-
out.push('Verification before marking this step complete:');
|
|
435
|
-
out.push('- All new tests pass.');
|
|
436
|
-
out.push('- The full unit test suite passes.');
|
|
437
|
-
out.push('- The lint/typecheck pipeline passes.');
|
|
438
|
-
out.push('- Public API surface compiles in isolation (no leaking internal-only types).');
|
|
439
|
-
out.push('');
|
|
440
|
-
// ADR/DDD pointer-only references — keep the prompt body lean.
|
|
441
|
-
if (adrs.length > 0) {
|
|
442
|
-
out.push(`Reference: ${adrs.map(a => `${a.id} (${truncate(a.title, 60)})`).join('; ')}`);
|
|
443
|
-
}
|
|
444
|
-
if (contexts.length > 0) {
|
|
445
|
-
out.push(`Domain context(s): ${contexts.map(c => c.name).join(', ')}`);
|
|
446
|
-
}
|
|
447
|
-
return out.join('\n');
|
|
448
|
-
}
|
|
449
|
-
// ── Helpers ────────────────────────────────────────────────────────────────
|
|
450
|
-
function readAgentResponses(runDir) {
|
|
451
|
-
const candidates = [
|
|
452
|
-
path.join(runDir, 'agent-responses.json'),
|
|
453
|
-
path.join(runDir, 'phase1', 'agent-responses.json'),
|
|
454
|
-
];
|
|
455
|
-
for (const p of candidates) {
|
|
456
|
-
try {
|
|
457
|
-
const raw = fs.readFileSync(p, 'utf-8');
|
|
458
|
-
const parsed = JSON.parse(raw);
|
|
459
|
-
if (parsed && Array.isArray(parsed.agents))
|
|
460
|
-
return parsed.agents;
|
|
461
|
-
}
|
|
462
|
-
catch {
|
|
463
|
-
// try next
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
return [];
|
|
467
|
-
}
|
|
468
|
-
function readScenario(runDir) {
|
|
469
|
-
for (const rel of ['scenario.json', 'phase1/scenario.json']) {
|
|
470
|
-
try {
|
|
471
|
-
const p = path.join(runDir, rel);
|
|
472
|
-
const raw = fs.readFileSync(p, 'utf-8');
|
|
473
|
-
const parsed = JSON.parse(raw);
|
|
474
|
-
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
475
|
-
return parsed;
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
catch { /* try next */ }
|
|
479
|
-
}
|
|
480
|
-
return undefined;
|
|
481
|
-
}
|
|
482
|
-
function readRiskAssessment(runDir) {
|
|
483
|
-
for (const rel of ['risk-assessment.json', 'phase1/risk-assessment.json']) {
|
|
484
|
-
try {
|
|
485
|
-
const p = path.join(runDir, rel);
|
|
486
|
-
const raw = fs.readFileSync(p, 'utf-8');
|
|
487
|
-
const parsed = JSON.parse(raw);
|
|
488
|
-
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
489
|
-
return parsed;
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
catch { /* try next */ }
|
|
493
|
-
}
|
|
494
|
-
return undefined;
|
|
495
|
-
}
|
|
496
|
-
function pickAgent(rows, domain, agent) {
|
|
497
|
-
const row = rows.find(r => r.domain === domain && r.agent === agent && r.status && r.status >= 200 && r.status < 300);
|
|
498
|
-
if (!row || !row.response_truncated)
|
|
499
|
-
return undefined;
|
|
500
|
-
// Strip truncation marker if present.
|
|
501
|
-
let raw = row.response_truncated.trim();
|
|
502
|
-
if (raw.endsWith('... [truncated]')) {
|
|
503
|
-
raw = raw.slice(0, -'... [truncated]'.length);
|
|
504
|
-
}
|
|
505
|
-
// The truncated payload is JSON-encoded; parse defensively.
|
|
506
|
-
try {
|
|
507
|
-
const parsed = JSON.parse(raw);
|
|
508
|
-
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
509
|
-
return parsed;
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
catch {
|
|
513
|
-
// Common case — truncation cut mid-string. Walk back to the last } and retry.
|
|
514
|
-
const lastBrace = raw.lastIndexOf('}');
|
|
515
|
-
if (lastBrace > 0) {
|
|
516
|
-
try {
|
|
517
|
-
const parsed = JSON.parse(raw.slice(0, lastBrace + 1));
|
|
518
|
-
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
519
|
-
return parsed;
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
catch { /* give up */ }
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
return undefined;
|
|
526
|
-
}
|
|
527
|
-
function extractStringArray(obj, keys) {
|
|
528
|
-
if (!obj)
|
|
529
|
-
return undefined;
|
|
530
|
-
for (const key of keys) {
|
|
531
|
-
// Direct array of strings.
|
|
532
|
-
const v = obj[key];
|
|
533
|
-
if (Array.isArray(v)) {
|
|
534
|
-
const strings = [];
|
|
535
|
-
for (const item of v) {
|
|
536
|
-
if (typeof item === 'string')
|
|
537
|
-
strings.push(item);
|
|
538
|
-
else if (item && typeof item === 'object') {
|
|
539
|
-
// Could be {description: ...} or {assumption: ...} etc.
|
|
540
|
-
const o = item;
|
|
541
|
-
for (const subKey of ['description', 'assumption', 'text', 'criterion', 'constraint', 'name', 'title']) {
|
|
542
|
-
if (typeof o[subKey] === 'string') {
|
|
543
|
-
strings.push(o[subKey]);
|
|
544
|
-
break;
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
if (strings.length > 0)
|
|
550
|
-
return strings;
|
|
551
|
-
}
|
|
552
|
-
// Sometimes nested under outputs.
|
|
553
|
-
const outputs = obj['outputs'];
|
|
554
|
-
if (outputs && typeof outputs === 'object' && !Array.isArray(outputs)) {
|
|
555
|
-
const nested = outputs[key];
|
|
556
|
-
if (Array.isArray(nested)) {
|
|
557
|
-
const strings = [];
|
|
558
|
-
for (const item of nested) {
|
|
559
|
-
if (typeof item === 'string')
|
|
560
|
-
strings.push(item);
|
|
561
|
-
else if (item && typeof item === 'object') {
|
|
562
|
-
const o = item;
|
|
563
|
-
for (const subKey of ['description', 'assumption', 'text', 'criterion', 'constraint', 'name', 'title']) {
|
|
564
|
-
if (typeof o[subKey] === 'string') {
|
|
565
|
-
strings.push(o[subKey]);
|
|
566
|
-
break;
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
if (strings.length > 0)
|
|
572
|
-
return strings;
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
return undefined;
|
|
577
|
-
}
|
|
578
|
-
function extractObjectArray(obj, keys) {
|
|
579
|
-
if (!obj)
|
|
580
|
-
return undefined;
|
|
581
|
-
for (const key of keys) {
|
|
582
|
-
const v = obj[key];
|
|
583
|
-
if (Array.isArray(v))
|
|
584
|
-
return v;
|
|
585
|
-
const outputs = obj['outputs'];
|
|
586
|
-
if (outputs && typeof outputs === 'object' && !Array.isArray(outputs)) {
|
|
587
|
-
const nested = outputs[key];
|
|
588
|
-
if (Array.isArray(nested))
|
|
589
|
-
return nested;
|
|
590
|
-
}
|
|
591
|
-
}
|
|
592
|
-
return undefined;
|
|
593
|
-
}
|
|
594
|
-
function extractRiskRegister(risk) {
|
|
595
|
-
if (!risk)
|
|
596
|
-
return [];
|
|
597
|
-
const candidates = [];
|
|
598
|
-
for (const key of ['risk_register', 'risks', 'register']) {
|
|
599
|
-
const v = risk[key];
|
|
600
|
-
if (Array.isArray(v))
|
|
601
|
-
candidates.push(...v);
|
|
602
|
-
}
|
|
603
|
-
const out = [];
|
|
604
|
-
for (const item of candidates) {
|
|
605
|
-
if (typeof item === 'string') {
|
|
606
|
-
out.push({ description: item });
|
|
607
|
-
}
|
|
608
|
-
else if (item && typeof item === 'object') {
|
|
609
|
-
const o = item;
|
|
610
|
-
const desc = o['description'] ?? o['risk'] ?? o['name'] ?? o['title'];
|
|
611
|
-
if (typeof desc === 'string' && desc.trim().length > 0) {
|
|
612
|
-
const sev = o['severity'] ?? o['impact'] ?? o['priority'];
|
|
613
|
-
out.push({
|
|
614
|
-
description: desc,
|
|
615
|
-
severity: typeof sev === 'string' ? sev : undefined,
|
|
616
|
-
});
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
return out;
|
|
621
|
-
}
|
|
622
|
-
function parseComplexity(v) {
|
|
623
|
-
if (typeof v === 'number' && Number.isFinite(v)) {
|
|
624
|
-
return Math.max(1, Math.min(5, Math.round(v)));
|
|
625
|
-
}
|
|
626
|
-
if (typeof v === 'string') {
|
|
627
|
-
const lower = v.toLowerCase().trim();
|
|
628
|
-
if (lower === 'low' || lower === 'small' || lower === 'simple')
|
|
629
|
-
return 2;
|
|
630
|
-
if (lower === 'medium' || lower === 'moderate')
|
|
631
|
-
return 3;
|
|
632
|
-
if (lower === 'high' || lower === 'large' || lower === 'complex')
|
|
633
|
-
return 4;
|
|
634
|
-
if (lower === 'critical' || lower === 'extreme')
|
|
635
|
-
return 5;
|
|
636
|
-
const n = Number.parseInt(lower, 10);
|
|
637
|
-
if (!Number.isNaN(n))
|
|
638
|
-
return Math.max(1, Math.min(5, n));
|
|
639
|
-
}
|
|
640
|
-
return 3;
|
|
641
|
-
}
|
|
642
|
-
function classifyTier(title, description) {
|
|
643
|
-
const t = `${title} ${description}`.toLowerCase();
|
|
644
|
-
if (/(foundation|setup|bootstrap|domain model|persistence|database|schema|infrastructure|core types|baseline)/.test(t)) {
|
|
645
|
-
return 'foundation';
|
|
646
|
-
}
|
|
647
|
-
if (/(integration|webhook|erp|sap|oracle|salesforce|servicenow|api client|adapter|connector|external)/.test(t)) {
|
|
648
|
-
return 'integration';
|
|
649
|
-
}
|
|
650
|
-
if (/(ui|dashboard|frontend|react|vue|svelte|page|component|presentation|report|visualization)/.test(t)) {
|
|
651
|
-
return 'presentation';
|
|
652
|
-
}
|
|
653
|
-
if (/(test|validation|smoke|hypercare|pilot|qa|verify|verification)/.test(t)) {
|
|
654
|
-
return 'validation';
|
|
655
|
-
}
|
|
656
|
-
return 'domain';
|
|
657
|
-
}
|
|
658
|
-
function resolveDeps(raw, existing) {
|
|
659
|
-
if (!Array.isArray(raw))
|
|
660
|
-
return [];
|
|
661
|
-
const out = [];
|
|
662
|
-
for (const r of raw) {
|
|
663
|
-
if (typeof r === 'number' && Number.isInteger(r) && r > 0 && r <= existing.length) {
|
|
664
|
-
out.push(existing[r - 1].id);
|
|
665
|
-
}
|
|
666
|
-
else if (typeof r === 'string') {
|
|
667
|
-
// Try to match an existing unit by slug or id substring.
|
|
668
|
-
const slug = slugify(r);
|
|
669
|
-
const match = existing.find(u => u.slug === slug || u.id.endsWith(slug) || u.id === r);
|
|
670
|
-
if (match)
|
|
671
|
-
out.push(match.id);
|
|
672
|
-
else
|
|
673
|
-
out.push(slug);
|
|
674
|
-
}
|
|
675
|
-
}
|
|
676
|
-
return out;
|
|
677
|
-
}
|
|
678
|
-
function pad3(n) {
|
|
679
|
-
return String(n).padStart(3, '0');
|
|
680
|
-
}
|
|
681
|
-
function slugify(text) {
|
|
682
|
-
return text.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)/g, '').slice(0, 60);
|
|
683
|
-
}
|
|
684
|
-
function truncate(text, max) {
|
|
685
|
-
if (!text)
|
|
686
|
-
return '';
|
|
687
|
-
return text.length > max ? text.slice(0, max - 1) + '…' : text;
|
|
688
|
-
}
|
|
689
|
-
function escapeYamlString(s) {
|
|
690
|
-
// Conservative — wrap in quotes if it contains anything that could confuse a YAML parser.
|
|
691
|
-
if (/[:#&*!|>'"%@`?[\]{},]/.test(s)) {
|
|
692
|
-
return `"${s.replace(/"/g, '\\"').replace(/\\/g, '\\\\')}"`;
|
|
693
|
-
}
|
|
694
|
-
return s;
|
|
695
|
-
}
|
|
696
|
-
//# sourceMappingURL=agent-fleet-decomposer.js.map
|