@mcoda/core 0.1.28 → 0.1.30
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/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/WorkOnTasksService.d.ts +1 -0
- package/dist/services/execution/WorkOnTasksService.d.ts.map +1 -1
- package/dist/services/execution/WorkOnTasksService.js +172 -33
- package/package.json +6 -6
|
@@ -93,6 +93,11 @@ export declare class TaskOrderingService {
|
|
|
93
93
|
private buildOrderingMetadata;
|
|
94
94
|
private buildDependencyGraph;
|
|
95
95
|
private hasDependencyPath;
|
|
96
|
+
private buildDependentsFromGraph;
|
|
97
|
+
private findCyclicComponents;
|
|
98
|
+
private chooseCycleBreakCandidate;
|
|
99
|
+
private removeDependencyEdge;
|
|
100
|
+
private resolveDependencyCycles;
|
|
96
101
|
private injectFoundationDependencies;
|
|
97
102
|
private applyInferredDependencies;
|
|
98
103
|
private compareTasks;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TaskOrderingService.d.ts","sourceRoot":"","sources":["../../../src/services/backlog/TaskOrderingService.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAG1E,OAAO,EAAgB,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAwEtE,KAAK,qBAAqB,GAAG,aAAa,GAAG,aAAa,GAAG,wBAAwB,CAAC;AAEtF,UAAU,UAAU;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED,UAAU,OAAO;IACf,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;CACf;AAuCD,UAAU,gBAAgB;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAC3C;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,UAAU,CAAC;IACpB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,UAAU,CAAC,EAAE,SAAS,EAAE,CAAC;IACzB,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,qBAAqB,CAAC,EAAE,qBAAqB,CAAC;CAC/C;
|
|
1
|
+
{"version":3,"file":"TaskOrderingService.d.ts","sourceRoot":"","sources":["../../../src/services/backlog/TaskOrderingService.ts"],"names":[],"mappings":"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"}
|
|
@@ -646,6 +646,166 @@ export class TaskOrderingService {
|
|
|
646
646
|
}
|
|
647
647
|
return false;
|
|
648
648
|
}
|
|
649
|
+
buildDependentsFromGraph(graph) {
|
|
650
|
+
const dependents = new Map();
|
|
651
|
+
for (const [taskId, dependencies] of graph.entries()) {
|
|
652
|
+
for (const dependencyId of dependencies) {
|
|
653
|
+
const list = dependents.get(dependencyId) ?? [];
|
|
654
|
+
list.push(taskId);
|
|
655
|
+
dependents.set(dependencyId, list);
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
return dependents;
|
|
659
|
+
}
|
|
660
|
+
findCyclicComponents(graph) {
|
|
661
|
+
const nodes = new Set();
|
|
662
|
+
for (const [taskId, dependencies] of graph.entries()) {
|
|
663
|
+
nodes.add(taskId);
|
|
664
|
+
for (const dependencyId of dependencies)
|
|
665
|
+
nodes.add(dependencyId);
|
|
666
|
+
}
|
|
667
|
+
const indexByNode = new Map();
|
|
668
|
+
const lowLinkByNode = new Map();
|
|
669
|
+
const stack = [];
|
|
670
|
+
const onStack = new Set();
|
|
671
|
+
const components = [];
|
|
672
|
+
let index = 0;
|
|
673
|
+
const visit = (nodeId) => {
|
|
674
|
+
indexByNode.set(nodeId, index);
|
|
675
|
+
lowLinkByNode.set(nodeId, index);
|
|
676
|
+
index += 1;
|
|
677
|
+
stack.push(nodeId);
|
|
678
|
+
onStack.add(nodeId);
|
|
679
|
+
const neighbors = graph.get(nodeId) ?? new Set();
|
|
680
|
+
for (const neighbor of neighbors) {
|
|
681
|
+
if (!indexByNode.has(neighbor)) {
|
|
682
|
+
visit(neighbor);
|
|
683
|
+
const nextLow = Math.min(lowLinkByNode.get(nodeId) ?? Number.MAX_SAFE_INTEGER, lowLinkByNode.get(neighbor) ?? Number.MAX_SAFE_INTEGER);
|
|
684
|
+
lowLinkByNode.set(nodeId, nextLow);
|
|
685
|
+
continue;
|
|
686
|
+
}
|
|
687
|
+
if (!onStack.has(neighbor))
|
|
688
|
+
continue;
|
|
689
|
+
const nextLow = Math.min(lowLinkByNode.get(nodeId) ?? Number.MAX_SAFE_INTEGER, indexByNode.get(neighbor) ?? Number.MAX_SAFE_INTEGER);
|
|
690
|
+
lowLinkByNode.set(nodeId, nextLow);
|
|
691
|
+
}
|
|
692
|
+
if ((lowLinkByNode.get(nodeId) ?? -1) !== (indexByNode.get(nodeId) ?? -2))
|
|
693
|
+
return;
|
|
694
|
+
const component = [];
|
|
695
|
+
while (stack.length > 0) {
|
|
696
|
+
const popped = stack.pop();
|
|
697
|
+
onStack.delete(popped);
|
|
698
|
+
component.push(popped);
|
|
699
|
+
if (popped === nodeId)
|
|
700
|
+
break;
|
|
701
|
+
}
|
|
702
|
+
if (component.length > 1) {
|
|
703
|
+
components.push(component);
|
|
704
|
+
return;
|
|
705
|
+
}
|
|
706
|
+
const only = component[0];
|
|
707
|
+
if (only && graph.get(only)?.has(only)) {
|
|
708
|
+
components.push(component);
|
|
709
|
+
}
|
|
710
|
+
};
|
|
711
|
+
for (const nodeId of nodes) {
|
|
712
|
+
if (!indexByNode.has(nodeId))
|
|
713
|
+
visit(nodeId);
|
|
714
|
+
}
|
|
715
|
+
return components;
|
|
716
|
+
}
|
|
717
|
+
chooseCycleBreakCandidate(component, dependencyGraph, impact) {
|
|
718
|
+
const members = new Set(component);
|
|
719
|
+
const candidates = [];
|
|
720
|
+
for (const taskId of component) {
|
|
721
|
+
const dependencies = dependencyGraph.get(taskId);
|
|
722
|
+
if (!dependencies || dependencies.size === 0)
|
|
723
|
+
continue;
|
|
724
|
+
const weight = impact.get(taskId)?.total ?? 0;
|
|
725
|
+
for (const dependencyId of dependencies) {
|
|
726
|
+
if (!members.has(dependencyId))
|
|
727
|
+
continue;
|
|
728
|
+
candidates.push({ taskId, dependsOnTaskId: dependencyId, weight });
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
if (candidates.length === 0)
|
|
732
|
+
return undefined;
|
|
733
|
+
const maxWeight = Math.max(...candidates.map((candidate) => candidate.weight));
|
|
734
|
+
const weighted = candidates.filter((candidate) => candidate.weight === maxWeight);
|
|
735
|
+
if (weighted.length === 1)
|
|
736
|
+
return weighted[0];
|
|
737
|
+
const randomIndex = Math.floor(Math.random() * weighted.length);
|
|
738
|
+
return weighted[randomIndex];
|
|
739
|
+
}
|
|
740
|
+
async removeDependencyEdge(deps, taskId, dependsOnTaskId, persist) {
|
|
741
|
+
const rows = deps.get(taskId) ?? [];
|
|
742
|
+
const rowIndex = rows.findIndex((row) => row.depends_on_task_id === dependsOnTaskId);
|
|
743
|
+
if (rowIndex < 0)
|
|
744
|
+
return false;
|
|
745
|
+
if (persist) {
|
|
746
|
+
await this.db.run(`DELETE FROM task_dependencies
|
|
747
|
+
WHERE id = (
|
|
748
|
+
SELECT id
|
|
749
|
+
FROM task_dependencies
|
|
750
|
+
WHERE task_id = ?
|
|
751
|
+
AND depends_on_task_id = ?
|
|
752
|
+
LIMIT 1
|
|
753
|
+
)`, taskId, dependsOnTaskId);
|
|
754
|
+
}
|
|
755
|
+
const nextRows = [...rows];
|
|
756
|
+
nextRows.splice(rowIndex, 1);
|
|
757
|
+
if (nextRows.length > 0) {
|
|
758
|
+
deps.set(taskId, nextRows);
|
|
759
|
+
}
|
|
760
|
+
else {
|
|
761
|
+
deps.delete(taskId);
|
|
762
|
+
}
|
|
763
|
+
return true;
|
|
764
|
+
}
|
|
765
|
+
async resolveDependencyCycles(tasks, deps, warnings, persist) {
|
|
766
|
+
const taskById = new Map(tasks.map((task) => [task.id, task]));
|
|
767
|
+
const maxIterations = Math.max(1, tasks.length * tasks.length);
|
|
768
|
+
let removedEdges = 0;
|
|
769
|
+
let unresolvedCycleMembers = new Set();
|
|
770
|
+
for (let iteration = 0; iteration < maxIterations; iteration += 1) {
|
|
771
|
+
const dependencyGraph = this.buildDependencyGraph(tasks, deps);
|
|
772
|
+
const cyclicComponents = this.findCyclicComponents(dependencyGraph);
|
|
773
|
+
if (cyclicComponents.length === 0) {
|
|
774
|
+
return { removedEdges, unresolvedCycleMembers: new Set() };
|
|
775
|
+
}
|
|
776
|
+
let removedThisIteration = false;
|
|
777
|
+
const dependents = this.buildDependentsFromGraph(dependencyGraph);
|
|
778
|
+
const impact = this.dependencyImpactMap(dependents);
|
|
779
|
+
for (const component of cyclicComponents) {
|
|
780
|
+
const candidate = this.chooseCycleBreakCandidate(component, dependencyGraph, impact);
|
|
781
|
+
if (!candidate)
|
|
782
|
+
continue;
|
|
783
|
+
const removed = await this.removeDependencyEdge(deps, candidate.taskId, candidate.dependsOnTaskId, persist);
|
|
784
|
+
if (!removed)
|
|
785
|
+
continue;
|
|
786
|
+
removedThisIteration = true;
|
|
787
|
+
removedEdges += 1;
|
|
788
|
+
const taskKey = taskById.get(candidate.taskId)?.key ?? candidate.taskId;
|
|
789
|
+
const dependencyKey = taskById.get(candidate.dependsOnTaskId)?.key ?? candidate.dependsOnTaskId;
|
|
790
|
+
warnings.push(persist
|
|
791
|
+
? `Resolved dependency cycle: removed ${taskKey} -> ${dependencyKey}.`
|
|
792
|
+
: `Dry run: would remove cyclic dependency ${taskKey} -> ${dependencyKey}.`);
|
|
793
|
+
break;
|
|
794
|
+
}
|
|
795
|
+
if (removedThisIteration)
|
|
796
|
+
continue;
|
|
797
|
+
unresolvedCycleMembers = new Set(cyclicComponents.flat());
|
|
798
|
+
break;
|
|
799
|
+
}
|
|
800
|
+
if (unresolvedCycleMembers.size === 0) {
|
|
801
|
+
const dependencyGraph = this.buildDependencyGraph(tasks, deps);
|
|
802
|
+
const cyclicComponents = this.findCyclicComponents(dependencyGraph);
|
|
803
|
+
if (cyclicComponents.length > 0) {
|
|
804
|
+
unresolvedCycleMembers = new Set(cyclicComponents.flat());
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
return { removedEdges, unresolvedCycleMembers };
|
|
808
|
+
}
|
|
649
809
|
async injectFoundationDependencies(tasks, deps, warnings, persist) {
|
|
650
810
|
const relationType = "inferred_foundation";
|
|
651
811
|
const classification = new Map();
|
|
@@ -1243,6 +1403,11 @@ export class TaskOrderingService {
|
|
|
1243
1403
|
else if (enableInference && !resolvedAgent) {
|
|
1244
1404
|
warnings.push("Dependency inference skipped: no agent resolved.");
|
|
1245
1405
|
}
|
|
1406
|
+
const cycleResolution = await this.resolveDependencyCycles(tasks, deps, warnings, applyChanges);
|
|
1407
|
+
if (cycleResolution.removedEdges > 0) {
|
|
1408
|
+
warnings.push(`Cycle resolution removed ${cycleResolution.removedEdges} dependency edge(s).`);
|
|
1409
|
+
}
|
|
1410
|
+
({ nodes, dependents, missingRefs } = this.buildNodes(tasks, deps));
|
|
1246
1411
|
if (missingRefs.size > 0) {
|
|
1247
1412
|
warnings.push(`Missing dependencies referenced: ${Array.from(missingRefs).join(", ")}`);
|
|
1248
1413
|
}
|
|
@@ -1270,7 +1435,7 @@ export class TaskOrderingService {
|
|
|
1270
1435
|
? this.buildOrderingMetadata(nodes, impact, missingContext, docContext)
|
|
1271
1436
|
: { metadataByTask: new Map(), complexityByTask: new Map() };
|
|
1272
1437
|
const { ordered: initialOrder, cycle, cycleMembers } = this.topologicalSort(nodes, dependents, impact, complexityByTask, missingContext, undefined, stageOrderMap);
|
|
1273
|
-
if (cycle) {
|
|
1438
|
+
if (cycle || cycleResolution.unresolvedCycleMembers.size > 0) {
|
|
1274
1439
|
warnings.push("Dependency cycle detected; ordering may be partial.");
|
|
1275
1440
|
}
|
|
1276
1441
|
let agentRank;
|
|
@@ -1351,7 +1516,11 @@ export class TaskOrderingService {
|
|
|
1351
1516
|
warnings.push("Agent refinement skipped: no agent resolved.");
|
|
1352
1517
|
}
|
|
1353
1518
|
const { ordered, cycle: cycleAfterAgent, cycleMembers: agentCycleMembers } = this.topologicalSort(nodes, dependents, impact, complexityByTask, missingContext, agentRank, stageOrderMap);
|
|
1354
|
-
const finalCycleMembers = new Set([
|
|
1519
|
+
const finalCycleMembers = new Set([
|
|
1520
|
+
...cycleMembers,
|
|
1521
|
+
...agentCycleMembers,
|
|
1522
|
+
...cycleResolution.unresolvedCycleMembers,
|
|
1523
|
+
]);
|
|
1355
1524
|
if (cycleAfterAgent && !cycle) {
|
|
1356
1525
|
warnings.push("Agent-influenced ordering encountered a cycle; used partial order.");
|
|
1357
1526
|
}
|
|
@@ -114,6 +114,7 @@ export declare class WorkOnTasksService {
|
|
|
114
114
|
private applyChromiumForTests;
|
|
115
115
|
private findMissingTestHarnessTasks;
|
|
116
116
|
private runTests;
|
|
117
|
+
private selectMostDependedTask;
|
|
117
118
|
workOnTasks(request: WorkOnTasksRequest): Promise<WorkOnTasksResult>;
|
|
118
119
|
}
|
|
119
120
|
//# sourceMappingURL=WorkOnTasksService.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WorkOnTasksService.d.ts","sourceRoot":"","sources":["../../../src/services/execution/WorkOnTasksService.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,YAAY,EAAgD,MAAM,eAAe,CAAC;AAC3F,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAE9D,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAuB,MAAM,WAAW,CAAC;AAEvF,OAAO,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAC1E,OAAO,EAAE,UAAU,EAAiB,MAAM,uBAAuB,CAAC;AAClE,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC1G,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAGzD,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAE7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;
|
|
1
|
+
{"version":3,"file":"WorkOnTasksService.d.ts","sourceRoot":"","sources":["../../../src/services/execution/WorkOnTasksService.ts"],"names":[],"mappings":"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"}
|
|
@@ -17,7 +17,6 @@ import { RoutingService } from "../agents/RoutingService.js";
|
|
|
17
17
|
import { GATEWAY_HANDOFF_ENV_PATH } from "../agents/GatewayHandoff.js";
|
|
18
18
|
import { AgentRatingService } from "../agents/AgentRatingService.js";
|
|
19
19
|
import { ensureProjectGuidance, isDocContextExcluded, loadProjectGuidance, normalizeDocType, } from "../shared/ProjectGuidance.js";
|
|
20
|
-
import { AUTH_ERROR_REASON, isAuthErrorMessage } from "../shared/AuthErrors.js";
|
|
21
20
|
import { buildDocdexUsageGuidance } from "../shared/DocdexGuidance.js";
|
|
22
21
|
import { createTaskCommentSlug, formatTaskCommentBody } from "../tasks/TaskCommentFormatter.js";
|
|
23
22
|
const exec = promisify(execCb);
|
|
@@ -67,6 +66,9 @@ const QA_PROMPT_MARKERS = [
|
|
|
67
66
|
"qa plan output schema",
|
|
68
67
|
];
|
|
69
68
|
const QA_ADAPTERS = new Set(["qa-cli"]);
|
|
69
|
+
const ANSI_WARNING_REGEX = /\x1B\[[0-?]*[ -/]*[@-~]/g;
|
|
70
|
+
const CONTROL_WARNING_REGEX = /[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g;
|
|
71
|
+
const DEPENDENCY_WARNING_REGEX = /^Skipped \d+ task\(s\) due to dependencies not ready\.$/i;
|
|
70
72
|
const sanitizeNonGatewayPrompt = (value) => {
|
|
71
73
|
if (!value)
|
|
72
74
|
return undefined;
|
|
@@ -144,6 +146,11 @@ const mergeUnique = (values) => {
|
|
|
144
146
|
}
|
|
145
147
|
return result;
|
|
146
148
|
};
|
|
149
|
+
const sanitizeWarningText = (value) => value
|
|
150
|
+
.replace(ANSI_WARNING_REGEX, "")
|
|
151
|
+
.replace(CONTROL_WARNING_REGEX, "")
|
|
152
|
+
.replace(/\s+/g, " ")
|
|
153
|
+
.trim();
|
|
147
154
|
const buildCodaliEnvOverrides = (options = []) => {
|
|
148
155
|
const resolvedOptions = Array.isArray(options) ? { preferredFiles: options } : options;
|
|
149
156
|
const overrides = {};
|
|
@@ -3563,6 +3570,46 @@ export class WorkOnTasksService {
|
|
|
3563
3570
|
}
|
|
3564
3571
|
return { ok: true, results };
|
|
3565
3572
|
}
|
|
3573
|
+
selectMostDependedTask(candidates) {
|
|
3574
|
+
if (candidates.length === 0)
|
|
3575
|
+
return undefined;
|
|
3576
|
+
const candidateIds = new Set(candidates.map((candidate) => candidate.task.id));
|
|
3577
|
+
const dependentCounts = new Map();
|
|
3578
|
+
for (const candidate of candidates) {
|
|
3579
|
+
dependentCounts.set(candidate.task.id, 0);
|
|
3580
|
+
}
|
|
3581
|
+
for (const candidate of candidates) {
|
|
3582
|
+
for (const dependencyId of candidate.dependencies.ids) {
|
|
3583
|
+
if (!candidateIds.has(dependencyId))
|
|
3584
|
+
continue;
|
|
3585
|
+
dependentCounts.set(dependencyId, (dependentCounts.get(dependencyId) ?? 0) + 1);
|
|
3586
|
+
}
|
|
3587
|
+
}
|
|
3588
|
+
const ranked = candidates
|
|
3589
|
+
.map((candidate, index) => ({
|
|
3590
|
+
candidate,
|
|
3591
|
+
index,
|
|
3592
|
+
dependentCount: dependentCounts.get(candidate.task.id) ?? 0,
|
|
3593
|
+
}))
|
|
3594
|
+
.sort((a, b) => {
|
|
3595
|
+
const priorityA = a.candidate.task.priority ?? Number.MAX_SAFE_INTEGER;
|
|
3596
|
+
const priorityB = b.candidate.task.priority ?? Number.MAX_SAFE_INTEGER;
|
|
3597
|
+
if (priorityA !== priorityB)
|
|
3598
|
+
return priorityA - priorityB;
|
|
3599
|
+
if (a.dependentCount !== b.dependentCount)
|
|
3600
|
+
return b.dependentCount - a.dependentCount;
|
|
3601
|
+
const storyPointsA = a.candidate.task.storyPoints ?? Number.POSITIVE_INFINITY;
|
|
3602
|
+
const storyPointsB = b.candidate.task.storyPoints ?? Number.POSITIVE_INFINITY;
|
|
3603
|
+
if (storyPointsA !== storyPointsB)
|
|
3604
|
+
return storyPointsA - storyPointsB;
|
|
3605
|
+
const createdA = Date.parse(a.candidate.task.createdAt) || 0;
|
|
3606
|
+
const createdB = Date.parse(b.candidate.task.createdAt) || 0;
|
|
3607
|
+
if (createdA !== createdB)
|
|
3608
|
+
return createdA - createdB;
|
|
3609
|
+
return a.index - b.index;
|
|
3610
|
+
});
|
|
3611
|
+
return ranked[0]?.candidate;
|
|
3612
|
+
}
|
|
3566
3613
|
async workOnTasks(request) {
|
|
3567
3614
|
await this.ensureMcoda();
|
|
3568
3615
|
const commandName = "work-on-tasks";
|
|
@@ -3724,13 +3771,24 @@ export class WorkOnTasksService {
|
|
|
3724
3771
|
try {
|
|
3725
3772
|
const warnings = [...baseBranchWarnings, ...statusWarnings, ...runnerWarnings];
|
|
3726
3773
|
const selectionWarningSet = new Set();
|
|
3774
|
+
let dependencyWarningIndex = null;
|
|
3727
3775
|
const pushSelectionWarnings = (entries) => {
|
|
3728
|
-
for (const
|
|
3729
|
-
const normalized =
|
|
3776
|
+
for (const rawWarning of entries) {
|
|
3777
|
+
const normalized = sanitizeWarningText(rawWarning);
|
|
3730
3778
|
if (!normalized || selectionWarningSet.has(normalized))
|
|
3731
3779
|
continue;
|
|
3780
|
+
if (DEPENDENCY_WARNING_REGEX.test(normalized)) {
|
|
3781
|
+
if (dependencyWarningIndex === null) {
|
|
3782
|
+
warnings.push(normalized);
|
|
3783
|
+
dependencyWarningIndex = warnings.length - 1;
|
|
3784
|
+
}
|
|
3785
|
+
else {
|
|
3786
|
+
warnings[dependencyWarningIndex] = normalized;
|
|
3787
|
+
}
|
|
3788
|
+
continue;
|
|
3789
|
+
}
|
|
3732
3790
|
selectionWarningSet.add(normalized);
|
|
3733
|
-
warnings.push(
|
|
3791
|
+
warnings.push(normalized);
|
|
3734
3792
|
}
|
|
3735
3793
|
};
|
|
3736
3794
|
try {
|
|
@@ -3770,6 +3828,57 @@ export class WorkOnTasksService {
|
|
|
3770
3828
|
limit: request.limit,
|
|
3771
3829
|
parallel: request.parallel,
|
|
3772
3830
|
});
|
|
3831
|
+
if (selection.ordered.length === 0 && !ignoreDependencies && !explicitTaskSelection) {
|
|
3832
|
+
const dependencyBlocked = selection.warnings.some((warning) => warning.toLowerCase().includes("dependencies not ready"));
|
|
3833
|
+
if (dependencyBlocked) {
|
|
3834
|
+
const refreshedSelection = await this.selectionService.selectTasks({
|
|
3835
|
+
projectKey: request.projectKey,
|
|
3836
|
+
epicKey: request.epicKey,
|
|
3837
|
+
storyKey: request.storyKey,
|
|
3838
|
+
taskKeys: request.taskKeys,
|
|
3839
|
+
statusFilter,
|
|
3840
|
+
ignoreStatusFilter,
|
|
3841
|
+
includeTypes,
|
|
3842
|
+
excludeTypes,
|
|
3843
|
+
ignoreDependencies,
|
|
3844
|
+
missingContextPolicy: request.missingContextPolicy ?? "block",
|
|
3845
|
+
limit: request.limit,
|
|
3846
|
+
parallel: request.parallel,
|
|
3847
|
+
});
|
|
3848
|
+
pushSelectionWarnings(refreshedSelection.warnings);
|
|
3849
|
+
if (refreshedSelection.ordered.length > 0) {
|
|
3850
|
+
selection = refreshedSelection;
|
|
3851
|
+
}
|
|
3852
|
+
else {
|
|
3853
|
+
const fallbackSelection = await this.selectionService.selectTasks({
|
|
3854
|
+
projectKey: request.projectKey,
|
|
3855
|
+
epicKey: request.epicKey,
|
|
3856
|
+
storyKey: request.storyKey,
|
|
3857
|
+
taskKeys: request.taskKeys,
|
|
3858
|
+
statusFilter,
|
|
3859
|
+
ignoreStatusFilter,
|
|
3860
|
+
includeTypes,
|
|
3861
|
+
excludeTypes,
|
|
3862
|
+
ignoreDependencies: true,
|
|
3863
|
+
missingContextPolicy: request.missingContextPolicy ?? "block",
|
|
3864
|
+
parallel: request.parallel,
|
|
3865
|
+
});
|
|
3866
|
+
pushSelectionWarnings(fallbackSelection.warnings);
|
|
3867
|
+
const fallbackTask = this.selectMostDependedTask(fallbackSelection.ordered);
|
|
3868
|
+
if (fallbackTask) {
|
|
3869
|
+
selection = {
|
|
3870
|
+
...selection,
|
|
3871
|
+
project: selection.project ?? fallbackSelection.project,
|
|
3872
|
+
ordered: [fallbackTask],
|
|
3873
|
+
warnings: [
|
|
3874
|
+
...selection.warnings,
|
|
3875
|
+
`No dependency-ready tasks available; selected ${fallbackTask.task.key} from dependency-loop fallback.`,
|
|
3876
|
+
],
|
|
3877
|
+
};
|
|
3878
|
+
}
|
|
3879
|
+
}
|
|
3880
|
+
}
|
|
3881
|
+
}
|
|
3773
3882
|
await this.checkpoint(job.id, "selection", {
|
|
3774
3883
|
ordered: selection.ordered.map((t) => t.task.key),
|
|
3775
3884
|
});
|
|
@@ -4013,6 +4122,9 @@ export class WorkOnTasksService {
|
|
|
4013
4122
|
};
|
|
4014
4123
|
const maxSelectedTasks = typeof request.limit === "number" && request.limit > 0 ? request.limit : undefined;
|
|
4015
4124
|
const selectedTaskIds = new Set(selection.ordered.map((entry) => entry.task.id));
|
|
4125
|
+
const seenDocWarnings = new Set(warnings
|
|
4126
|
+
.map((warning) => sanitizeWarningText(warning))
|
|
4127
|
+
.filter((warning) => warning.length > 0));
|
|
4016
4128
|
const refreshSelectionQueue = async (processedCount) => {
|
|
4017
4129
|
if (maxSelectedTasks !== undefined && selectedTaskIds.size >= maxSelectedTasks)
|
|
4018
4130
|
return;
|
|
@@ -4045,6 +4157,37 @@ export class WorkOnTasksService {
|
|
|
4045
4157
|
break;
|
|
4046
4158
|
newlyEligible.push(entry);
|
|
4047
4159
|
}
|
|
4160
|
+
if (newlyEligible.length === 0 && !ignoreDependencies && !explicitTaskSelection) {
|
|
4161
|
+
const dependencyBlocked = refreshedSelection.warnings.some((warning) => warning.toLowerCase().includes("dependencies not ready"));
|
|
4162
|
+
if (dependencyBlocked) {
|
|
4163
|
+
const fallbackSelection = await this.selectionService.selectTasks({
|
|
4164
|
+
projectKey: request.projectKey,
|
|
4165
|
+
epicKey: request.epicKey,
|
|
4166
|
+
storyKey: request.storyKey,
|
|
4167
|
+
taskKeys: request.taskKeys,
|
|
4168
|
+
statusFilter,
|
|
4169
|
+
ignoreStatusFilter,
|
|
4170
|
+
includeTypes,
|
|
4171
|
+
excludeTypes,
|
|
4172
|
+
ignoreDependencies: true,
|
|
4173
|
+
missingContextPolicy: request.missingContextPolicy ?? "block",
|
|
4174
|
+
limit: remainingBudget,
|
|
4175
|
+
parallel: request.parallel,
|
|
4176
|
+
});
|
|
4177
|
+
pushSelectionWarnings(fallbackSelection.warnings);
|
|
4178
|
+
const fallbackTask = this.selectMostDependedTask(fallbackSelection.ordered.filter((entry) => !selectedTaskIds.has(entry.task.id)));
|
|
4179
|
+
if (fallbackTask) {
|
|
4180
|
+
const fallbackWarning = `No dependency-ready tasks available; selected ${fallbackTask.task.key} from dependency-loop fallback.`;
|
|
4181
|
+
selection = {
|
|
4182
|
+
...selection,
|
|
4183
|
+
project: selection.project ?? refreshedSelection.project ?? fallbackSelection.project,
|
|
4184
|
+
warnings: [...selection.warnings, fallbackWarning],
|
|
4185
|
+
};
|
|
4186
|
+
pushSelectionWarnings([fallbackWarning]);
|
|
4187
|
+
newlyEligible.push(fallbackTask);
|
|
4188
|
+
}
|
|
4189
|
+
}
|
|
4190
|
+
}
|
|
4048
4191
|
if (newlyEligible.length === 0)
|
|
4049
4192
|
return;
|
|
4050
4193
|
selection = {
|
|
@@ -4067,13 +4210,10 @@ export class WorkOnTasksService {
|
|
|
4067
4210
|
processedItems: processedCount,
|
|
4068
4211
|
});
|
|
4069
4212
|
};
|
|
4070
|
-
let abortRemainingReason = null;
|
|
4071
4213
|
taskLoop: for (let index = 0; index < selection.ordered.length; index += 1) {
|
|
4072
4214
|
const task = selection.ordered[index];
|
|
4073
4215
|
if (!task)
|
|
4074
4216
|
continue;
|
|
4075
|
-
if (abortRemainingReason)
|
|
4076
|
-
break taskLoop;
|
|
4077
4217
|
abortIfSignaled();
|
|
4078
4218
|
const startedAt = new Date().toISOString();
|
|
4079
4219
|
const taskRun = await this.deps.workspaceRepo.createTaskRun({
|
|
@@ -4793,8 +4933,18 @@ export class WorkOnTasksService {
|
|
|
4793
4933
|
docdexUnavailable = docContext.docdexUnavailable;
|
|
4794
4934
|
}
|
|
4795
4935
|
if (docWarnings.length) {
|
|
4796
|
-
|
|
4797
|
-
|
|
4936
|
+
const uniqueDocWarnings = [];
|
|
4937
|
+
for (const warning of docWarnings) {
|
|
4938
|
+
const normalized = sanitizeWarningText(warning);
|
|
4939
|
+
if (!normalized || seenDocWarnings.has(normalized))
|
|
4940
|
+
continue;
|
|
4941
|
+
seenDocWarnings.add(normalized);
|
|
4942
|
+
uniqueDocWarnings.push(normalized);
|
|
4943
|
+
}
|
|
4944
|
+
if (uniqueDocWarnings.length > 0) {
|
|
4945
|
+
warnings.push(...uniqueDocWarnings);
|
|
4946
|
+
await this.logTask(taskRun.id, uniqueDocWarnings.join("; "), "docdex");
|
|
4947
|
+
}
|
|
4798
4948
|
}
|
|
4799
4949
|
if (docdexUnavailable) {
|
|
4800
4950
|
const message = "Docdex unavailable; missing required context for this task.";
|
|
@@ -5187,13 +5337,9 @@ export class WorkOnTasksService {
|
|
|
5187
5337
|
});
|
|
5188
5338
|
const codaliReason = resolveCodaliFailureReason(message);
|
|
5189
5339
|
const failureNote = codaliReason ?? message;
|
|
5340
|
+
await this.stateService.markFailed(task.task, failureNote, statusContext);
|
|
5190
5341
|
setFailureReason(failureNote);
|
|
5191
5342
|
results.push({ taskKey: task.task.key, status: "failed", notes: failureNote });
|
|
5192
|
-
if (isAuthErrorMessage(message)) {
|
|
5193
|
-
abortRemainingReason = message;
|
|
5194
|
-
setFailureReason(AUTH_ERROR_REASON);
|
|
5195
|
-
warnings.push(`Auth/rate limit error detected; stopping after ${task.task.key}. ${message}`);
|
|
5196
|
-
}
|
|
5197
5343
|
taskStatus = "failed";
|
|
5198
5344
|
await this.deps.jobService.updateJobStatus(job.id, "running", { processedItems: index + 1 });
|
|
5199
5345
|
continue taskLoop;
|
|
@@ -6319,7 +6465,7 @@ export class WorkOnTasksService {
|
|
|
6319
6465
|
throw error;
|
|
6320
6466
|
}
|
|
6321
6467
|
finally {
|
|
6322
|
-
if (runVcsPhase && !vcsFinalized) {
|
|
6468
|
+
if (runVcsPhase && !vcsFinalized && taskStatus === "succeeded") {
|
|
6323
6469
|
try {
|
|
6324
6470
|
await runVcsPhase({ allowResultUpdate: false, reason: "finalize" });
|
|
6325
6471
|
}
|
|
@@ -6361,28 +6507,21 @@ export class WorkOnTasksService {
|
|
|
6361
6507
|
}
|
|
6362
6508
|
finally {
|
|
6363
6509
|
await emitTaskEndOnce();
|
|
6364
|
-
|
|
6365
|
-
|
|
6366
|
-
|
|
6367
|
-
|
|
6368
|
-
|
|
6369
|
-
warnings.push(`selection_refresh_failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
6370
|
-
}
|
|
6510
|
+
try {
|
|
6511
|
+
await refreshSelectionQueue(index + 1);
|
|
6512
|
+
}
|
|
6513
|
+
catch (error) {
|
|
6514
|
+
warnings.push(`selection_refresh_failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
6371
6515
|
}
|
|
6372
6516
|
}
|
|
6373
6517
|
}
|
|
6374
|
-
if (abortRemainingReason) {
|
|
6375
|
-
warnings.push(`Stopped remaining tasks due to auth/rate limit: ${abortRemainingReason}`);
|
|
6376
|
-
}
|
|
6377
6518
|
const failureCount = results.filter((r) => r.status === "failed").length;
|
|
6378
|
-
const state =
|
|
6379
|
-
? "
|
|
6380
|
-
: failureCount ===
|
|
6381
|
-
? "
|
|
6382
|
-
:
|
|
6383
|
-
|
|
6384
|
-
: "partial";
|
|
6385
|
-
const errorSummary = abortRemainingReason ?? (failureCount ? `${failureCount} task(s) failed` : undefined);
|
|
6519
|
+
const state = failureCount === 0
|
|
6520
|
+
? "completed"
|
|
6521
|
+
: failureCount === results.length
|
|
6522
|
+
? "failed"
|
|
6523
|
+
: "partial";
|
|
6524
|
+
const errorSummary = failureCount ? `${failureCount} task(s) failed` : undefined;
|
|
6386
6525
|
const summaryTasks = results.map((result) => {
|
|
6387
6526
|
const existing = taskSummaries.get(result.taskKey);
|
|
6388
6527
|
if (existing)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mcoda/core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.30",
|
|
4
4
|
"description": "Core services and APIs for the mcoda CLI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -32,11 +32,11 @@
|
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@apidevtools/swagger-parser": "^10.1.0",
|
|
34
34
|
"yaml": "^2.4.2",
|
|
35
|
-
"@mcoda/shared": "0.1.
|
|
36
|
-
"@mcoda/
|
|
37
|
-
"@mcoda/
|
|
38
|
-
"@mcoda/
|
|
39
|
-
"@mcoda/integrations": "0.1.
|
|
35
|
+
"@mcoda/shared": "0.1.30",
|
|
36
|
+
"@mcoda/db": "0.1.30",
|
|
37
|
+
"@mcoda/agents": "0.1.30",
|
|
38
|
+
"@mcoda/generators": "0.1.30",
|
|
39
|
+
"@mcoda/integrations": "0.1.30"
|
|
40
40
|
},
|
|
41
41
|
"scripts": {
|
|
42
42
|
"build": "tsc -p tsconfig.json",
|