@llm-dev-ops/agentics-cli 2.7.31 → 2.7.33
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/pipeline/local-fallback/phase5a-local-fallback.js +140 -28
- package/dist/pipeline/local-fallback/phase5a-local-fallback.js.map +1 -1
- package/dist/pipeline/phase2/phases/adr-generator.d.ts.map +1 -1
- package/dist/pipeline/phase2/phases/adr-generator.js +291 -49
- package/dist/pipeline/phase2/phases/adr-generator.js.map +1 -1
- package/dist/pipeline/phase2/types.d.ts +57 -0
- package/dist/pipeline/phase2/types.d.ts.map +1 -1
- package/dist/synthesis/agent-fleet-decomposer.d.ts +40 -16
- package/dist/synthesis/agent-fleet-decomposer.d.ts.map +1 -1
- package/dist/synthesis/agent-fleet-decomposer.js +163 -17
- package/dist/synthesis/agent-fleet-decomposer.js.map +1 -1
- package/package.json +1 -1
|
@@ -70,48 +70,176 @@ function findDossierRefs(dossier, keywords) {
|
|
|
70
70
|
// ============================================================================
|
|
71
71
|
// ADR Markdown Renderer
|
|
72
72
|
// ============================================================================
|
|
73
|
+
/**
|
|
74
|
+
* Render a Phase2ADRRecord as a markdown document.
|
|
75
|
+
*
|
|
76
|
+
* ADR-PIPELINE-101 — when the LLM-generated record carries the rich-content
|
|
77
|
+
* fields (architectureDiagram, components, performanceTargets, references,
|
|
78
|
+
* appendices, implementationStatus), the renderer interleaves them into a
|
|
79
|
+
* structure that mirrors `docs/good_ADR_example.md`. When those fields are
|
|
80
|
+
* absent (deterministic fallback path), it produces the legacy thin format
|
|
81
|
+
* for backwards compatibility.
|
|
82
|
+
*/
|
|
73
83
|
function renderADRMarkdown(adr) {
|
|
74
84
|
const lines = [];
|
|
75
85
|
lines.push(`# ${adr.id}: ${adr.title}`);
|
|
76
86
|
lines.push('');
|
|
77
87
|
lines.push(`**Status:** ${adr.status}`);
|
|
78
88
|
lines.push(`**Date:** ${adr.date}`);
|
|
89
|
+
if (adr.authors && adr.authors.length > 0) {
|
|
90
|
+
lines.push(`**Authors:** ${adr.authors.join(', ')}`);
|
|
91
|
+
}
|
|
92
|
+
if (adr.deciders && adr.deciders.length > 0) {
|
|
93
|
+
lines.push(`**Deciders:** ${adr.deciders.join(', ')}`);
|
|
94
|
+
}
|
|
95
|
+
if (adr.supersedes) {
|
|
96
|
+
lines.push('');
|
|
97
|
+
lines.push(`**Note:** This ADR supersedes ${adr.supersedes}.`);
|
|
98
|
+
}
|
|
99
|
+
lines.push('');
|
|
100
|
+
lines.push('---');
|
|
79
101
|
lines.push('');
|
|
102
|
+
// ── Context ─────────────────────────────────────────────────────────
|
|
80
103
|
lines.push('## Context');
|
|
104
|
+
lines.push('');
|
|
81
105
|
lines.push(adr.context);
|
|
82
106
|
lines.push('');
|
|
107
|
+
// ── Decision ────────────────────────────────────────────────────────
|
|
83
108
|
lines.push('## Decision');
|
|
109
|
+
lines.push('');
|
|
84
110
|
lines.push(adr.decision);
|
|
85
111
|
lines.push('');
|
|
112
|
+
// ── Architecture diagram (if provided) ──────────────────────────────
|
|
113
|
+
if (adr.architectureDiagram && adr.architectureDiagram.trim()) {
|
|
114
|
+
lines.push('### Architecture');
|
|
115
|
+
lines.push('');
|
|
116
|
+
lines.push('```');
|
|
117
|
+
lines.push(adr.architectureDiagram.trim());
|
|
118
|
+
lines.push('```');
|
|
119
|
+
lines.push('');
|
|
120
|
+
}
|
|
121
|
+
// ── Key Components ──────────────────────────────────────────────────
|
|
122
|
+
if (adr.components && adr.components.length > 0) {
|
|
123
|
+
lines.push('## Key Components');
|
|
124
|
+
lines.push('');
|
|
125
|
+
let idx = 0;
|
|
126
|
+
for (const comp of adr.components) {
|
|
127
|
+
idx++;
|
|
128
|
+
lines.push(`### ${idx}. ${comp.name}`);
|
|
129
|
+
lines.push('');
|
|
130
|
+
lines.push(comp.description);
|
|
131
|
+
lines.push('');
|
|
132
|
+
if (comp.codeExample && comp.codeExample.code.trim()) {
|
|
133
|
+
lines.push('```' + (comp.codeExample.language || ''));
|
|
134
|
+
lines.push(comp.codeExample.code.trim());
|
|
135
|
+
lines.push('```');
|
|
136
|
+
lines.push('');
|
|
137
|
+
}
|
|
138
|
+
if (comp.configTable && comp.configTable.rows.length > 0) {
|
|
139
|
+
lines.push(renderTable(comp.configTable.headers, comp.configTable.rows));
|
|
140
|
+
lines.push('');
|
|
141
|
+
}
|
|
142
|
+
if (comp.performanceCharacteristics && comp.performanceCharacteristics.length > 0) {
|
|
143
|
+
lines.push('**Performance characteristics:**');
|
|
144
|
+
lines.push('');
|
|
145
|
+
for (const p of comp.performanceCharacteristics)
|
|
146
|
+
lines.push(`- ${p}`);
|
|
147
|
+
lines.push('');
|
|
148
|
+
}
|
|
149
|
+
if (comp.securityNotes && comp.securityNotes.length > 0) {
|
|
150
|
+
lines.push('**Security guarantees:**');
|
|
151
|
+
lines.push('');
|
|
152
|
+
for (const s of comp.securityNotes)
|
|
153
|
+
lines.push(`- ${s}`);
|
|
154
|
+
lines.push('');
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
// ── Performance Targets (top-level table) ───────────────────────────
|
|
159
|
+
if (adr.performanceTargets && adr.performanceTargets.rows.length > 0) {
|
|
160
|
+
lines.push('## Performance Targets');
|
|
161
|
+
lines.push('');
|
|
162
|
+
lines.push(renderTable(adr.performanceTargets.headers, adr.performanceTargets.rows));
|
|
163
|
+
lines.push('');
|
|
164
|
+
}
|
|
165
|
+
// ── Alternatives ────────────────────────────────────────────────────
|
|
86
166
|
if (adr.alternatives.length > 0) {
|
|
87
167
|
lines.push('## Alternatives Considered');
|
|
168
|
+
lines.push('');
|
|
88
169
|
for (const alt of adr.alternatives) {
|
|
89
170
|
const status = alt.rejected ? '(rejected)' : '(selected)';
|
|
90
171
|
lines.push(`- **${alt.option}** ${status}: ${alt.rationale}`);
|
|
91
172
|
}
|
|
92
173
|
lines.push('');
|
|
93
174
|
}
|
|
175
|
+
// ── Consequences ────────────────────────────────────────────────────
|
|
94
176
|
if (adr.consequences.length > 0) {
|
|
95
177
|
lines.push('## Consequences');
|
|
178
|
+
lines.push('');
|
|
96
179
|
for (const c of adr.consequences) {
|
|
97
180
|
const icon = c.type === 'positive' ? '+' : c.type === 'negative' ? '-' : '~';
|
|
98
181
|
lines.push(`- [${icon}] ${c.description}`);
|
|
99
182
|
}
|
|
100
183
|
lines.push('');
|
|
101
184
|
}
|
|
102
|
-
|
|
185
|
+
// ── Implementation Status ───────────────────────────────────────────
|
|
186
|
+
if (adr.implementationStatus && adr.implementationStatus.rows.length > 0) {
|
|
187
|
+
lines.push('## Implementation Status');
|
|
188
|
+
lines.push('');
|
|
189
|
+
lines.push(renderTable(adr.implementationStatus.headers, adr.implementationStatus.rows));
|
|
190
|
+
lines.push('');
|
|
191
|
+
}
|
|
192
|
+
// ── References (numbered) ───────────────────────────────────────────
|
|
193
|
+
if (adr.references && adr.references.length > 0) {
|
|
103
194
|
lines.push('## References');
|
|
104
|
-
lines.push(`Dossier items: ${adr.researchDossierItemRefs.join(', ')}`);
|
|
105
195
|
lines.push('');
|
|
196
|
+
let r = 0;
|
|
197
|
+
for (const ref of adr.references) {
|
|
198
|
+
r++;
|
|
199
|
+
lines.push(`${r}. ${ref}`);
|
|
200
|
+
}
|
|
201
|
+
lines.push('');
|
|
202
|
+
}
|
|
203
|
+
// ── Dossier item refs (kept as a separate section so they don't bury
|
|
204
|
+
// the formal references) ───────────────────────────────────────────
|
|
205
|
+
if (adr.researchDossierItemRefs.length > 0) {
|
|
206
|
+
lines.push('## Research Dossier Source Items');
|
|
207
|
+
lines.push('');
|
|
208
|
+
lines.push(adr.researchDossierItemRefs.map(r => `\`${r}\``).join(', '));
|
|
209
|
+
lines.push('');
|
|
210
|
+
}
|
|
211
|
+
// ── Appendices ──────────────────────────────────────────────────────
|
|
212
|
+
if (adr.appendices && adr.appendices.length > 0) {
|
|
213
|
+
let letterCode = 'A'.charCodeAt(0);
|
|
214
|
+
for (const apx of adr.appendices) {
|
|
215
|
+
lines.push(`## Appendix ${String.fromCharCode(letterCode)}: ${apx.title}`);
|
|
216
|
+
lines.push('');
|
|
217
|
+
lines.push(apx.body);
|
|
218
|
+
lines.push('');
|
|
219
|
+
letterCode++;
|
|
220
|
+
}
|
|
106
221
|
}
|
|
107
222
|
// ADR-PIPELINE-018: Include simulation lineage when present
|
|
108
223
|
if (adr.simulationId) {
|
|
109
224
|
lines.push('## Lineage');
|
|
110
|
-
lines.push(
|
|
225
|
+
lines.push('');
|
|
226
|
+
lines.push(`Originating simulation: \`${adr.simulationId}\``);
|
|
111
227
|
lines.push('');
|
|
112
228
|
}
|
|
113
229
|
return lines.join('\n');
|
|
114
230
|
}
|
|
231
|
+
/** Render a markdown table — header row + separator + data rows. */
|
|
232
|
+
function renderTable(headers, rows) {
|
|
233
|
+
const lines = [];
|
|
234
|
+
lines.push(`| ${headers.join(' | ')} |`);
|
|
235
|
+
lines.push(`|${headers.map(() => '---').join('|')}|`);
|
|
236
|
+
for (const row of rows) {
|
|
237
|
+
// Pad/truncate to header length so the table stays valid.
|
|
238
|
+
const padded = headers.map((_, i) => row[i] ?? '');
|
|
239
|
+
lines.push(`| ${padded.join(' | ')} |`);
|
|
240
|
+
}
|
|
241
|
+
return lines.join('\n');
|
|
242
|
+
}
|
|
115
243
|
// ============================================================================
|
|
116
244
|
// ADR Builder — derived from SPARC + dossier
|
|
117
245
|
// ============================================================================
|
|
@@ -1178,6 +1306,7 @@ function tryGenerateLLMADRs(sparc, dossier, query, phase2Dir = '') {
|
|
|
1178
1306
|
const startMs = Date.now();
|
|
1179
1307
|
// Use --print for one-shot generation (no MCP, no interactive session)
|
|
1180
1308
|
const model = process.env['AGENTICS_CLAUDE_MODEL'] || 'claude-sonnet-4-20250514';
|
|
1309
|
+
process.stderr.write(`[adr-generator] Invoking ${claudeBin} (${model}) for rich ADR generation — up to 6 minutes\n`);
|
|
1181
1310
|
const rawOutput = execFileSync(claudeBin, [
|
|
1182
1311
|
'--print',
|
|
1183
1312
|
'--output-format', 'text',
|
|
@@ -1185,8 +1314,11 @@ function tryGenerateLLMADRs(sparc, dossier, query, phase2Dir = '') {
|
|
|
1185
1314
|
prompt,
|
|
1186
1315
|
], {
|
|
1187
1316
|
encoding: 'utf-8',
|
|
1188
|
-
|
|
1189
|
-
|
|
1317
|
+
// ADR-PIPELINE-101 — rich ADRs (architecture diagrams + per-component
|
|
1318
|
+
// code examples + appendices) need substantially more output than the
|
|
1319
|
+
// pre-101 thin format. Bump to 6 minutes.
|
|
1320
|
+
timeout: 360_000,
|
|
1321
|
+
maxBuffer: 32 * 1024 * 1024,
|
|
1190
1322
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
1191
1323
|
env: { ...process.env, MCP_SERVER_MODE: undefined }, // Clear MCP flag for subprocess
|
|
1192
1324
|
});
|
|
@@ -1238,54 +1370,80 @@ function buildADRPrompt(sparc, dossier, query, phase2Dir = '') {
|
|
|
1238
1370
|
const erpPromptContext = techStack.erp
|
|
1239
1371
|
? `\nERP API DETAILS (use these EXACT paths):\n${formatERPContextForPrompt(techStack.erp)}\n`
|
|
1240
1372
|
: '';
|
|
1241
|
-
return (`You are a senior
|
|
1373
|
+
return (`You are a senior staff architect writing detailed Architecture Decision Records (ADRs) for a real engineering team.\n\n` +
|
|
1374
|
+
`Each ADR you produce MUST be a long-form, implementation-grade document that includes ASCII architecture diagrams, named components with code examples, configuration tables, performance targets, security guarantees, references, and appendices — NOT a 5-bullet skeleton. A developer must be able to take ONE of your ADRs and build the slice it describes without asking follow-up questions. Reference shape: 400-800 lines of markdown per ADR when rendered.\n\n` +
|
|
1242
1375
|
`## Project Requirements\n\n${query}\n\n` +
|
|
1243
1376
|
`## Services Identified\n${serviceList}\n` +
|
|
1244
|
-
`\
|
|
1245
|
-
(agentContext ? agentContext + '\n' : '') +
|
|
1246
|
-
`\
|
|
1377
|
+
`\n## Research Findings\n${dossierContext}\n` +
|
|
1378
|
+
(agentContext ? '\n' + agentContext + '\n' : '') +
|
|
1379
|
+
`\n## Detected Technologies\n${techMentions.join(', ') || 'not specified'}\n` +
|
|
1247
1380
|
erpPromptContext +
|
|
1248
|
-
`\
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
` - How to model the core domain concepts (e.g., "Model emissions as facility-level time series with BU rollup")\n` +
|
|
1252
|
-
` - How to implement the key algorithms/calculations (scoring, ranking, normalization, weighting)\n` +
|
|
1253
|
-
` - How to structure the decision/simulation/optimization logic\n` +
|
|
1254
|
-
` - How domain entities relate and what business rules constrain them\n` +
|
|
1255
|
-
` - How to handle the specific workflows the user described\n` +
|
|
1256
|
-
` - CRITICAL: Each non-obvious algorithmic choice gets its OWN ADR:\n` +
|
|
1257
|
-
` * Scoring/ranking algorithms (normalization method, weight handling, rounding)\n` +
|
|
1258
|
-
` * Routing/filtering strategies (viability thresholds, mode selection, split ratios)\n` +
|
|
1259
|
-
` * Factor sourcing (emissions factors, cost multipliers, regional variations)\n` +
|
|
1260
|
-
` * Threshold models (SLA tolerances, priority levels, constraint buffers)\n` +
|
|
1261
|
-
` * Estimation fallbacks (distance estimation for unknown routes, default handling)\n` +
|
|
1262
|
-
` * Immutability/audit patterns (comparison persistence, content hashing, lineage)\n` +
|
|
1263
|
-
`\nB) INFRASTRUCTURE DECISIONS (3-5) — technology choices that support the domain:\n` +
|
|
1264
|
-
` - Data persistence: which database(s) and why\n` +
|
|
1265
|
-
` - ERP integration approach for ${techMentions[0] || 'the ERP system'}\n` +
|
|
1266
|
-
` - Deployment and cloud platform specifics\n` +
|
|
1267
|
-
` - Security and compliance requirements\n` +
|
|
1268
|
-
`\nEach ADR title should describe a SPECIFIC decision, not a category. Example:\n` +
|
|
1269
|
-
` GOOD: "Use Monte Carlo simulation for carbon price forecasting with configurable volatility"\n` +
|
|
1270
|
-
` BAD: "Performance Architecture"\n` +
|
|
1271
|
-
` GOOD: "Model emissions baseline as facility-level time series with business unit rollup"\n` +
|
|
1272
|
-
` BAD: "Data persistence strategy"\n` +
|
|
1273
|
-
`\nReturn ONLY a JSON array (no markdown fences, no explanation before or after):\n` +
|
|
1381
|
+
`\n## Output Contract\n\n` +
|
|
1382
|
+
`Return ONLY a JSON array of 8-12 ADRs (no markdown fences, no preamble). Each ADR object MUST have this shape:\n` +
|
|
1383
|
+
'```json\n' +
|
|
1274
1384
|
`[\n` +
|
|
1275
1385
|
` {\n` +
|
|
1276
|
-
` "title": "Specific decision title
|
|
1277
|
-
` "
|
|
1278
|
-
` "
|
|
1279
|
-
` "
|
|
1280
|
-
` "
|
|
1386
|
+
` "title": "Specific decision title — what the developer is building (e.g. 'Model water usage as facility-level 15-min time series with cooling-zone partitioning and weather correlation')",\n` +
|
|
1387
|
+
` "authors": ["Solution Architecture", "Sustainability Operations"],\n` +
|
|
1388
|
+
` "deciders": ["Architecture Review Board"],\n` +
|
|
1389
|
+
` "context": "3-6 paragraphs. Sub-section the problem space (the challenge, current state, target state). Cite specific requirements from the brief. NEVER one paragraph.",\n` +
|
|
1390
|
+
` "decision": "3-6 paragraphs. State the chosen approach concretely with named modules, schemas, thresholds. Cite ERP API paths if applicable. NEVER one sentence.",\n` +
|
|
1391
|
+
` "architectureDiagram": "ASCII box diagram showing how data/control flows through this slice. 8-15 lines. Use +-+, |, ->, etc.",\n` +
|
|
1392
|
+
` "components": [\n` +
|
|
1393
|
+
` {\n` +
|
|
1394
|
+
` "name": "Concrete module/class name (e.g. 'WaterUsageIngestor', 'CoolingScenarioEvaluator')",\n` +
|
|
1395
|
+
` "description": "1-2 paragraphs explaining what this component does and why it exists.",\n` +
|
|
1396
|
+
` "codeExample": { "language": "typescript", "code": "// 5-25 lines of REAL working code — interface, function signature, schema, query, etc. Not pseudo-code." },\n` +
|
|
1397
|
+
` "configTable": { "headers": ["Parameter", "Default", "Description"], "rows": [["window_size_min", "15", "Aggregation window for time-series rollup"], ["..."], ["..."]] },\n` +
|
|
1398
|
+
` "performanceCharacteristics": ["Bullet list of measurable perf claims (e.g. 'p99 ingest latency < 200ms at 50K events/sec')"],\n` +
|
|
1399
|
+
` "securityNotes": ["Bullet list of threat-model notes (e.g. 'Approval signatures use Ed25519, stored in HSM-backed key vault')"]\n` +
|
|
1400
|
+
` }\n` +
|
|
1401
|
+
` ],\n` +
|
|
1402
|
+
` "performanceTargets": { "headers": ["Metric", "Target", "Measurement"], "rows": [["Ingestion throughput", ">= 50K events/sec", "Per region, sustained 1h"], ["Query p95", "< 500ms", "Facility-level rollup, last 7 days"]] },\n` +
|
|
1403
|
+
` "alternatives": [\n` +
|
|
1404
|
+
` { "option": "Concrete alternative name (not 'Approach A')", "rationale": "Why it was considered and why rejected/selected. 1-3 sentences.", "rejected": true }\n` +
|
|
1405
|
+
` ],\n` +
|
|
1406
|
+
` "consequences": [\n` +
|
|
1407
|
+
` { "description": "Concrete consequence — what becomes easier/harder/possible/impossible. Reference specific downstream impact.", "type": "positive" }\n` +
|
|
1408
|
+
` ],\n` +
|
|
1409
|
+
` "implementationStatus": { "headers": ["Component", "Status", "Notes"], "rows": [["WaterUsageIngestor", "Planned", "Spec complete, awaiting Phase 5"], ["CoolingScenarioEvaluator", "Planned", "Depends on factor library"]] },\n` +
|
|
1410
|
+
` "references": [\n` +
|
|
1411
|
+
` "ASHRAE TC 9.9 — Thermal Guidelines for Data Processing Environments (2021)",\n` +
|
|
1412
|
+
` "ServiceNow Sustainability Management — Water Use API: https://developer.servicenow.com/...",\n` +
|
|
1413
|
+
` "Belady, C. — Carbon-Aware Computing in Hyperscale Data Centers (2022)"\n` +
|
|
1414
|
+
` ],\n` +
|
|
1415
|
+
` "appendices": [\n` +
|
|
1416
|
+
` { "title": "ServiceNow Field Mapping", "body": "Markdown table or prose mapping ADR concepts to ServiceNow CMDB tables (cmdb_ci_facility, sn_sustain_*) with example record shapes." },\n` +
|
|
1417
|
+
` { "title": "Sample Query", "body": "A realistic query/snippet showing how this ADR's slice is exercised end-to-end. Fenced code block." }\n` +
|
|
1418
|
+
` ]\n` +
|
|
1281
1419
|
` }\n` +
|
|
1282
1420
|
`]\n` +
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1421
|
+
'```\n' +
|
|
1422
|
+
`\n## Coverage Requirements\n\n` +
|
|
1423
|
+
`A) DOMAIN-SPECIFIC DECISIONS (at least 5):\n` +
|
|
1424
|
+
` - How to model the core domain (data shape, partitioning, time semantics)\n` +
|
|
1425
|
+
` - How to implement key algorithms (scoring, anomaly detection, scenario comparison, recommendation ranking)\n` +
|
|
1426
|
+
` - How decision/simulation/optimization logic is structured\n` +
|
|
1427
|
+
` - How domain entities relate and what business rules constrain them\n` +
|
|
1428
|
+
` - How specific user-described workflows are wired end-to-end\n` +
|
|
1429
|
+
` Each non-obvious algorithmic choice gets its OWN ADR: scoring/normalization, threshold models, factor sourcing, estimation fallbacks, immutability/audit patterns.\n\n` +
|
|
1430
|
+
`B) INFRASTRUCTURE DECISIONS (3-5):\n` +
|
|
1431
|
+
` - Data persistence (which database, why, schema sketch)\n` +
|
|
1432
|
+
` - ERP integration for ${techMentions[0] || 'the named ERP'} (concrete API paths, idempotency, retry, rate limits)\n` +
|
|
1433
|
+
` - Deployment + cloud platform specifics\n` +
|
|
1434
|
+
` - Security, compliance, audit trail (RBAC, separation of duties, hash chain)\n\n` +
|
|
1435
|
+
`## Critical Rules\n\n` +
|
|
1436
|
+
`- NO generic titles like "Security Architecture", "Performance Architecture", "Service Boundary".\n` +
|
|
1437
|
+
`- Every ADR's "decision" field is 3-6 paragraphs, NEVER one sentence.\n` +
|
|
1438
|
+
`- "context" is 3-6 paragraphs explaining the problem space + current state + target state.\n` +
|
|
1439
|
+
`- "components" has 2-5 entries per ADR, each with a real code example.\n` +
|
|
1440
|
+
`- "appendices" has at least 1 entry (e.g. ERP field mapping, sample queries, threshold tables).\n` +
|
|
1441
|
+
`- Reference actual domain concepts from the brief — never generic "entities" or "services".\n` +
|
|
1442
|
+
`- If the user named technologies (${techMentions.join(', ')}), every relevant ADR cites them BY NAME with concrete API paths.\n` +
|
|
1443
|
+
`- ASCII diagrams MUST show real component names from this project, not abstract boxes labelled "Service A".\n` +
|
|
1444
|
+
`- Code examples MUST compile (or be syntactically valid pseudo-code with real types).\n` +
|
|
1445
|
+
`- References MUST include at least one industry standard / RFC / vendor doc URL where applicable.\n\n` +
|
|
1446
|
+
`Return ONLY the JSON array. No prose before or after. No markdown fences around the array.`);
|
|
1289
1447
|
}
|
|
1290
1448
|
/**
|
|
1291
1449
|
* Parse raw LLM JSON response into Phase2ADRRecord[].
|
|
@@ -1326,6 +1484,7 @@ function parseRawADRs(rawText, dossier) {
|
|
|
1326
1484
|
const cons = Array.isArray(raw.consequences)
|
|
1327
1485
|
? raw.consequences.map(c => makeCons(String(c.description || ''), (c.type || 'neutral')))
|
|
1328
1486
|
: [makeCons('Decision applied', 'neutral')];
|
|
1487
|
+
// ADR-PIPELINE-101: thread the rich-content fields if the LLM produced them.
|
|
1329
1488
|
return {
|
|
1330
1489
|
id: `ADR-P2-${pad3(idx + 1)}`,
|
|
1331
1490
|
title: String(raw.title || `Decision ${idx + 1}`),
|
|
@@ -1339,9 +1498,90 @@ function parseRawADRs(rawText, dossier) {
|
|
|
1339
1498
|
researchDossierItemRefs: findDossierRefs(dossier, [
|
|
1340
1499
|
String(raw.title || '').toLowerCase(),
|
|
1341
1500
|
]),
|
|
1501
|
+
...parseRichADRFields(raw),
|
|
1342
1502
|
};
|
|
1343
1503
|
});
|
|
1344
1504
|
}
|
|
1505
|
+
/**
|
|
1506
|
+
* Extract the optional ADR-PIPELINE-101 rich-content fields (architectureDiagram,
|
|
1507
|
+
* components, performanceTargets, references, appendices, implementationStatus,
|
|
1508
|
+
* authors, deciders) from an LLM JSON object. Returns a partial object — caller
|
|
1509
|
+
* spreads it onto the base record so missing fields stay undefined.
|
|
1510
|
+
*/
|
|
1511
|
+
function parseRichADRFields(raw) {
|
|
1512
|
+
const out = {};
|
|
1513
|
+
if (Array.isArray(raw.authors)) {
|
|
1514
|
+
out.authors = raw.authors.map(String).filter(Boolean);
|
|
1515
|
+
}
|
|
1516
|
+
if (Array.isArray(raw.deciders)) {
|
|
1517
|
+
out.deciders = raw.deciders.map(String).filter(Boolean);
|
|
1518
|
+
}
|
|
1519
|
+
if (typeof raw.architectureDiagram === 'string' && raw.architectureDiagram.trim()) {
|
|
1520
|
+
out.architectureDiagram = raw.architectureDiagram;
|
|
1521
|
+
}
|
|
1522
|
+
if (Array.isArray(raw.references)) {
|
|
1523
|
+
out.references = raw.references.map(String).filter(Boolean);
|
|
1524
|
+
}
|
|
1525
|
+
const pt = parseTable(raw.performanceTargets);
|
|
1526
|
+
if (pt)
|
|
1527
|
+
out.performanceTargets = pt;
|
|
1528
|
+
const is = parseTable(raw.implementationStatus);
|
|
1529
|
+
if (is)
|
|
1530
|
+
out.implementationStatus = is;
|
|
1531
|
+
if (Array.isArray(raw.appendices)) {
|
|
1532
|
+
out.appendices = raw.appendices
|
|
1533
|
+
.filter((a) => a !== null && typeof a === 'object')
|
|
1534
|
+
.map(a => ({
|
|
1535
|
+
title: String(a['title'] ?? ''),
|
|
1536
|
+
body: String(a['body'] ?? ''),
|
|
1537
|
+
}))
|
|
1538
|
+
.filter(a => a.title && a.body);
|
|
1539
|
+
}
|
|
1540
|
+
if (Array.isArray(raw.components)) {
|
|
1541
|
+
out.components = raw.components
|
|
1542
|
+
.filter((c) => c !== null && typeof c === 'object')
|
|
1543
|
+
.map(c => {
|
|
1544
|
+
const compOut = {
|
|
1545
|
+
name: String(c['name'] ?? ''),
|
|
1546
|
+
description: String(c['description'] ?? ''),
|
|
1547
|
+
};
|
|
1548
|
+
const ce = c['codeExample'];
|
|
1549
|
+
if (ce && typeof ce === 'object') {
|
|
1550
|
+
const lang = String(ce['language'] ?? '');
|
|
1551
|
+
const code = String(ce['code'] ?? '');
|
|
1552
|
+
if (code.trim())
|
|
1553
|
+
compOut.codeExample = { language: lang, code };
|
|
1554
|
+
}
|
|
1555
|
+
const ct = parseTable(c['configTable']);
|
|
1556
|
+
if (ct)
|
|
1557
|
+
compOut.configTable = ct;
|
|
1558
|
+
if (Array.isArray(c['performanceCharacteristics'])) {
|
|
1559
|
+
compOut.performanceCharacteristics = c['performanceCharacteristics'].map(String).filter(Boolean);
|
|
1560
|
+
}
|
|
1561
|
+
if (Array.isArray(c['securityNotes'])) {
|
|
1562
|
+
compOut.securityNotes = c['securityNotes'].map(String).filter(Boolean);
|
|
1563
|
+
}
|
|
1564
|
+
return compOut;
|
|
1565
|
+
})
|
|
1566
|
+
.filter(c => c.name && c.description);
|
|
1567
|
+
}
|
|
1568
|
+
return out;
|
|
1569
|
+
}
|
|
1570
|
+
/** Parse a {headers, rows} table-shaped value into a strict shape, or undefined. */
|
|
1571
|
+
function parseTable(v) {
|
|
1572
|
+
if (!v || typeof v !== 'object')
|
|
1573
|
+
return undefined;
|
|
1574
|
+
const t = v;
|
|
1575
|
+
if (!Array.isArray(t['headers']) || !Array.isArray(t['rows']))
|
|
1576
|
+
return undefined;
|
|
1577
|
+
const headers = t['headers'].map(String);
|
|
1578
|
+
const rows = t['rows']
|
|
1579
|
+
.filter(Array.isArray)
|
|
1580
|
+
.map(row => row.map(String));
|
|
1581
|
+
if (headers.length === 0 || rows.length === 0)
|
|
1582
|
+
return undefined;
|
|
1583
|
+
return { headers, rows };
|
|
1584
|
+
}
|
|
1345
1585
|
/**
|
|
1346
1586
|
* Generate ADRs via direct Anthropic API call.
|
|
1347
1587
|
* Used when Claude Code runner is unavailable (MCP mode).
|
|
@@ -1369,14 +1609,16 @@ function tryGenerateADRsViaAPI(sparc, dossier, query) {
|
|
|
1369
1609
|
'-H', 'anthropic-version: 2023-06-01',
|
|
1370
1610
|
'-d', JSON.stringify({
|
|
1371
1611
|
model: process.env['AGENTICS_ADR_MODEL'] || 'claude-sonnet-4-20250514',
|
|
1372
|
-
|
|
1612
|
+
// ADR-PIPELINE-101 — rich ADRs with diagrams + components + appendices
|
|
1613
|
+
// need substantially more output tokens than the pre-101 thin format.
|
|
1614
|
+
max_tokens: 32768,
|
|
1373
1615
|
messages: [{ role: 'user', content: prompt }],
|
|
1374
1616
|
}),
|
|
1375
1617
|
];
|
|
1376
1618
|
const rawResponse = execFileSync('curl', curlArgs, {
|
|
1377
1619
|
encoding: 'utf-8',
|
|
1378
|
-
timeout:
|
|
1379
|
-
maxBuffer:
|
|
1620
|
+
timeout: 360_000,
|
|
1621
|
+
maxBuffer: 32 * 1024 * 1024,
|
|
1380
1622
|
});
|
|
1381
1623
|
const response = JSON.parse(rawResponse);
|
|
1382
1624
|
if (response.error) {
|