@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.
@@ -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":"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"}
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;AAyFD,eAAO,MAAM,8BAA8B,GACzC,QAAQ,MAAM,EACd,eAAe,GAAG,CAAC,MAAM,CAAC,EAC1B,UAAU,MAAM,EAAE,KACjB,kBAAkB,EA2EpB,CAAC;AAEF,qBAAa,mBAAmB;IAE5B,OAAO,CAAC,SAAS;IACjB,OAAO,CAAC,EAAE;IACV,OAAO,CAAC,IAAI;IACZ,OAAO,CAAC,UAAU;IAClB,OAAO,CAAC,YAAY;IACpB,OAAO,CAAC,UAAU;IAClB,OAAO,CAAC,cAAc;IACtB,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,eAAe;IATzB,OAAO;IAYP,OAAO,CAAC,sBAAsB;IAY9B,OAAO,CAAC,uBAAuB;IAqD/B,OAAO,CAAC,4BAA4B;WAuBvB,MAAM,CACjB,SAAS,EAAE,mBAAmB,EAC9B,OAAO,GAAE;QAAE,eAAe,CAAC,EAAE,OAAO,CAAA;KAAO,GAC1C,OAAO,CAAC,mBAAmB,CAAC;YAsCjB,eAAe;IAgDvB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YAgBd,UAAU;YAQV,OAAO;YASP,QAAQ;YAeR,UAAU;YAkEV,iBAAiB;YA0BjB,kBAAkB;IAgBhC,OAAO,CAAC,mBAAmB;IA8B3B,OAAO,CAAC,qBAAqB;IAgB7B,OAAO,CAAC,cAAc;IAOtB,OAAO,CAAC,qBAAqB;IA8D7B,OAAO,CAAC,oBAAoB;IAkB5B,OAAO,CAAC,iBAAiB;IAkBzB,OAAO,CAAC,wBAAwB;IAYhC,OAAO,CAAC,oBAAoB;IA2D5B,OAAO,CAAC,yBAAyB;YAwBnB,oBAAoB;YAiCpB,uBAAuB;YAsDvB,4BAA4B;YAgI5B,yBAAyB;IAqFvC,OAAO,CAAC,YAAY;IAwDpB,OAAO,CAAC,eAAe;IAyDvB,OAAO,CAAC,UAAU;YAuCJ,YAAY;YASZ,WAAW;IAwBzB,OAAO,CAAC,iBAAiB;YAqCX,0BAA0B;YAmD1B,iBAAiB;IA0C/B,OAAO,CAAC,SAAS;IAyBX,UAAU,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,kBAAkB,CAAC;CAmT5E"}
@@ -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([...cycleMembers, ...agentCycleMembers]);
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":"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"}
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 warning of entries) {
3729
- const normalized = warning.trim();
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(warning);
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
- warnings.push(...docWarnings);
4797
- await this.logTask(taskRun.id, docWarnings.join("; "), "docdex");
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
- if (!abortRemainingReason) {
6365
- try {
6366
- await refreshSelectionQueue(index + 1);
6367
- }
6368
- catch (error) {
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 = abortRemainingReason
6379
- ? "failed"
6380
- : failureCount === 0
6381
- ? "completed"
6382
- : failureCount === results.length
6383
- ? "failed"
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.28",
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.28",
36
- "@mcoda/generators": "0.1.28",
37
- "@mcoda/db": "0.1.28",
38
- "@mcoda/agents": "0.1.28",
39
- "@mcoda/integrations": "0.1.28"
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",