@mcoda/core 0.1.27 → 0.1.29
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/api/AgentsApi.d.ts +9 -1
- package/dist/api/AgentsApi.d.ts.map +1 -1
- package/dist/api/AgentsApi.js +53 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/services/backlog/TaskOrderingService.d.ts +5 -0
- package/dist/services/backlog/TaskOrderingService.d.ts.map +1 -1
- package/dist/services/backlog/TaskOrderingService.js +171 -2
- package/dist/services/execution/QaTasksService.d.ts +2 -0
- package/dist/services/execution/QaTasksService.d.ts.map +1 -1
- package/dist/services/execution/QaTasksService.js +181 -20
- package/dist/services/execution/WorkOnTasksService.d.ts +1 -0
- package/dist/services/execution/WorkOnTasksService.d.ts.map +1 -1
- package/dist/services/execution/WorkOnTasksService.js +281 -7
- package/dist/services/planning/CreateTasksService.d.ts +8 -0
- package/dist/services/planning/CreateTasksService.d.ts.map +1 -1
- package/dist/services/planning/CreateTasksService.js +350 -39
- package/dist/services/planning/SdsPreflightService.d.ts +98 -0
- package/dist/services/planning/SdsPreflightService.d.ts.map +1 -0
- package/dist/services/planning/SdsPreflightService.js +1093 -0
- package/dist/services/planning/TaskSufficiencyService.d.ts.map +1 -1
- package/dist/services/planning/TaskSufficiencyService.js +178 -83
- package/dist/services/review/CodeReviewService.d.ts.map +1 -1
- package/dist/services/review/CodeReviewService.js +149 -7
- package/package.json +6 -6
|
@@ -13,6 +13,7 @@ import { TaskOrderingService } from "../backlog/TaskOrderingService.js";
|
|
|
13
13
|
import { QaTestCommandBuilder } from "../execution/QaTestCommandBuilder.js";
|
|
14
14
|
import { createEpicKeyGenerator, createStoryKeyGenerator, createTaskKeyGenerator, } from "./KeyHelpers.js";
|
|
15
15
|
import { TaskSufficiencyService } from "./TaskSufficiencyService.js";
|
|
16
|
+
import { SdsPreflightService } from "./SdsPreflightService.js";
|
|
16
17
|
const formatBullets = (items, fallback) => {
|
|
17
18
|
if (!items || items.length === 0)
|
|
18
19
|
return `- ${fallback}`;
|
|
@@ -257,6 +258,14 @@ const extractMarkdownHeadings = (value, limit) => {
|
|
|
257
258
|
!line.startsWith("*")) {
|
|
258
259
|
headings.push(line);
|
|
259
260
|
}
|
|
261
|
+
else {
|
|
262
|
+
const numberedHeading = line.match(/^(\d+(?:\.\d+)+)\s+(.+)$/);
|
|
263
|
+
if (numberedHeading) {
|
|
264
|
+
const headingText = `${numberedHeading[1]} ${numberedHeading[2]}`.trim();
|
|
265
|
+
if (/[a-z]/i.test(headingText))
|
|
266
|
+
headings.push(headingText);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
260
269
|
if (headings.length >= limit)
|
|
261
270
|
break;
|
|
262
271
|
}
|
|
@@ -398,6 +407,89 @@ const extractJsonObjects = (value) => {
|
|
|
398
407
|
}
|
|
399
408
|
return results;
|
|
400
409
|
};
|
|
410
|
+
const normalizeAgentFailoverEvents = (value) => {
|
|
411
|
+
if (!Array.isArray(value))
|
|
412
|
+
return [];
|
|
413
|
+
const events = [];
|
|
414
|
+
for (const entry of value) {
|
|
415
|
+
if (!isPlainObject(entry))
|
|
416
|
+
continue;
|
|
417
|
+
if (typeof entry.type !== "string" || entry.type.trim().length === 0)
|
|
418
|
+
continue;
|
|
419
|
+
events.push({ ...entry });
|
|
420
|
+
}
|
|
421
|
+
return events;
|
|
422
|
+
};
|
|
423
|
+
const mergeAgentFailoverEvents = (left, right) => {
|
|
424
|
+
if (!left.length)
|
|
425
|
+
return right;
|
|
426
|
+
if (!right.length)
|
|
427
|
+
return left;
|
|
428
|
+
const seen = new Set();
|
|
429
|
+
const merged = [];
|
|
430
|
+
const signature = (event) => [
|
|
431
|
+
event.type ?? "",
|
|
432
|
+
event.fromAgentId ?? "",
|
|
433
|
+
event.toAgentId ?? "",
|
|
434
|
+
event.at ?? "",
|
|
435
|
+
event.until ?? "",
|
|
436
|
+
event.durationMs ?? "",
|
|
437
|
+
].join("|");
|
|
438
|
+
for (const event of [...left, ...right]) {
|
|
439
|
+
const key = signature(event);
|
|
440
|
+
if (seen.has(key))
|
|
441
|
+
continue;
|
|
442
|
+
seen.add(key);
|
|
443
|
+
merged.push(event);
|
|
444
|
+
}
|
|
445
|
+
return merged;
|
|
446
|
+
};
|
|
447
|
+
const mergeAgentInvocationMetadata = (current, incoming) => {
|
|
448
|
+
if (!current && !incoming)
|
|
449
|
+
return undefined;
|
|
450
|
+
if (!incoming)
|
|
451
|
+
return current;
|
|
452
|
+
if (!current)
|
|
453
|
+
return { ...incoming };
|
|
454
|
+
const merged = { ...current, ...incoming };
|
|
455
|
+
const currentEvents = normalizeAgentFailoverEvents(current.failoverEvents);
|
|
456
|
+
const incomingEvents = normalizeAgentFailoverEvents(incoming.failoverEvents);
|
|
457
|
+
if (currentEvents.length > 0 || incomingEvents.length > 0) {
|
|
458
|
+
merged.failoverEvents = mergeAgentFailoverEvents(currentEvents, incomingEvents);
|
|
459
|
+
}
|
|
460
|
+
return merged;
|
|
461
|
+
};
|
|
462
|
+
const summarizeAgentFailoverEvent = (event) => {
|
|
463
|
+
const type = String(event.type ?? "unknown");
|
|
464
|
+
if (type === "switch_agent") {
|
|
465
|
+
const from = typeof event.fromAgentId === "string" ? event.fromAgentId : "unknown";
|
|
466
|
+
const to = typeof event.toAgentId === "string" ? event.toAgentId : "unknown";
|
|
467
|
+
return `switch_agent ${from} -> ${to}`;
|
|
468
|
+
}
|
|
469
|
+
if (type === "sleep_until_reset") {
|
|
470
|
+
const duration = typeof event.durationMs === "number" && Number.isFinite(event.durationMs)
|
|
471
|
+
? `${Math.round(event.durationMs / 1000)}s`
|
|
472
|
+
: "unknown duration";
|
|
473
|
+
const until = typeof event.until === "string" ? event.until : "unknown";
|
|
474
|
+
return `sleep_until_reset ${duration} (until ${until})`;
|
|
475
|
+
}
|
|
476
|
+
if (type === "stream_restart_after_limit") {
|
|
477
|
+
const from = typeof event.fromAgentId === "string" ? event.fromAgentId : "unknown";
|
|
478
|
+
return `stream_restart_after_limit from ${from}`;
|
|
479
|
+
}
|
|
480
|
+
return type;
|
|
481
|
+
};
|
|
482
|
+
const resolveTerminalFailoverAgentId = (events, fallbackAgentId) => {
|
|
483
|
+
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
484
|
+
const event = events[index];
|
|
485
|
+
if (event?.type !== "switch_agent")
|
|
486
|
+
continue;
|
|
487
|
+
if (typeof event.toAgentId === "string" && event.toAgentId.trim().length > 0) {
|
|
488
|
+
return event.toAgentId;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
return fallbackAgentId;
|
|
492
|
+
};
|
|
401
493
|
const compactNarrative = (value, fallback, maxLines = 5) => {
|
|
402
494
|
if (!value || value.trim().length === 0)
|
|
403
495
|
return fallback;
|
|
@@ -721,6 +813,7 @@ export class CreateTasksService {
|
|
|
721
813
|
this.ratingService = deps.ratingService;
|
|
722
814
|
this.taskOrderingFactory = deps.taskOrderingFactory ?? TaskOrderingService.create;
|
|
723
815
|
this.taskSufficiencyFactory = deps.taskSufficiencyFactory ?? TaskSufficiencyService.create;
|
|
816
|
+
this.sdsPreflightFactory = deps.sdsPreflightFactory ?? SdsPreflightService.create;
|
|
724
817
|
}
|
|
725
818
|
static async create(workspace) {
|
|
726
819
|
const repo = await GlobalRepository.create();
|
|
@@ -742,6 +835,7 @@ export class CreateTasksService {
|
|
|
742
835
|
workspaceRepo,
|
|
743
836
|
routingService,
|
|
744
837
|
taskSufficiencyFactory: TaskSufficiencyService.create,
|
|
838
|
+
sdsPreflightFactory: SdsPreflightService.create,
|
|
745
839
|
});
|
|
746
840
|
}
|
|
747
841
|
async close() {
|
|
@@ -830,6 +924,20 @@ export class CreateTasksService {
|
|
|
830
924
|
const resolved = path.isAbsolute(input) ? input : path.join(this.workspace.workspaceRoot, input);
|
|
831
925
|
return path.resolve(resolved).toLowerCase();
|
|
832
926
|
}
|
|
927
|
+
mergeDocInputs(primary, extras) {
|
|
928
|
+
const merged = [];
|
|
929
|
+
const seen = new Set();
|
|
930
|
+
for (const input of [...primary, ...extras]) {
|
|
931
|
+
if (!input?.trim())
|
|
932
|
+
continue;
|
|
933
|
+
const key = this.normalizeDocInputForSet(input);
|
|
934
|
+
if (seen.has(key))
|
|
935
|
+
continue;
|
|
936
|
+
seen.add(key);
|
|
937
|
+
merged.push(input);
|
|
938
|
+
}
|
|
939
|
+
return merged;
|
|
940
|
+
}
|
|
833
941
|
docIdentity(doc) {
|
|
834
942
|
const pathKey = `${doc.path ?? ""}`.trim().toLowerCase();
|
|
835
943
|
const idKey = `${doc.id ?? ""}`.trim().toLowerCase();
|
|
@@ -2100,8 +2208,11 @@ export class CreateTasksService {
|
|
|
2100
2208
|
const segmentHeadings = (doc.segments ?? [])
|
|
2101
2209
|
.map((segment) => segment.heading?.trim())
|
|
2102
2210
|
.filter((heading) => Boolean(heading));
|
|
2211
|
+
const segmentContentHeadings = (doc.segments ?? [])
|
|
2212
|
+
.flatMap((segment) => extractMarkdownHeadings(segment.content ?? "", Math.max(6, Math.ceil(limit / 4))))
|
|
2213
|
+
.slice(0, limit);
|
|
2103
2214
|
const contentHeadings = extractMarkdownHeadings(doc.content ?? "", limit);
|
|
2104
|
-
for (const heading of [...segmentHeadings, ...contentHeadings]) {
|
|
2215
|
+
for (const heading of [...segmentHeadings, ...segmentContentHeadings, ...contentHeadings]) {
|
|
2105
2216
|
const normalized = heading.replace(/[`*_]/g, "").trim();
|
|
2106
2217
|
if (!normalized)
|
|
2107
2218
|
continue;
|
|
@@ -2345,6 +2456,7 @@ export class CreateTasksService {
|
|
|
2345
2456
|
async invokeAgentWithRetry(agent, prompt, action, stream, jobId, commandRunId, metadata) {
|
|
2346
2457
|
const startedAt = Date.now();
|
|
2347
2458
|
let output = "";
|
|
2459
|
+
let invocationMetadata;
|
|
2348
2460
|
const logChunk = async (chunk) => {
|
|
2349
2461
|
if (!chunk)
|
|
2350
2462
|
return;
|
|
@@ -2352,17 +2464,51 @@ export class CreateTasksService {
|
|
|
2352
2464
|
if (stream)
|
|
2353
2465
|
process.stdout.write(chunk);
|
|
2354
2466
|
};
|
|
2467
|
+
const baseInvocationMetadata = {
|
|
2468
|
+
command: "create-tasks",
|
|
2469
|
+
action,
|
|
2470
|
+
phase: `create_tasks_${action}`,
|
|
2471
|
+
};
|
|
2472
|
+
const logFailoverEvents = async (events) => {
|
|
2473
|
+
if (!events.length)
|
|
2474
|
+
return;
|
|
2475
|
+
for (const event of events) {
|
|
2476
|
+
await this.jobService.appendLog(jobId, `[create-tasks] agent failover (${action}): ${summarizeAgentFailoverEvent(event)}\n`);
|
|
2477
|
+
}
|
|
2478
|
+
};
|
|
2479
|
+
const resolveUsageAgent = async (events) => {
|
|
2480
|
+
const agentId = resolveTerminalFailoverAgentId(events, agent.id);
|
|
2481
|
+
if (agentId === agent.id) {
|
|
2482
|
+
return { id: agent.id, defaultModel: agent.defaultModel };
|
|
2483
|
+
}
|
|
2484
|
+
try {
|
|
2485
|
+
const resolved = await this.agentService.resolveAgent(agentId);
|
|
2486
|
+
return { id: resolved.id, defaultModel: resolved.defaultModel };
|
|
2487
|
+
}
|
|
2488
|
+
catch (error) {
|
|
2489
|
+
await this.jobService.appendLog(jobId, `[create-tasks] unable to resolve failover agent (${agentId}) for usage accounting: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
2490
|
+
return { id: agent.id, defaultModel: agent.defaultModel };
|
|
2491
|
+
}
|
|
2492
|
+
};
|
|
2355
2493
|
try {
|
|
2356
2494
|
if (stream) {
|
|
2357
|
-
const gen = await this.agentService.invokeStream(agent.id, {
|
|
2495
|
+
const gen = await this.agentService.invokeStream(agent.id, {
|
|
2496
|
+
input: prompt,
|
|
2497
|
+
metadata: baseInvocationMetadata,
|
|
2498
|
+
});
|
|
2358
2499
|
for await (const chunk of gen) {
|
|
2359
2500
|
output += chunk.output ?? "";
|
|
2501
|
+
invocationMetadata = mergeAgentInvocationMetadata(invocationMetadata, chunk.metadata);
|
|
2360
2502
|
await logChunk(chunk.output);
|
|
2361
2503
|
}
|
|
2362
2504
|
}
|
|
2363
2505
|
else {
|
|
2364
|
-
const result = await this.agentService.invoke(agent.id, {
|
|
2506
|
+
const result = await this.agentService.invoke(agent.id, {
|
|
2507
|
+
input: prompt,
|
|
2508
|
+
metadata: baseInvocationMetadata,
|
|
2509
|
+
});
|
|
2365
2510
|
output = result.output ?? "";
|
|
2511
|
+
invocationMetadata = mergeAgentInvocationMetadata(invocationMetadata, result.metadata);
|
|
2366
2512
|
await logChunk(output);
|
|
2367
2513
|
}
|
|
2368
2514
|
}
|
|
@@ -2379,10 +2525,22 @@ export class CreateTasksService {
|
|
|
2379
2525
|
`Original content:\n${output}`,
|
|
2380
2526
|
].join("\n\n");
|
|
2381
2527
|
try {
|
|
2382
|
-
|
|
2528
|
+
let retryInvocationMetadata;
|
|
2529
|
+
const fix = await this.agentService.invoke(agent.id, {
|
|
2530
|
+
input: fixPrompt,
|
|
2531
|
+
metadata: {
|
|
2532
|
+
...baseInvocationMetadata,
|
|
2533
|
+
attempt: 2,
|
|
2534
|
+
stage: "json_repair",
|
|
2535
|
+
},
|
|
2536
|
+
});
|
|
2383
2537
|
output = fix.output ?? "";
|
|
2538
|
+
retryInvocationMetadata = mergeAgentInvocationMetadata(retryInvocationMetadata, fix.metadata);
|
|
2384
2539
|
parsed = extractJson(output);
|
|
2385
2540
|
if (parsed) {
|
|
2541
|
+
const failoverEvents = normalizeAgentFailoverEvents(retryInvocationMetadata?.failoverEvents);
|
|
2542
|
+
await logFailoverEvents(failoverEvents);
|
|
2543
|
+
const usageAgent = await resolveUsageAgent(failoverEvents);
|
|
2386
2544
|
const promptTokens = estimateTokens(prompt);
|
|
2387
2545
|
const completionTokens = estimateTokens(output);
|
|
2388
2546
|
const durationSeconds = (Date.now() - startedAt) / 1000;
|
|
@@ -2391,8 +2549,8 @@ export class CreateTasksService {
|
|
|
2391
2549
|
workspaceId: this.workspace.workspaceId,
|
|
2392
2550
|
jobId,
|
|
2393
2551
|
commandRunId,
|
|
2394
|
-
agentId:
|
|
2395
|
-
modelName:
|
|
2552
|
+
agentId: usageAgent.id,
|
|
2553
|
+
modelName: usageAgent.defaultModel,
|
|
2396
2554
|
promptTokens,
|
|
2397
2555
|
completionTokens,
|
|
2398
2556
|
tokensPrompt: promptTokens,
|
|
@@ -2403,6 +2561,7 @@ export class CreateTasksService {
|
|
|
2403
2561
|
action: `create_tasks_${action}`,
|
|
2404
2562
|
phase: `create_tasks_${action}`,
|
|
2405
2563
|
attempt,
|
|
2564
|
+
failoverEvents: failoverEvents.length > 0 ? failoverEvents : undefined,
|
|
2406
2565
|
...(metadata ?? {}),
|
|
2407
2566
|
},
|
|
2408
2567
|
});
|
|
@@ -2416,6 +2575,9 @@ export class CreateTasksService {
|
|
|
2416
2575
|
if (!parsed) {
|
|
2417
2576
|
throw new Error(`Agent output was not valid JSON for ${action}`);
|
|
2418
2577
|
}
|
|
2578
|
+
const failoverEvents = normalizeAgentFailoverEvents(invocationMetadata?.failoverEvents);
|
|
2579
|
+
await logFailoverEvents(failoverEvents);
|
|
2580
|
+
const usageAgent = await resolveUsageAgent(failoverEvents);
|
|
2419
2581
|
const promptTokens = estimateTokens(prompt);
|
|
2420
2582
|
const completionTokens = estimateTokens(output);
|
|
2421
2583
|
const durationSeconds = (Date.now() - startedAt) / 1000;
|
|
@@ -2424,8 +2586,8 @@ export class CreateTasksService {
|
|
|
2424
2586
|
workspaceId: this.workspace.workspaceId,
|
|
2425
2587
|
jobId,
|
|
2426
2588
|
commandRunId,
|
|
2427
|
-
agentId:
|
|
2428
|
-
modelName:
|
|
2589
|
+
agentId: usageAgent.id,
|
|
2590
|
+
modelName: usageAgent.defaultModel,
|
|
2429
2591
|
promptTokens,
|
|
2430
2592
|
completionTokens,
|
|
2431
2593
|
tokensPrompt: promptTokens,
|
|
@@ -2436,6 +2598,7 @@ export class CreateTasksService {
|
|
|
2436
2598
|
action: `create_tasks_${action}`,
|
|
2437
2599
|
phase: `create_tasks_${action}`,
|
|
2438
2600
|
attempt: 1,
|
|
2601
|
+
failoverEvents: failoverEvents.length > 0 ? failoverEvents : undefined,
|
|
2439
2602
|
...(metadata ?? {}),
|
|
2440
2603
|
},
|
|
2441
2604
|
});
|
|
@@ -3032,6 +3195,7 @@ export class CreateTasksService {
|
|
|
3032
3195
|
inputs: options.inputs,
|
|
3033
3196
|
agent: options.agentName,
|
|
3034
3197
|
agentStream,
|
|
3198
|
+
sdsPreflightCommit: options.sdsPreflightCommit === true,
|
|
3035
3199
|
},
|
|
3036
3200
|
});
|
|
3037
3201
|
let lastError;
|
|
@@ -3042,8 +3206,110 @@ export class CreateTasksService {
|
|
|
3042
3206
|
name: options.projectKey,
|
|
3043
3207
|
description: `Workspace project ${options.projectKey}`,
|
|
3044
3208
|
});
|
|
3045
|
-
|
|
3046
|
-
|
|
3209
|
+
let sdsPreflight;
|
|
3210
|
+
let sdsPreflightError;
|
|
3211
|
+
if (this.sdsPreflightFactory) {
|
|
3212
|
+
let sdsPreflightCloseError;
|
|
3213
|
+
try {
|
|
3214
|
+
const preflightService = await this.sdsPreflightFactory(this.workspace);
|
|
3215
|
+
try {
|
|
3216
|
+
sdsPreflight = await preflightService.runPreflight({
|
|
3217
|
+
workspace: options.workspace,
|
|
3218
|
+
projectKey: options.projectKey,
|
|
3219
|
+
inputPaths: options.inputs,
|
|
3220
|
+
sdsPaths: options.inputs,
|
|
3221
|
+
writeArtifacts: true,
|
|
3222
|
+
applyToSds: true,
|
|
3223
|
+
commitAppliedChanges: options.sdsPreflightCommit === true,
|
|
3224
|
+
commitMessage: options.sdsPreflightCommitMessage,
|
|
3225
|
+
});
|
|
3226
|
+
}
|
|
3227
|
+
finally {
|
|
3228
|
+
try {
|
|
3229
|
+
await preflightService.close();
|
|
3230
|
+
}
|
|
3231
|
+
catch (closeError) {
|
|
3232
|
+
sdsPreflightCloseError = closeError?.message ?? String(closeError);
|
|
3233
|
+
await this.jobService.appendLog(job.id, `SDS preflight close warning: ${sdsPreflightCloseError}\n`);
|
|
3234
|
+
}
|
|
3235
|
+
}
|
|
3236
|
+
}
|
|
3237
|
+
catch (error) {
|
|
3238
|
+
sdsPreflightError = error?.message ?? String(error);
|
|
3239
|
+
}
|
|
3240
|
+
if (!sdsPreflight) {
|
|
3241
|
+
const message = `create-tasks blocked: SDS preflight failed before backlog generation (${sdsPreflightError ?? "unknown error"}).`;
|
|
3242
|
+
await this.jobService.writeCheckpoint(job.id, {
|
|
3243
|
+
stage: "sds_preflight",
|
|
3244
|
+
timestamp: new Date().toISOString(),
|
|
3245
|
+
details: {
|
|
3246
|
+
status: "failed",
|
|
3247
|
+
error: message,
|
|
3248
|
+
readyForPlanning: false,
|
|
3249
|
+
qualityStatus: undefined,
|
|
3250
|
+
sourceSdsCount: 0,
|
|
3251
|
+
issueCount: 0,
|
|
3252
|
+
blockingIssueCount: 0,
|
|
3253
|
+
questionCount: 0,
|
|
3254
|
+
requiredQuestionCount: 0,
|
|
3255
|
+
reportPath: undefined,
|
|
3256
|
+
openQuestionsPath: undefined,
|
|
3257
|
+
gapAddendumPath: undefined,
|
|
3258
|
+
warnings: [],
|
|
3259
|
+
},
|
|
3260
|
+
});
|
|
3261
|
+
throw new Error(message);
|
|
3262
|
+
}
|
|
3263
|
+
const preflightWarnings = uniqueStrings([
|
|
3264
|
+
...(sdsPreflight.warnings ?? []),
|
|
3265
|
+
...(sdsPreflightCloseError ? [`SDS preflight close warning: ${sdsPreflightCloseError}`] : []),
|
|
3266
|
+
]);
|
|
3267
|
+
const blockingReasons = [];
|
|
3268
|
+
if (sdsPreflight.qualityStatus === "fail") {
|
|
3269
|
+
blockingReasons.push("SDS quality gates failed.");
|
|
3270
|
+
}
|
|
3271
|
+
if (sdsPreflight.blockingIssueCount > 0) {
|
|
3272
|
+
blockingReasons.push(`Blocking SDS issues: ${sdsPreflight.blockingIssueCount}.`);
|
|
3273
|
+
}
|
|
3274
|
+
if (sdsPreflight.requiredQuestionCount > 0) {
|
|
3275
|
+
blockingReasons.push(`Required open questions remaining: ${sdsPreflight.requiredQuestionCount}.`);
|
|
3276
|
+
}
|
|
3277
|
+
if (!sdsPreflight.readyForPlanning) {
|
|
3278
|
+
blockingReasons.push("SDS preflight reported planning context is not ready.");
|
|
3279
|
+
}
|
|
3280
|
+
if (blockingReasons.length > 0) {
|
|
3281
|
+
sdsPreflightError = blockingReasons.join(" ");
|
|
3282
|
+
}
|
|
3283
|
+
await this.jobService.writeCheckpoint(job.id, {
|
|
3284
|
+
stage: "sds_preflight",
|
|
3285
|
+
timestamp: new Date().toISOString(),
|
|
3286
|
+
details: {
|
|
3287
|
+
status: blockingReasons.length > 0 ? "blocked" : "succeeded",
|
|
3288
|
+
error: sdsPreflightError,
|
|
3289
|
+
readyForPlanning: sdsPreflight.readyForPlanning,
|
|
3290
|
+
qualityStatus: sdsPreflight.qualityStatus,
|
|
3291
|
+
sourceSdsCount: sdsPreflight.sourceSdsPaths.length,
|
|
3292
|
+
issueCount: sdsPreflight.issueCount,
|
|
3293
|
+
blockingIssueCount: sdsPreflight.blockingIssueCount,
|
|
3294
|
+
questionCount: sdsPreflight.questionCount,
|
|
3295
|
+
requiredQuestionCount: sdsPreflight.requiredQuestionCount,
|
|
3296
|
+
reportPath: sdsPreflight.reportPath,
|
|
3297
|
+
openQuestionsPath: sdsPreflight.openQuestionsPath,
|
|
3298
|
+
gapAddendumPath: sdsPreflight.gapAddendumPath,
|
|
3299
|
+
appliedToSds: sdsPreflight.appliedToSds,
|
|
3300
|
+
appliedSdsCount: sdsPreflight.appliedSdsPaths.length,
|
|
3301
|
+
commitHash: sdsPreflight.commitHash,
|
|
3302
|
+
warnings: preflightWarnings,
|
|
3303
|
+
},
|
|
3304
|
+
});
|
|
3305
|
+
if (blockingReasons.length > 0) {
|
|
3306
|
+
throw new Error(`create-tasks blocked by SDS preflight. ${blockingReasons.join(" ")} Report: ${sdsPreflight.reportPath}`);
|
|
3307
|
+
}
|
|
3308
|
+
}
|
|
3309
|
+
const preflightDocInputs = this.mergeDocInputs(options.inputs, sdsPreflight ? [...sdsPreflight.sourceSdsPaths, ...sdsPreflight.generatedDocPaths] : []);
|
|
3310
|
+
const docs = await this.prepareDocs(preflightDocInputs);
|
|
3311
|
+
const { docSummary, warnings: indexedDocWarnings } = this.buildDocContext(docs);
|
|
3312
|
+
const docWarnings = uniqueStrings([...(sdsPreflight?.warnings ?? []), ...indexedDocWarnings]);
|
|
3047
3313
|
const discoveryGraph = this.buildServiceDependencyGraph({ epics: [], stories: [], tasks: [] }, docs);
|
|
3048
3314
|
const projectBuildMethod = this.buildProjectConstructionMethod(docs, discoveryGraph);
|
|
3049
3315
|
const projectBuildPlan = this.buildProjectPlanArtifact(options.projectKey, docs, discoveryGraph, projectBuildMethod);
|
|
@@ -3139,57 +3405,83 @@ export class CreateTasksService {
|
|
|
3139
3405
|
let sufficiencyAudit;
|
|
3140
3406
|
let sufficiencyAuditError;
|
|
3141
3407
|
if (this.taskSufficiencyFactory) {
|
|
3408
|
+
let sufficiencyCloseError;
|
|
3142
3409
|
try {
|
|
3143
3410
|
const sufficiencyService = await this.taskSufficiencyFactory(this.workspace);
|
|
3144
3411
|
try {
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
});
|
|
3151
|
-
}
|
|
3152
|
-
catch (error) {
|
|
3153
|
-
sufficiencyAuditError = error?.message ?? String(error);
|
|
3154
|
-
await this.jobService.appendLog(job.id, `Task sufficiency audit failed; continuing with created backlog: ${sufficiencyAuditError}\n`);
|
|
3155
|
-
}
|
|
3412
|
+
sufficiencyAudit = await sufficiencyService.runAudit({
|
|
3413
|
+
workspace: options.workspace,
|
|
3414
|
+
projectKey: options.projectKey,
|
|
3415
|
+
sourceCommand: "create-tasks",
|
|
3416
|
+
});
|
|
3156
3417
|
}
|
|
3157
3418
|
finally {
|
|
3158
3419
|
try {
|
|
3159
3420
|
await sufficiencyService.close();
|
|
3160
3421
|
}
|
|
3161
3422
|
catch (closeError) {
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
sufficiencyAuditError = sufficiencyAuditError ? `${sufficiencyAuditError}; ${details}` : details;
|
|
3165
|
-
await this.jobService.appendLog(job.id, `${details}\n`);
|
|
3423
|
+
sufficiencyCloseError = closeError?.message ?? String(closeError);
|
|
3424
|
+
await this.jobService.appendLog(job.id, `Task sufficiency audit close warning: ${sufficiencyCloseError}\n`);
|
|
3166
3425
|
}
|
|
3167
3426
|
}
|
|
3168
3427
|
}
|
|
3169
3428
|
catch (error) {
|
|
3170
3429
|
sufficiencyAuditError = error?.message ?? String(error);
|
|
3171
|
-
|
|
3430
|
+
}
|
|
3431
|
+
if (!sufficiencyAudit) {
|
|
3432
|
+
const message = `create-tasks blocked: task sufficiency audit failed (${sufficiencyAuditError ?? "unknown error"}).`;
|
|
3433
|
+
await this.jobService.writeCheckpoint(job.id, {
|
|
3434
|
+
stage: "task_sufficiency_audit",
|
|
3435
|
+
timestamp: new Date().toISOString(),
|
|
3436
|
+
details: {
|
|
3437
|
+
status: "failed",
|
|
3438
|
+
error: message,
|
|
3439
|
+
jobId: undefined,
|
|
3440
|
+
commandRunId: undefined,
|
|
3441
|
+
satisfied: false,
|
|
3442
|
+
dryRun: undefined,
|
|
3443
|
+
totalTasksAdded: undefined,
|
|
3444
|
+
totalTasksUpdated: undefined,
|
|
3445
|
+
finalCoverageRatio: undefined,
|
|
3446
|
+
reportPath: undefined,
|
|
3447
|
+
remainingSectionCount: undefined,
|
|
3448
|
+
remainingFolderCount: undefined,
|
|
3449
|
+
remainingGapCount: undefined,
|
|
3450
|
+
warnings: [],
|
|
3451
|
+
},
|
|
3452
|
+
});
|
|
3453
|
+
throw new Error(message);
|
|
3454
|
+
}
|
|
3455
|
+
const sufficiencyWarnings = uniqueStrings([
|
|
3456
|
+
...(sufficiencyAudit.warnings ?? []),
|
|
3457
|
+
...(sufficiencyCloseError ? [`Task sufficiency audit close warning: ${sufficiencyCloseError}`] : []),
|
|
3458
|
+
]);
|
|
3459
|
+
if (!sufficiencyAudit.satisfied) {
|
|
3460
|
+
sufficiencyAuditError = `SDS coverage target not reached (coverage=${sufficiencyAudit.finalCoverageRatio}, remaining gaps=${sufficiencyAudit.remainingGaps.total}).`;
|
|
3172
3461
|
}
|
|
3173
3462
|
await this.jobService.writeCheckpoint(job.id, {
|
|
3174
3463
|
stage: "task_sufficiency_audit",
|
|
3175
3464
|
timestamp: new Date().toISOString(),
|
|
3176
3465
|
details: {
|
|
3177
|
-
status: sufficiencyAudit ? "succeeded" : "
|
|
3466
|
+
status: sufficiencyAudit.satisfied ? "succeeded" : "blocked",
|
|
3178
3467
|
error: sufficiencyAuditError,
|
|
3179
|
-
jobId: sufficiencyAudit
|
|
3180
|
-
commandRunId: sufficiencyAudit
|
|
3181
|
-
satisfied: sufficiencyAudit
|
|
3182
|
-
dryRun: sufficiencyAudit
|
|
3183
|
-
totalTasksAdded: sufficiencyAudit
|
|
3184
|
-
totalTasksUpdated: sufficiencyAudit
|
|
3185
|
-
finalCoverageRatio: sufficiencyAudit
|
|
3186
|
-
reportPath: sufficiencyAudit
|
|
3187
|
-
remainingSectionCount: sufficiencyAudit
|
|
3188
|
-
remainingFolderCount: sufficiencyAudit
|
|
3189
|
-
remainingGapCount: sufficiencyAudit
|
|
3190
|
-
warnings:
|
|
3468
|
+
jobId: sufficiencyAudit.jobId,
|
|
3469
|
+
commandRunId: sufficiencyAudit.commandRunId,
|
|
3470
|
+
satisfied: sufficiencyAudit.satisfied,
|
|
3471
|
+
dryRun: sufficiencyAudit.dryRun,
|
|
3472
|
+
totalTasksAdded: sufficiencyAudit.totalTasksAdded,
|
|
3473
|
+
totalTasksUpdated: sufficiencyAudit.totalTasksUpdated,
|
|
3474
|
+
finalCoverageRatio: sufficiencyAudit.finalCoverageRatio,
|
|
3475
|
+
reportPath: sufficiencyAudit.reportPath,
|
|
3476
|
+
remainingSectionCount: sufficiencyAudit.remainingSectionHeadings.length,
|
|
3477
|
+
remainingFolderCount: sufficiencyAudit.remainingFolderEntries.length,
|
|
3478
|
+
remainingGapCount: sufficiencyAudit.remainingGaps.total,
|
|
3479
|
+
warnings: sufficiencyWarnings,
|
|
3191
3480
|
},
|
|
3192
3481
|
});
|
|
3482
|
+
if (!sufficiencyAudit.satisfied) {
|
|
3483
|
+
throw new Error(`create-tasks blocked: task sufficiency audit did not reach full coverage. Report: ${sufficiencyAudit.reportPath}`);
|
|
3484
|
+
}
|
|
3193
3485
|
}
|
|
3194
3486
|
await this.jobService.updateJobStatus(job.id, "completed", {
|
|
3195
3487
|
payload: {
|
|
@@ -3201,6 +3493,25 @@ export class CreateTasksService {
|
|
|
3201
3493
|
planFolder: folder,
|
|
3202
3494
|
planSource,
|
|
3203
3495
|
fallbackReason,
|
|
3496
|
+
sdsPreflight: sdsPreflight
|
|
3497
|
+
? {
|
|
3498
|
+
readyForPlanning: sdsPreflight.readyForPlanning,
|
|
3499
|
+
qualityStatus: sdsPreflight.qualityStatus,
|
|
3500
|
+
sourceSdsCount: sdsPreflight.sourceSdsPaths.length,
|
|
3501
|
+
issueCount: sdsPreflight.issueCount,
|
|
3502
|
+
blockingIssueCount: sdsPreflight.blockingIssueCount,
|
|
3503
|
+
questionCount: sdsPreflight.questionCount,
|
|
3504
|
+
requiredQuestionCount: sdsPreflight.requiredQuestionCount,
|
|
3505
|
+
appliedToSds: sdsPreflight.appliedToSds,
|
|
3506
|
+
appliedSdsPaths: sdsPreflight.appliedSdsPaths,
|
|
3507
|
+
commitHash: sdsPreflight.commitHash,
|
|
3508
|
+
reportPath: sdsPreflight.reportPath,
|
|
3509
|
+
openQuestionsPath: sdsPreflight.openQuestionsPath,
|
|
3510
|
+
gapAddendumPath: sdsPreflight.gapAddendumPath,
|
|
3511
|
+
warnings: sdsPreflight.warnings,
|
|
3512
|
+
}
|
|
3513
|
+
: undefined,
|
|
3514
|
+
sdsPreflightError,
|
|
3204
3515
|
sufficiencyAudit: sufficiencyAudit
|
|
3205
3516
|
? {
|
|
3206
3517
|
jobId: sufficiencyAudit.jobId,
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { WorkspaceResolution } from "../../workspace/WorkspaceManager.js";
|
|
2
|
+
import { type ReviewSeverity } from "../docs/review/ReviewTypes.js";
|
|
3
|
+
export interface SdsPreflightQuestionAnswer {
|
|
4
|
+
question: string;
|
|
5
|
+
normalized: string;
|
|
6
|
+
required: boolean;
|
|
7
|
+
target: string;
|
|
8
|
+
sourcePath?: string;
|
|
9
|
+
line?: number;
|
|
10
|
+
answer: string;
|
|
11
|
+
rationale: string;
|
|
12
|
+
assumptions: string[];
|
|
13
|
+
}
|
|
14
|
+
export interface SdsPreflightIssueSummary {
|
|
15
|
+
gateId: string;
|
|
16
|
+
gateName: string;
|
|
17
|
+
severity: ReviewSeverity;
|
|
18
|
+
category: string;
|
|
19
|
+
message: string;
|
|
20
|
+
remediation: string;
|
|
21
|
+
location: string;
|
|
22
|
+
}
|
|
23
|
+
export interface SdsPreflightResult {
|
|
24
|
+
projectKey: string;
|
|
25
|
+
generatedAt: string;
|
|
26
|
+
readyForPlanning: boolean;
|
|
27
|
+
qualityStatus: "pass" | "warn" | "fail";
|
|
28
|
+
sourceSdsPaths: string[];
|
|
29
|
+
reportPath: string;
|
|
30
|
+
openQuestionsPath: string;
|
|
31
|
+
gapAddendumPath: string;
|
|
32
|
+
generatedDocPaths: string[];
|
|
33
|
+
questionCount: number;
|
|
34
|
+
requiredQuestionCount: number;
|
|
35
|
+
issueCount: number;
|
|
36
|
+
blockingIssueCount: number;
|
|
37
|
+
appliedToSds: boolean;
|
|
38
|
+
appliedSdsPaths: string[];
|
|
39
|
+
commitHash?: string;
|
|
40
|
+
issues: SdsPreflightIssueSummary[];
|
|
41
|
+
questions: SdsPreflightQuestionAnswer[];
|
|
42
|
+
warnings: string[];
|
|
43
|
+
}
|
|
44
|
+
export interface SdsPreflightOptions {
|
|
45
|
+
workspace: WorkspaceResolution;
|
|
46
|
+
projectKey: string;
|
|
47
|
+
inputPaths?: string[];
|
|
48
|
+
sdsPaths?: string[];
|
|
49
|
+
writeArtifacts?: boolean;
|
|
50
|
+
applyToSds?: boolean;
|
|
51
|
+
commitAppliedChanges?: boolean;
|
|
52
|
+
commitMessage?: string;
|
|
53
|
+
}
|
|
54
|
+
export declare class SdsPreflightService {
|
|
55
|
+
private readonly workspace;
|
|
56
|
+
constructor(workspace: WorkspaceResolution);
|
|
57
|
+
static create(workspace: WorkspaceResolution): Promise<SdsPreflightService>;
|
|
58
|
+
close(): Promise<void>;
|
|
59
|
+
private walkCandidates;
|
|
60
|
+
private collectPathCandidates;
|
|
61
|
+
private discoverSdsPaths;
|
|
62
|
+
private isLikelySdsPath;
|
|
63
|
+
private resolveSdsPaths;
|
|
64
|
+
private buildArtifacts;
|
|
65
|
+
private getGateRunners;
|
|
66
|
+
private dedupeIssues;
|
|
67
|
+
private resolveWorkspacePath;
|
|
68
|
+
private collectSignalsFromContent;
|
|
69
|
+
private collectSignalsByPath;
|
|
70
|
+
private signalsForPath;
|
|
71
|
+
private answerForQuestion;
|
|
72
|
+
private managedFolderTreeSection;
|
|
73
|
+
private managedTechnologySection;
|
|
74
|
+
private managedPolicyTelemetrySection;
|
|
75
|
+
private managedOperationsSection;
|
|
76
|
+
private managedAdapterSection;
|
|
77
|
+
private shouldReplaceIssueLine;
|
|
78
|
+
private replacementForIssue;
|
|
79
|
+
private extractQuestionAnswers;
|
|
80
|
+
private issueSection;
|
|
81
|
+
private remediationLines;
|
|
82
|
+
private verificationLines;
|
|
83
|
+
private buildAddendum;
|
|
84
|
+
private buildQuestionsDoc;
|
|
85
|
+
private summarizeIssues;
|
|
86
|
+
private collectGateResults;
|
|
87
|
+
private issueMatchesPath;
|
|
88
|
+
private questionMatchesPath;
|
|
89
|
+
private normalizeResolvedText;
|
|
90
|
+
private buildLineReplacementsForPath;
|
|
91
|
+
private applyLineReplacements;
|
|
92
|
+
private buildManagedSdsBlock;
|
|
93
|
+
private upsertManagedSdsBlock;
|
|
94
|
+
private applyPreflightRemediationsToSds;
|
|
95
|
+
private commitAppliedSdsChanges;
|
|
96
|
+
runPreflight(options: SdsPreflightOptions): Promise<SdsPreflightResult>;
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=SdsPreflightService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SdsPreflightService.d.ts","sourceRoot":"","sources":["../../../src/services/planning/SdsPreflightService.ts"],"names":[],"mappings":"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"}
|