@mcoda/core 0.1.22 → 0.1.23
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.
|
@@ -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;
|
|
1
|
+
{"version":3,"file":"TaskOrderingService.d.ts","sourceRoot":"","sources":["../../../src/services/backlog/TaskOrderingService.ts"],"names":[],"mappings":"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"}
|
|
@@ -472,6 +472,7 @@ export class TaskOrderingService {
|
|
|
472
472
|
SELECT
|
|
473
473
|
td.task_id,
|
|
474
474
|
td.depends_on_task_id,
|
|
475
|
+
td.relation_type,
|
|
475
476
|
dep.key as depends_on_key,
|
|
476
477
|
dep.status as depends_on_status
|
|
477
478
|
FROM task_dependencies td
|
|
@@ -646,47 +647,81 @@ export class TaskOrderingService {
|
|
|
646
647
|
return false;
|
|
647
648
|
}
|
|
648
649
|
async injectFoundationDependencies(tasks, deps, warnings, persist) {
|
|
650
|
+
const relationType = "inferred_foundation";
|
|
649
651
|
const classification = new Map();
|
|
650
652
|
for (const task of tasks) {
|
|
651
653
|
classification.set(task.id, this.resolveClassification(task));
|
|
652
654
|
}
|
|
653
655
|
const foundationTasks = tasks.filter((task) => classification.get(task.id)?.foundation);
|
|
654
656
|
const nonFoundationTasks = tasks.filter((task) => !classification.get(task.id)?.foundation);
|
|
655
|
-
|
|
656
|
-
|
|
657
|
+
let removedInferred = 0;
|
|
658
|
+
for (const task of tasks) {
|
|
659
|
+
const rows = deps.get(task.id) ?? [];
|
|
660
|
+
if (rows.length === 0)
|
|
661
|
+
continue;
|
|
662
|
+
const kept = rows.filter((row) => row.relation_type?.toLowerCase() !== relationType);
|
|
663
|
+
removedInferred += rows.length - kept.length;
|
|
664
|
+
deps.set(task.id, kept);
|
|
665
|
+
}
|
|
666
|
+
const foundationsByStory = new Map();
|
|
667
|
+
const foundationsByEpic = new Map();
|
|
668
|
+
for (const foundation of foundationTasks) {
|
|
669
|
+
const storyEntries = foundationsByStory.get(foundation.story_id) ?? [];
|
|
670
|
+
storyEntries.push(foundation);
|
|
671
|
+
foundationsByStory.set(foundation.story_id, storyEntries);
|
|
672
|
+
const epicEntries = foundationsByEpic.get(foundation.epic_id) ?? [];
|
|
673
|
+
epicEntries.push(foundation);
|
|
674
|
+
foundationsByEpic.set(foundation.epic_id, epicEntries);
|
|
675
|
+
}
|
|
676
|
+
const byRank = (a, b) => {
|
|
677
|
+
const priorityA = a.priority ?? Number.MAX_SAFE_INTEGER;
|
|
678
|
+
const priorityB = b.priority ?? Number.MAX_SAFE_INTEGER;
|
|
679
|
+
if (priorityA !== priorityB)
|
|
680
|
+
return priorityA - priorityB;
|
|
681
|
+
const createdA = Date.parse(a.created_at) || 0;
|
|
682
|
+
const createdB = Date.parse(b.created_at) || 0;
|
|
683
|
+
if (createdA !== createdB)
|
|
684
|
+
return createdA - createdB;
|
|
685
|
+
return a.key.localeCompare(b.key);
|
|
686
|
+
};
|
|
687
|
+
for (const list of foundationsByStory.values())
|
|
688
|
+
list.sort(byRank);
|
|
689
|
+
for (const list of foundationsByEpic.values())
|
|
690
|
+
list.sort(byRank);
|
|
657
691
|
const taskById = new Map(tasks.map((task) => [task.id, task]));
|
|
658
692
|
const dependencyGraph = this.buildDependencyGraph(tasks, deps);
|
|
659
693
|
const inserts = [];
|
|
660
694
|
let skippedCycles = 0;
|
|
661
695
|
const skippedEdges = [];
|
|
662
696
|
for (const task of nonFoundationTasks) {
|
|
663
|
-
const
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
skippedEdges.push(`${task.key}->${foundation.key}`);
|
|
675
|
-
}
|
|
676
|
-
continue;
|
|
697
|
+
const existingDeps = (deps.get(task.id) ?? []).filter((dep) => Boolean(dep.depends_on_task_id));
|
|
698
|
+
if (existingDeps.length > 0)
|
|
699
|
+
continue;
|
|
700
|
+
const scopedCandidates = foundationsByStory.get(task.story_id) ?? foundationsByEpic.get(task.epic_id) ?? [];
|
|
701
|
+
const foundation = scopedCandidates.find((candidate) => candidate.id !== task.id);
|
|
702
|
+
if (!foundation)
|
|
703
|
+
continue;
|
|
704
|
+
if (this.hasDependencyPath(dependencyGraph, foundation.id, task.id)) {
|
|
705
|
+
skippedCycles += 1;
|
|
706
|
+
if (skippedEdges.length < 5) {
|
|
707
|
+
skippedEdges.push(`${task.key}->${foundation.key}`);
|
|
677
708
|
}
|
|
678
|
-
|
|
679
|
-
taskId: task.id,
|
|
680
|
-
dependsOnTaskId: foundation.id,
|
|
681
|
-
relationType: "inferred_foundation",
|
|
682
|
-
});
|
|
683
|
-
existing.add(foundation.id);
|
|
684
|
-
const edges = dependencyGraph.get(task.id) ?? new Set();
|
|
685
|
-
edges.add(foundation.id);
|
|
686
|
-
dependencyGraph.set(task.id, edges);
|
|
709
|
+
continue;
|
|
687
710
|
}
|
|
711
|
+
inserts.push({
|
|
712
|
+
taskId: task.id,
|
|
713
|
+
dependsOnTaskId: foundation.id,
|
|
714
|
+
relationType,
|
|
715
|
+
});
|
|
716
|
+
const edges = dependencyGraph.get(task.id) ?? new Set();
|
|
717
|
+
edges.add(foundation.id);
|
|
718
|
+
dependencyGraph.set(task.id, edges);
|
|
688
719
|
}
|
|
689
|
-
if (
|
|
720
|
+
if (persist && removedInferred > 0) {
|
|
721
|
+
const placeholders = tasks.map(() => "?").join(", ");
|
|
722
|
+
await this.db.run(`DELETE FROM task_dependencies WHERE relation_type = ? AND task_id IN (${placeholders})`, relationType, ...tasks.map((task) => task.id));
|
|
723
|
+
}
|
|
724
|
+
if (inserts.length === 0 && removedInferred === 0) {
|
|
690
725
|
if (skippedCycles > 0) {
|
|
691
726
|
warnings.push(`Skipped ${skippedCycles} inferred foundation deps due to cycles.`);
|
|
692
727
|
if (skippedEdges.length > 0) {
|
|
@@ -704,16 +739,25 @@ export class TaskOrderingService {
|
|
|
704
739
|
depList.push({
|
|
705
740
|
task_id: insert.taskId,
|
|
706
741
|
depends_on_task_id: insert.dependsOnTaskId,
|
|
742
|
+
relation_type: relationType,
|
|
707
743
|
depends_on_key: dependsOn?.key,
|
|
708
744
|
depends_on_status: dependsOn?.status,
|
|
709
745
|
});
|
|
710
746
|
deps.set(insert.taskId, depList);
|
|
711
747
|
}
|
|
712
|
-
if (persist) {
|
|
713
|
-
warnings.push(`
|
|
748
|
+
if (persist && removedInferred > 0) {
|
|
749
|
+
warnings.push(`Reconciled ${removedInferred} inferred foundation deps before re-inference.`);
|
|
714
750
|
}
|
|
715
|
-
else {
|
|
716
|
-
warnings.push(`Dry run:
|
|
751
|
+
else if (!persist && removedInferred > 0) {
|
|
752
|
+
warnings.push(`Dry run: would reconcile ${removedInferred} inferred foundation deps.`);
|
|
753
|
+
}
|
|
754
|
+
if (inserts.length > 0) {
|
|
755
|
+
if (persist) {
|
|
756
|
+
warnings.push(`Injected ${inserts.length} scoped inferred foundation deps.`);
|
|
757
|
+
}
|
|
758
|
+
else {
|
|
759
|
+
warnings.push(`Dry run: inferred ${inserts.length} scoped foundation deps (not persisted).`);
|
|
760
|
+
}
|
|
717
761
|
}
|
|
718
762
|
if (skippedCycles > 0) {
|
|
719
763
|
warnings.push(`Skipped ${skippedCycles} inferred foundation deps due to cycles.`);
|
|
@@ -782,6 +826,7 @@ export class TaskOrderingService {
|
|
|
782
826
|
depList.push({
|
|
783
827
|
task_id: insert.taskId,
|
|
784
828
|
depends_on_task_id: insert.dependsOnTaskId,
|
|
829
|
+
relation_type: "inferred_agent",
|
|
785
830
|
depends_on_key: dependsOn?.key,
|
|
786
831
|
depends_on_status: dependsOn?.status,
|
|
787
832
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mcoda/core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.23",
|
|
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/
|
|
36
|
-
"@mcoda/
|
|
37
|
-
"@mcoda/generators": "0.1.
|
|
38
|
-
"@mcoda/
|
|
39
|
-
"@mcoda/
|
|
35
|
+
"@mcoda/shared": "0.1.23",
|
|
36
|
+
"@mcoda/db": "0.1.23",
|
|
37
|
+
"@mcoda/generators": "0.1.23",
|
|
38
|
+
"@mcoda/agents": "0.1.23",
|
|
39
|
+
"@mcoda/integrations": "0.1.23"
|
|
40
40
|
},
|
|
41
41
|
"scripts": {
|
|
42
42
|
"build": "tsc -p tsconfig.json",
|