@llm-dev-ops/agentics-cli 2.7.0 → 2.7.1
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/cli/index.js +30 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/pipeline/auto-chain.d.ts +168 -0
- package/dist/pipeline/auto-chain.d.ts.map +1 -1
- package/dist/pipeline/auto-chain.js +1854 -880
- package/dist/pipeline/auto-chain.js.map +1 -1
- package/dist/pipeline/enterprise/artifact-renderers.d.ts +30 -0
- package/dist/pipeline/enterprise/artifact-renderers.d.ts.map +1 -1
- package/dist/pipeline/enterprise/artifact-renderers.js +129 -1
- package/dist/pipeline/enterprise/artifact-renderers.js.map +1 -1
- package/dist/pipeline/gate/phase-dependency-gate.d.ts +93 -0
- package/dist/pipeline/gate/phase-dependency-gate.d.ts.map +1 -0
- package/dist/pipeline/gate/phase-dependency-gate.js +349 -0
- package/dist/pipeline/gate/phase-dependency-gate.js.map +1 -0
- package/dist/pipeline/phase3-sparc/phase3-sparc-coordinator.d.ts.map +1 -1
- package/dist/pipeline/phase3-sparc/phase3-sparc-coordinator.js +280 -40
- package/dist/pipeline/phase3-sparc/phase3-sparc-coordinator.js.map +1 -1
- package/dist/pipeline/phase4-adrs/phase4-adrs-coordinator.d.ts.map +1 -1
- package/dist/pipeline/phase4-adrs/phase4-adrs-coordinator.js +363 -87
- package/dist/pipeline/phase4-adrs/phase4-adrs-coordinator.js.map +1 -1
- package/dist/pipeline/phases/prompt-generator.d.ts.map +1 -1
- package/dist/pipeline/phases/prompt-generator.js +95 -6
- package/dist/pipeline/phases/prompt-generator.js.map +1 -1
- package/dist/pipeline/ruflo-phase-executor.d.ts +124 -1
- package/dist/pipeline/ruflo-phase-executor.d.ts.map +1 -1
- package/dist/pipeline/ruflo-phase-executor.js +319 -4
- package/dist/pipeline/ruflo-phase-executor.js.map +1 -1
- package/package.json +1 -1
|
@@ -24,6 +24,25 @@ const CHECK_TIMEOUT = 30_000;
|
|
|
24
24
|
const DEFAULT_SWARM_TIMEOUT = 900_000; // 15 min — SPARC generation takes ~10 min via ruflo
|
|
25
25
|
const DEFAULT_MAX_AGENTS = 8;
|
|
26
26
|
const MAX_ARTIFACT_CONTENT_BYTES = 32_000;
|
|
27
|
+
/**
|
|
28
|
+
* Per-phase wall-clock budget for `runPrimaryPhaseExecution` (ADR-PIPELINE-091 §6).
|
|
29
|
+
* Read once at module scope; overridable via `AGENTICS_RUFLO_PHASE_TIMEOUT` env var.
|
|
30
|
+
* Default 600000 ms (10 min) matches the ADR.
|
|
31
|
+
*/
|
|
32
|
+
const DEFAULT_PHASE_TIMEOUT = (() => {
|
|
33
|
+
const raw = process.env['AGENTICS_RUFLO_PHASE_TIMEOUT'];
|
|
34
|
+
if (!raw)
|
|
35
|
+
return 600_000;
|
|
36
|
+
const parsed = Number(raw);
|
|
37
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : 600_000;
|
|
38
|
+
})();
|
|
39
|
+
/** True when the operator has explicitly disabled the Ruflo primary executor. */
|
|
40
|
+
function isRufloDisabledByEnv() {
|
|
41
|
+
const raw = process.env['AGENTICS_DISABLE_RUFLO'];
|
|
42
|
+
if (!raw)
|
|
43
|
+
return false;
|
|
44
|
+
return /^(1|true|yes|on)$/i.test(raw.trim());
|
|
45
|
+
}
|
|
27
46
|
/**
|
|
28
47
|
* Naming convention rules embedded in implementation prompts to prevent
|
|
29
48
|
* double-prefix bugs like "IIbudgetManagementPort".
|
|
@@ -121,11 +140,22 @@ export function resolveRufloBinary() {
|
|
|
121
140
|
return null;
|
|
122
141
|
return null;
|
|
123
142
|
}
|
|
124
|
-
/**
|
|
143
|
+
/**
|
|
144
|
+
* Check ruflo is installed and has swarm support.
|
|
145
|
+
*
|
|
146
|
+
* Adds an optional `reason` field when Ruflo is unavailable so callers
|
|
147
|
+
* (notably `runPrimaryPhaseExecution`) can distinguish operator-disabled
|
|
148
|
+
* from binary-missing from swarm-unsupported. Per ADR-PIPELINE-091 §6,
|
|
149
|
+
* `AGENTICS_DISABLE_RUFLO=true` short-circuits this check before any binary
|
|
150
|
+
* resolution so CI environments never attempt Ruflo at all.
|
|
151
|
+
*/
|
|
125
152
|
export function checkRufloAvailable() {
|
|
153
|
+
if (isRufloDisabledByEnv()) {
|
|
154
|
+
return { available: false, version: 'N/A', binary: '', reason: 'disabled-by-env' };
|
|
155
|
+
}
|
|
126
156
|
const binary = resolveRufloBinary();
|
|
127
157
|
if (!binary)
|
|
128
|
-
return { available: false, version: 'N/A', binary: '' };
|
|
158
|
+
return { available: false, version: 'N/A', binary: '', reason: 'binary-not-found' };
|
|
129
159
|
try {
|
|
130
160
|
const output = execSync(`${binary} --version`, {
|
|
131
161
|
encoding: 'utf-8', timeout: CHECK_TIMEOUT, stdio: 'pipe',
|
|
@@ -136,12 +166,12 @@ export function checkRufloAvailable() {
|
|
|
136
166
|
execSync(`${binary} swarm --help`, { encoding: 'utf-8', timeout: CHECK_TIMEOUT, stdio: 'pipe' });
|
|
137
167
|
}
|
|
138
168
|
catch {
|
|
139
|
-
return { available: false, version: `${version} (swarm not supported)`, binary };
|
|
169
|
+
return { available: false, version: `${version} (swarm not supported)`, binary, reason: 'swarm-unsupported' };
|
|
140
170
|
}
|
|
141
171
|
return { available: true, version, binary };
|
|
142
172
|
}
|
|
143
173
|
catch {
|
|
144
|
-
return { available: false, version: 'N/A', binary };
|
|
174
|
+
return { available: false, version: 'N/A', binary, reason: 'version-probe-failed' };
|
|
145
175
|
}
|
|
146
176
|
}
|
|
147
177
|
// ============================================================================
|
|
@@ -492,6 +522,7 @@ export function groupIntoWaves(sortedLevels, _language, scenarioQuery, artifacts
|
|
|
492
522
|
`base repository interfaces, and ERP client stubs. Source: ${artifactCtx}`,
|
|
493
523
|
targetDir: 'src/shared',
|
|
494
524
|
wave: 1,
|
|
525
|
+
argv: ['--local-only'],
|
|
495
526
|
}],
|
|
496
527
|
dependsOn: [],
|
|
497
528
|
});
|
|
@@ -519,6 +550,7 @@ export function groupIntoWaves(sortedLevels, _language, scenarioQuery, artifacts
|
|
|
519
550
|
`base repository interfaces, and ERP client stubs. Source: ${artifactCtx}`,
|
|
520
551
|
targetDir: 'src/shared',
|
|
521
552
|
wave: 1,
|
|
553
|
+
argv: ['--local-only'],
|
|
522
554
|
}],
|
|
523
555
|
dependsOn: [],
|
|
524
556
|
});
|
|
@@ -564,6 +596,7 @@ export function groupIntoWaves(sortedLevels, _language, scenarioQuery, artifacts
|
|
|
564
596
|
targetDir: 'ui',
|
|
565
597
|
wave: 4,
|
|
566
598
|
dependsOn: ['Database & Shared Infrastructure'],
|
|
599
|
+
argv: ['--local-only'],
|
|
567
600
|
},
|
|
568
601
|
{
|
|
569
602
|
label: 'Infrastructure & Integration',
|
|
@@ -575,6 +608,7 @@ export function groupIntoWaves(sortedLevels, _language, scenarioQuery, artifacts
|
|
|
575
608
|
targetDir: 'src/infra',
|
|
576
609
|
wave: 4,
|
|
577
610
|
dependsOn: ['Database & Shared Infrastructure'],
|
|
611
|
+
argv: ['--local-only'],
|
|
578
612
|
},
|
|
579
613
|
{
|
|
580
614
|
label: 'Test Suite',
|
|
@@ -592,6 +626,7 @@ export function groupIntoWaves(sortedLevels, _language, scenarioQuery, artifacts
|
|
|
592
626
|
targetDir: 'tests',
|
|
593
627
|
wave: 4,
|
|
594
628
|
dependsOn: testDependsOn,
|
|
629
|
+
argv: ['--local-only'],
|
|
595
630
|
},
|
|
596
631
|
{
|
|
597
632
|
label: 'Deployment Configuration',
|
|
@@ -603,6 +638,7 @@ export function groupIntoWaves(sortedLevels, _language, scenarioQuery, artifacts
|
|
|
603
638
|
targetDir: 'deploy',
|
|
604
639
|
wave: 4,
|
|
605
640
|
dependsOn: ['Database & Shared Infrastructure'],
|
|
641
|
+
argv: ['--local-only'],
|
|
606
642
|
},
|
|
607
643
|
],
|
|
608
644
|
dependsOn: [1, 2, 3],
|
|
@@ -804,6 +840,7 @@ export function buildIterativeImplementationPrompts(artifacts, scenarioQuery, la
|
|
|
804
840
|
targetDir: `src/${ctxSlug}`,
|
|
805
841
|
wave: wave.wave,
|
|
806
842
|
dependsOn: taskDependsOn.length > 0 ? taskDependsOn : undefined,
|
|
843
|
+
argv: ['--local-only'],
|
|
807
844
|
});
|
|
808
845
|
}
|
|
809
846
|
// Merge context tasks with any pre-existing wave tasks (e.g., infrastructure, cross-cutting)
|
|
@@ -1140,6 +1177,7 @@ Write all output to the ./plans folder as markdown files:
|
|
|
1140
1177
|
|
|
1141
1178
|
The implementation must be enterprise grade, commercially viable, production ready, bug and error free with no compilation issues.`,
|
|
1142
1179
|
targetDir: 'plans',
|
|
1180
|
+
argv: ['--local-only'],
|
|
1143
1181
|
},
|
|
1144
1182
|
];
|
|
1145
1183
|
}
|
|
@@ -1163,6 +1201,7 @@ Write all output to the ./plans folder:
|
|
|
1163
1201
|
- plans/adrs/ (one .md file per ADR)
|
|
1164
1202
|
- plans/ddd/ (domain-model.md + context-map.md)`,
|
|
1165
1203
|
targetDir: 'plans',
|
|
1204
|
+
argv: ['--local-only'],
|
|
1166
1205
|
},
|
|
1167
1206
|
];
|
|
1168
1207
|
}
|
|
@@ -1193,6 +1232,7 @@ Write all output to the ./plans/prompts/ folder:
|
|
|
1193
1232
|
- ... (continue for all phases, typically 10-30 depending on complexity)
|
|
1194
1233
|
- plans/prompts/execution-plan.json (ordered list of all prompts with dependencies)`,
|
|
1195
1234
|
targetDir: 'plans/prompts',
|
|
1235
|
+
argv: ['--local-only'],
|
|
1196
1236
|
},
|
|
1197
1237
|
];
|
|
1198
1238
|
}
|
|
@@ -1232,6 +1272,7 @@ CRITICAL — DO NOT generate scaffolding or stubs. This is a REAL implementation
|
|
|
1232
1272
|
Engineers on the client's team should be able to plug this into their ERP using the ERP surface endpoints that are already established, with light massaging only.`,
|
|
1233
1273
|
targetDir: 'src',
|
|
1234
1274
|
wave: 1,
|
|
1275
|
+
argv: ['--local-only'],
|
|
1235
1276
|
},
|
|
1236
1277
|
];
|
|
1237
1278
|
}
|
|
@@ -1257,6 +1298,7 @@ function buildPhase5TasksLegacy(scenarioQuery, language, artifacts) {
|
|
|
1257
1298
|
`and comprehensive error handling. Follow the technology decisions in the ADRs. Source files: ${artifactCtx}`,
|
|
1258
1299
|
targetDir: 'src',
|
|
1259
1300
|
wave: 1,
|
|
1301
|
+
argv: ['--local-only'],
|
|
1260
1302
|
},
|
|
1261
1303
|
{
|
|
1262
1304
|
label: 'Frontend Application',
|
|
@@ -1269,6 +1311,7 @@ function buildPhase5TasksLegacy(scenarioQuery, language, artifacts) {
|
|
|
1269
1311
|
targetDir: 'ui',
|
|
1270
1312
|
wave: 2,
|
|
1271
1313
|
dependsOn: ['Backend Implementation'],
|
|
1314
|
+
argv: ['--local-only'],
|
|
1272
1315
|
},
|
|
1273
1316
|
{
|
|
1274
1317
|
label: 'Infrastructure & Integration',
|
|
@@ -1281,6 +1324,7 @@ function buildPhase5TasksLegacy(scenarioQuery, language, artifacts) {
|
|
|
1281
1324
|
targetDir: 'src/infra',
|
|
1282
1325
|
wave: 2,
|
|
1283
1326
|
dependsOn: ['Backend Implementation'],
|
|
1327
|
+
argv: ['--local-only'],
|
|
1284
1328
|
},
|
|
1285
1329
|
{
|
|
1286
1330
|
label: 'Database & SQL',
|
|
@@ -1292,6 +1336,7 @@ function buildPhase5TasksLegacy(scenarioQuery, language, artifacts) {
|
|
|
1292
1336
|
`database-specific optimizations for the database engine specified in the ADRs. Source files: ${artifactCtx}`,
|
|
1293
1337
|
targetDir: 'sql',
|
|
1294
1338
|
wave: 1,
|
|
1339
|
+
argv: ['--local-only'],
|
|
1295
1340
|
},
|
|
1296
1341
|
{
|
|
1297
1342
|
label: 'Test Suite',
|
|
@@ -1310,6 +1355,7 @@ function buildPhase5TasksLegacy(scenarioQuery, language, artifacts) {
|
|
|
1310
1355
|
targetDir: 'tests',
|
|
1311
1356
|
wave: 4,
|
|
1312
1357
|
dependsOn: ['Backend Implementation', 'Database & SQL'],
|
|
1358
|
+
argv: ['--local-only'],
|
|
1313
1359
|
},
|
|
1314
1360
|
{
|
|
1315
1361
|
label: 'Deployment Configuration',
|
|
@@ -1324,6 +1370,7 @@ function buildPhase5TasksLegacy(scenarioQuery, language, artifacts) {
|
|
|
1324
1370
|
targetDir: 'deploy',
|
|
1325
1371
|
wave: 2,
|
|
1326
1372
|
dependsOn: ['Backend Implementation'],
|
|
1373
|
+
argv: ['--local-only'],
|
|
1327
1374
|
},
|
|
1328
1375
|
];
|
|
1329
1376
|
}
|
|
@@ -1364,9 +1411,269 @@ export function buildPhase6Tasks(scenarioQuery, artifacts) {
|
|
|
1364
1411
|
`data transformation adapters specific to this domain, and health monitoring for ERP connectivity. ` +
|
|
1365
1412
|
`Source files: ${artifactCtx}`,
|
|
1366
1413
|
targetDir: 'erp',
|
|
1414
|
+
argv: ['--local-only'],
|
|
1415
|
+
},
|
|
1416
|
+
];
|
|
1417
|
+
}
|
|
1418
|
+
// ============================================================================
|
|
1419
|
+
// Phase 5/5a task builders — ADR-PIPELINE-091
|
|
1420
|
+
// ============================================================================
|
|
1421
|
+
/**
|
|
1422
|
+
* Canonical SPARC section ordering. `buildImplPromptsTasks` emits one Ruflo
|
|
1423
|
+
* task per section so downstream tests can assert `task count ===
|
|
1424
|
+
* SPARC_SECTIONS.length` on the default path.
|
|
1425
|
+
*/
|
|
1426
|
+
const SPARC_SECTIONS = ['specification', 'pseudocode', 'architecture', 'refinement', 'completion'];
|
|
1427
|
+
/**
|
|
1428
|
+
* Build Ruflo tasks for the coverage-gap slot (normally handled by the remote
|
|
1429
|
+
* `quality-engineering/coverage-gap-detect` agent). Under ADR-PIPELINE-091
|
|
1430
|
+
* the local swarm produces the same output so remote 503s do not gate the
|
|
1431
|
+
* pipeline. The task writes a `coverage-gaps.json` under the phase-5
|
|
1432
|
+
* coverage directory.
|
|
1433
|
+
*/
|
|
1434
|
+
export function buildCoverageGapTasks(dossier, context) {
|
|
1435
|
+
const language = context.language ?? 'typescript';
|
|
1436
|
+
const sparcRef = dossier.artifacts?.['SPARC'] ?? dossier.artifacts?.['sparc-combined'] ?? '';
|
|
1437
|
+
const tddRef = dossier.artifacts?.['TDD'] ?? dossier.artifacts?.['tdd'] ?? '';
|
|
1438
|
+
const adrRef = dossier.artifacts?.['ADRs'] ?? dossier.artifacts?.['adrs'] ?? '';
|
|
1439
|
+
const dddRef = dossier.artifacts?.['DDD'] ?? dossier.artifacts?.['ddd'] ?? '';
|
|
1440
|
+
const sources = [
|
|
1441
|
+
sparcRef ? `SPARC=${sparcRef}` : '',
|
|
1442
|
+
tddRef ? `TDD=${tddRef}` : '',
|
|
1443
|
+
adrRef ? `ADRs=${adrRef}` : '',
|
|
1444
|
+
dddRef ? `DDD=${dddRef}` : '',
|
|
1445
|
+
].filter(Boolean).join(', ') || '(none yet — infer gaps from scenario query alone)';
|
|
1446
|
+
return [
|
|
1447
|
+
{
|
|
1448
|
+
label: 'Coverage Gap Detection',
|
|
1449
|
+
description: [
|
|
1450
|
+
`Task: emit the coverage-gap list the remote quality-engineering/coverage-gap-detect agent would normally produce.`,
|
|
1451
|
+
``,
|
|
1452
|
+
`## Project scenario`,
|
|
1453
|
+
dossier.scenarioQuery,
|
|
1454
|
+
``,
|
|
1455
|
+
`## Target language`,
|
|
1456
|
+
language,
|
|
1457
|
+
``,
|
|
1458
|
+
`## Source artifacts`,
|
|
1459
|
+
sources,
|
|
1460
|
+
``,
|
|
1461
|
+
`## Required output (write exactly these files under ./coverage/)`,
|
|
1462
|
+
`- coverage/coverage-gaps.json`,
|
|
1463
|
+
` Shape: { "traceId": "${dossier.traceId}", "gaps": [{ "area": string, "file": string, "severity": "high"|"medium"|"low", "reason": string, "suggestedTests": string[] }], "summary": { "high": number, "medium": number, "low": number } }`,
|
|
1464
|
+
`- coverage/coverage-gaps.md (human-readable companion report).`,
|
|
1465
|
+
``,
|
|
1466
|
+
`Rules:`,
|
|
1467
|
+
`1. Every gap must cite the SPARC or DDD element it comes from (command, query, aggregate, or domain event).`,
|
|
1468
|
+
`2. Severity uses "high" for unvalidated domain invariants, "medium" for missing command-handler integration tests, "low" for style/docstring gaps.`,
|
|
1469
|
+
`3. Do NOT invent files that are not in the SPARC/DDD/ADR set above — if no source is available, emit an empty gaps array and a summary with zeroes.`,
|
|
1470
|
+
`4. This is a LOCAL-ONLY run. Do not call any remote diligence agent; the primary executor has already marked this pass as local-only via --local-only.`,
|
|
1471
|
+
].join('\n'),
|
|
1472
|
+
targetDir: 'coverage',
|
|
1473
|
+
argv: ['--local-only'],
|
|
1367
1474
|
},
|
|
1368
1475
|
];
|
|
1369
1476
|
}
|
|
1477
|
+
/**
|
|
1478
|
+
* Build Ruflo tasks for the implementation-prompts slot (normally produced
|
|
1479
|
+
* from SPARC + ADRs + DDD). Emits one Ruflo task per canonical SPARC section
|
|
1480
|
+
* so the combined output populates `plans/prompts/impl-NNN-<slug>.md` plus
|
|
1481
|
+
* `plans/prompts/execution-plan.json`. Matches the filename format auto-chain
|
|
1482
|
+
* consumes: `impl-${String(order).padStart(3, '0')}-${phase.slug}.md`.
|
|
1483
|
+
*/
|
|
1484
|
+
export function buildImplPromptsTasks(dossier, context, adrs = [], ddd = []) {
|
|
1485
|
+
const language = context.language ?? 'typescript';
|
|
1486
|
+
const adrSummary = adrs.length
|
|
1487
|
+
? adrs.slice(0, 24).map(a => ` - ${a.id}: ${a.title}${a.decision ? ' — ' + a.decision.slice(0, 160) : ''}`).join('\n')
|
|
1488
|
+
: ' (no ADRs supplied — infer from SPARC alone)';
|
|
1489
|
+
const dddSummary = ddd.length
|
|
1490
|
+
? ddd.slice(0, 24).map(c => ` - ${c.name}${c.aggregates?.length ? ' (' + c.aggregates.slice(0, 6).join(', ') + ')' : ''}`).join('\n')
|
|
1491
|
+
: ' (no DDD contexts supplied — infer from SPARC alone)';
|
|
1492
|
+
const sparcRef = dossier.artifacts?.['SPARC'] ?? dossier.artifacts?.['sparc-combined'] ?? '';
|
|
1493
|
+
return SPARC_SECTIONS.map((section, idx) => {
|
|
1494
|
+
const order = idx + 1;
|
|
1495
|
+
const slug = section;
|
|
1496
|
+
const filename = `impl-${String(order).padStart(3, '0')}-${slug}.md`;
|
|
1497
|
+
return {
|
|
1498
|
+
label: `Impl Prompt — ${section}`,
|
|
1499
|
+
description: [
|
|
1500
|
+
`Task: produce the implementation prompt for the SPARC "${section}" section.`,
|
|
1501
|
+
``,
|
|
1502
|
+
`## Scenario`,
|
|
1503
|
+
dossier.scenarioQuery,
|
|
1504
|
+
``,
|
|
1505
|
+
`## Target language`,
|
|
1506
|
+
language,
|
|
1507
|
+
``,
|
|
1508
|
+
`## ADRs in scope`,
|
|
1509
|
+
adrSummary,
|
|
1510
|
+
``,
|
|
1511
|
+
`## Bounded contexts in scope`,
|
|
1512
|
+
dddSummary,
|
|
1513
|
+
``,
|
|
1514
|
+
`## Source SPARC artifact`,
|
|
1515
|
+
sparcRef || '(none — use scenario query alone)',
|
|
1516
|
+
``,
|
|
1517
|
+
`## Required output`,
|
|
1518
|
+
`Write exactly one file: plans/prompts/${filename}`,
|
|
1519
|
+
`The prompt must be dependency-coherent with its predecessors (prompts 001..${String(order - 1).padStart(3, '0')}).`,
|
|
1520
|
+
`Include:`,
|
|
1521
|
+
`- Exact typed interfaces with domain-specific fields (no Record<string, unknown>).`,
|
|
1522
|
+
`- The algorithms or database DDL this step implements.`,
|
|
1523
|
+
`- Test cases validating domain behavior (not "id is defined").`,
|
|
1524
|
+
`- A clear "Prerequisites" block that references prior prompts by filename.`,
|
|
1525
|
+
``,
|
|
1526
|
+
`After writing the ${filename} file, append an entry to plans/prompts/execution-plan.json (create if missing) with shape: `,
|
|
1527
|
+
` { "order": ${order}, "file": "${filename}", "section": "${section}", "dependsOn": [${order > 1 ? `"impl-${String(order - 1).padStart(3, '0')}-${SPARC_SECTIONS[idx - 1]}.md"` : ''}] }.`,
|
|
1528
|
+
``,
|
|
1529
|
+
`This is a LOCAL-ONLY run. --local-only is already set on the task; do not invoke remote diligence agents.`,
|
|
1530
|
+
].join('\n'),
|
|
1531
|
+
targetDir: 'plans/prompts',
|
|
1532
|
+
wave: order === 1 ? 1 : 2,
|
|
1533
|
+
dependsOn: order === 1 ? undefined : [`Impl Prompt — ${SPARC_SECTIONS[idx - 1]}`],
|
|
1534
|
+
argv: ['--local-only'],
|
|
1535
|
+
};
|
|
1536
|
+
});
|
|
1537
|
+
}
|
|
1538
|
+
/**
|
|
1539
|
+
* Map a canonical phase id onto the matching task builder + label + output
|
|
1540
|
+
* slot. Centralised here so both auto-chain and the ADR-093 dependency gate
|
|
1541
|
+
* see the same mapping.
|
|
1542
|
+
*/
|
|
1543
|
+
function buildTasksForPrimaryPhase(phaseId, dossier, context) {
|
|
1544
|
+
const artifacts = dossier.artifacts ?? {};
|
|
1545
|
+
switch (phaseId) {
|
|
1546
|
+
case 'phase3-sparc':
|
|
1547
|
+
return {
|
|
1548
|
+
tasks: buildPhase3Tasks(dossier.scenarioQuery, artifacts),
|
|
1549
|
+
label: 'SPARC + London TDD (primary)',
|
|
1550
|
+
phaseNumber: 3,
|
|
1551
|
+
};
|
|
1552
|
+
case 'phase4-adrs-ddd':
|
|
1553
|
+
return {
|
|
1554
|
+
tasks: buildPhase4Tasks(dossier.scenarioQuery, artifacts),
|
|
1555
|
+
label: 'ADRs + DDD (primary)',
|
|
1556
|
+
phaseNumber: 4,
|
|
1557
|
+
};
|
|
1558
|
+
case 'phase5a-prompts': {
|
|
1559
|
+
// Try to extract ADR + DDD summaries from dossier artifacts if present.
|
|
1560
|
+
const adrSummaries = artifacts['ADRs']
|
|
1561
|
+
? extractADRSummary(artifacts['ADRs']).map(a => ({ id: a.id, title: a.title, decision: a.decision }))
|
|
1562
|
+
: [];
|
|
1563
|
+
const dddSummaries = artifacts['DDD']
|
|
1564
|
+
? extractDDDSummary(artifacts['DDD']).contexts.map(c => ({ name: c.name, aggregates: c.aggregates }))
|
|
1565
|
+
: [];
|
|
1566
|
+
return {
|
|
1567
|
+
tasks: buildImplPromptsTasks(dossier, context, adrSummaries, dddSummaries),
|
|
1568
|
+
label: 'Implementation Prompts (primary)',
|
|
1569
|
+
phaseNumber: 5,
|
|
1570
|
+
};
|
|
1571
|
+
}
|
|
1572
|
+
case 'phase5-coverage':
|
|
1573
|
+
return {
|
|
1574
|
+
tasks: buildCoverageGapTasks(dossier, context),
|
|
1575
|
+
label: 'Coverage Gap Detection (primary)',
|
|
1576
|
+
phaseNumber: 5,
|
|
1577
|
+
};
|
|
1578
|
+
}
|
|
1579
|
+
}
|
|
1580
|
+
/**
|
|
1581
|
+
* Canonical primary-executor entry point per ADR-PIPELINE-091.
|
|
1582
|
+
*
|
|
1583
|
+
* Invariants:
|
|
1584
|
+
* 1. Never throws. Returns `{ success: false, executionTier: 'unavailable' }`
|
|
1585
|
+
* when Ruflo is not runnable — the caller decides fallback.
|
|
1586
|
+
* 2. Honors `AGENTICS_DISABLE_RUFLO=true` (short-circuits without touching
|
|
1587
|
+
* the binary; reason is `ruflo-disabled-by-env`).
|
|
1588
|
+
* 3. Timeout defaults to `DEFAULT_PHASE_TIMEOUT`, overridable per-call. When
|
|
1589
|
+
* the Ruflo swarm exceeds the budget, tier stays `ruflo-local` but
|
|
1590
|
+
* `success` flips to false with reason `ruflo-timeout` so the gate can
|
|
1591
|
+
* trigger template fallback.
|
|
1592
|
+
* 4. Every dispatched task carries `--local-only` (enforced both in builders
|
|
1593
|
+
* and defensively by the executor loop).
|
|
1594
|
+
*/
|
|
1595
|
+
export async function runPrimaryPhaseExecution(phaseId, dossier, context, timeout) {
|
|
1596
|
+
const startTime = Date.now();
|
|
1597
|
+
const effectiveTimeout = typeof timeout === 'number' && timeout > 0 ? timeout : DEFAULT_PHASE_TIMEOUT;
|
|
1598
|
+
const check = checkRufloAvailable();
|
|
1599
|
+
if (!check.available) {
|
|
1600
|
+
const reason = check.reason === 'disabled-by-env' ? 'ruflo-disabled-by-env' : 'ruflo-not-available';
|
|
1601
|
+
return {
|
|
1602
|
+
phaseId,
|
|
1603
|
+
success: false,
|
|
1604
|
+
executionTier: 'unavailable',
|
|
1605
|
+
reason,
|
|
1606
|
+
filesModified: 0,
|
|
1607
|
+
timing: Date.now() - startTime,
|
|
1608
|
+
message: check.reason === 'disabled-by-env'
|
|
1609
|
+
? `Ruflo disabled via AGENTICS_DISABLE_RUFLO; caller should fall through to templates for ${phaseId}`
|
|
1610
|
+
: `Ruflo not available (${check.version || 'no binary'}); caller should fall through to templates for ${phaseId}`,
|
|
1611
|
+
};
|
|
1612
|
+
}
|
|
1613
|
+
const { tasks, label, phaseNumber } = buildTasksForPrimaryPhase(phaseId, dossier, context);
|
|
1614
|
+
if (tasks.length === 0) {
|
|
1615
|
+
return {
|
|
1616
|
+
phaseId,
|
|
1617
|
+
success: false,
|
|
1618
|
+
executionTier: 'ruflo-local',
|
|
1619
|
+
reason: 'no-tasks-built',
|
|
1620
|
+
filesModified: 0,
|
|
1621
|
+
timing: Date.now() - startTime,
|
|
1622
|
+
message: `No Ruflo tasks built for ${phaseId}`,
|
|
1623
|
+
};
|
|
1624
|
+
}
|
|
1625
|
+
// Ensure output dir exists before dispatch — executeRufloPhaseSwarm does
|
|
1626
|
+
// this too but the caller expects the directory to exist on success.
|
|
1627
|
+
try {
|
|
1628
|
+
fs.mkdirSync(context.outputDir, { recursive: true, mode: 0o700 });
|
|
1629
|
+
}
|
|
1630
|
+
catch {
|
|
1631
|
+
// non-fatal; downstream will surface any permission problems
|
|
1632
|
+
}
|
|
1633
|
+
let result;
|
|
1634
|
+
try {
|
|
1635
|
+
result = executeRufloPhaseSwarm({
|
|
1636
|
+
phase: phaseNumber,
|
|
1637
|
+
label,
|
|
1638
|
+
scenarioQuery: dossier.scenarioQuery,
|
|
1639
|
+
runDir: dossier.runDir,
|
|
1640
|
+
traceId: dossier.traceId,
|
|
1641
|
+
outputDir: context.outputDir,
|
|
1642
|
+
tasks,
|
|
1643
|
+
agenticsResults: context.agenticsResults ?? [],
|
|
1644
|
+
priorArtifacts: dossier.artifacts ?? {},
|
|
1645
|
+
maxAgents: context.maxAgents ?? DEFAULT_MAX_AGENTS,
|
|
1646
|
+
timeoutMs: effectiveTimeout,
|
|
1647
|
+
});
|
|
1648
|
+
}
|
|
1649
|
+
catch (err) {
|
|
1650
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1651
|
+
return {
|
|
1652
|
+
phaseId,
|
|
1653
|
+
success: false,
|
|
1654
|
+
executionTier: 'ruflo-local',
|
|
1655
|
+
reason: 'swarm-error',
|
|
1656
|
+
filesModified: 0,
|
|
1657
|
+
timing: Date.now() - startTime,
|
|
1658
|
+
message: `Ruflo swarm errored for ${phaseId}: ${msg.slice(0, 200)}`,
|
|
1659
|
+
};
|
|
1660
|
+
}
|
|
1661
|
+
const timing = Date.now() - startTime;
|
|
1662
|
+
const success = result.filesModified > 0;
|
|
1663
|
+
const timedOut = timing >= effectiveTimeout && !result.swarmCompleted;
|
|
1664
|
+
return {
|
|
1665
|
+
phaseId,
|
|
1666
|
+
success,
|
|
1667
|
+
executionTier: 'ruflo-local',
|
|
1668
|
+
reason: success ? undefined : (timedOut ? 'ruflo-timeout' : 'swarm-error'),
|
|
1669
|
+
rufloResult: result,
|
|
1670
|
+
filesModified: result.filesModified,
|
|
1671
|
+
timing,
|
|
1672
|
+
message: success
|
|
1673
|
+
? `Ruflo primary executor produced ${result.filesModified} files for ${phaseId} in ${timing}ms`
|
|
1674
|
+
: `Ruflo primary executor produced no files for ${phaseId} in ${timing}ms`,
|
|
1675
|
+
};
|
|
1676
|
+
}
|
|
1370
1677
|
// ============================================================================
|
|
1371
1678
|
// Main Executor
|
|
1372
1679
|
// ============================================================================
|
|
@@ -1442,11 +1749,19 @@ export function executeRufloPhaseSwarm(config) {
|
|
|
1442
1749
|
agenticsContext,
|
|
1443
1750
|
artifactContent.length > 0 ? `\n--- PRIOR PHASE ARTIFACTS (actual content) ---${artifactContent}\n--- END PRIOR PHASE ARTIFACTS ---` : '',
|
|
1444
1751
|
].join('\n');
|
|
1752
|
+
// Per ADR-PIPELINE-091: every engineering task is dispatched `--local-only`
|
|
1753
|
+
// so the local swarm does not round-trip to the remote fleet it is meant
|
|
1754
|
+
// to be independent of. Task builders normalize this; if a legacy task
|
|
1755
|
+
// slipped through without argv, we defensively inject it here too.
|
|
1756
|
+
const extraArgs = Array.from(task.argv ?? []);
|
|
1757
|
+
if (!extraArgs.includes('--local-only'))
|
|
1758
|
+
extraArgs.push('--local-only');
|
|
1445
1759
|
try {
|
|
1446
1760
|
rufloExec(check.binary, [
|
|
1447
1761
|
'task', 'create',
|
|
1448
1762
|
'--type', 'implementation',
|
|
1449
1763
|
'--description', fullDescription,
|
|
1764
|
+
...extraArgs,
|
|
1450
1765
|
], config.outputDir, CHECK_TIMEOUT);
|
|
1451
1766
|
tasksCreated++;
|
|
1452
1767
|
}
|