@mcoda/core 0.1.8 → 0.1.11

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.
Files changed (216) hide show
  1. package/CHANGELOG.md +3 -0
  2. package/README.md +2 -2
  3. package/dist/api/AgentsApi.d.ts +9 -1
  4. package/dist/api/AgentsApi.d.ts.map +1 -1
  5. package/dist/api/AgentsApi.js +201 -6
  6. package/dist/api/QaTasksApi.d.ts.map +1 -1
  7. package/dist/api/QaTasksApi.js +6 -0
  8. package/dist/api/TasksApi.d.ts.map +1 -1
  9. package/dist/api/TasksApi.js +1 -0
  10. package/dist/index.d.ts +4 -0
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +4 -0
  13. package/dist/prompts/PdrPrompts.d.ts.map +1 -1
  14. package/dist/prompts/PdrPrompts.js +9 -1
  15. package/dist/prompts/SdsPrompts.d.ts.map +1 -1
  16. package/dist/prompts/SdsPrompts.js +9 -0
  17. package/dist/services/agents/AgentRatingFormula.d.ts +27 -0
  18. package/dist/services/agents/AgentRatingFormula.d.ts.map +1 -0
  19. package/dist/services/agents/AgentRatingFormula.js +45 -0
  20. package/dist/services/agents/AgentRatingService.d.ts +60 -0
  21. package/dist/services/agents/AgentRatingService.d.ts.map +1 -0
  22. package/dist/services/agents/AgentRatingService.js +363 -0
  23. package/dist/services/agents/GatewayAgentService.d.ts +11 -0
  24. package/dist/services/agents/GatewayAgentService.d.ts.map +1 -1
  25. package/dist/services/agents/GatewayAgentService.js +525 -84
  26. package/dist/services/agents/GatewayHandoff.d.ts +11 -0
  27. package/dist/services/agents/GatewayHandoff.d.ts.map +1 -0
  28. package/dist/services/agents/GatewayHandoff.js +141 -0
  29. package/dist/services/agents/RoutingService.d.ts +1 -0
  30. package/dist/services/agents/RoutingService.d.ts.map +1 -1
  31. package/dist/services/agents/RoutingService.js +4 -4
  32. package/dist/services/backlog/BacklogService.d.ts +23 -0
  33. package/dist/services/backlog/BacklogService.d.ts.map +1 -1
  34. package/dist/services/backlog/BacklogService.js +62 -7
  35. package/dist/services/backlog/TaskOrderingHeuristics.d.ts +12 -0
  36. package/dist/services/backlog/TaskOrderingHeuristics.d.ts.map +1 -0
  37. package/dist/services/backlog/TaskOrderingHeuristics.js +56 -0
  38. package/dist/services/backlog/TaskOrderingService.d.ts +17 -4
  39. package/dist/services/backlog/TaskOrderingService.d.ts.map +1 -1
  40. package/dist/services/backlog/TaskOrderingService.js +538 -79
  41. package/dist/services/docs/DocInventory.d.ts +11 -0
  42. package/dist/services/docs/DocInventory.d.ts.map +1 -0
  43. package/dist/services/docs/DocInventory.js +230 -0
  44. package/dist/services/docs/DocgenRunContext.d.ts +59 -0
  45. package/dist/services/docs/DocgenRunContext.d.ts.map +1 -0
  46. package/dist/services/docs/DocgenRunContext.js +4 -0
  47. package/dist/services/docs/DocsService.d.ts +70 -3
  48. package/dist/services/docs/DocsService.d.ts.map +1 -1
  49. package/dist/services/docs/DocsService.js +1930 -89
  50. package/dist/services/docs/alignment/DocAlignmentGraph.d.ts +23 -0
  51. package/dist/services/docs/alignment/DocAlignmentGraph.d.ts.map +1 -0
  52. package/dist/services/docs/alignment/DocAlignmentGraph.js +78 -0
  53. package/dist/services/docs/alignment/DocAlignmentPatcher.d.ts +19 -0
  54. package/dist/services/docs/alignment/DocAlignmentPatcher.d.ts.map +1 -0
  55. package/dist/services/docs/alignment/DocAlignmentPatcher.js +222 -0
  56. package/dist/services/docs/patch/DocPatchEngine.d.ts +57 -0
  57. package/dist/services/docs/patch/DocPatchEngine.d.ts.map +1 -0
  58. package/dist/services/docs/patch/DocPatchEngine.js +331 -0
  59. package/dist/services/docs/review/Glossary.d.ts +16 -0
  60. package/dist/services/docs/review/Glossary.d.ts.map +1 -0
  61. package/dist/services/docs/review/Glossary.js +47 -0
  62. package/dist/services/docs/review/ReviewReportRenderer.d.ts +3 -0
  63. package/dist/services/docs/review/ReviewReportRenderer.d.ts.map +1 -0
  64. package/dist/services/docs/review/ReviewReportRenderer.js +133 -0
  65. package/dist/services/docs/review/ReviewReportSchema.d.ts +39 -0
  66. package/dist/services/docs/review/ReviewReportSchema.d.ts.map +1 -0
  67. package/dist/services/docs/review/ReviewReportSchema.js +47 -0
  68. package/dist/services/docs/review/ReviewTypes.d.ts +76 -0
  69. package/dist/services/docs/review/ReviewTypes.d.ts.map +1 -0
  70. package/dist/services/docs/review/ReviewTypes.js +94 -0
  71. package/dist/services/docs/review/gates/AdminOpenApiSpecGate.d.ts +7 -0
  72. package/dist/services/docs/review/gates/AdminOpenApiSpecGate.d.ts.map +1 -0
  73. package/dist/services/docs/review/gates/AdminOpenApiSpecGate.js +93 -0
  74. package/dist/services/docs/review/gates/ApiPathConsistencyGate.d.ts +7 -0
  75. package/dist/services/docs/review/gates/ApiPathConsistencyGate.d.ts.map +1 -0
  76. package/dist/services/docs/review/gates/ApiPathConsistencyGate.js +308 -0
  77. package/dist/services/docs/review/gates/BuildReadyCompletenessGate.d.ts +8 -0
  78. package/dist/services/docs/review/gates/BuildReadyCompletenessGate.d.ts.map +1 -0
  79. package/dist/services/docs/review/gates/BuildReadyCompletenessGate.js +278 -0
  80. package/dist/services/docs/review/gates/DeploymentBlueprintGate.d.ts +8 -0
  81. package/dist/services/docs/review/gates/DeploymentBlueprintGate.d.ts.map +1 -0
  82. package/dist/services/docs/review/gates/DeploymentBlueprintGate.js +487 -0
  83. package/dist/services/docs/review/gates/NoMaybesGate.d.ts +8 -0
  84. package/dist/services/docs/review/gates/NoMaybesGate.d.ts.map +1 -0
  85. package/dist/services/docs/review/gates/NoMaybesGate.js +145 -0
  86. package/dist/services/docs/review/gates/OpenApiCoverageGate.d.ts +7 -0
  87. package/dist/services/docs/review/gates/OpenApiCoverageGate.d.ts.map +1 -0
  88. package/dist/services/docs/review/gates/OpenApiCoverageGate.js +266 -0
  89. package/dist/services/docs/review/gates/OpenApiSchemaSanityGate.d.ts +7 -0
  90. package/dist/services/docs/review/gates/OpenApiSchemaSanityGate.d.ts.map +1 -0
  91. package/dist/services/docs/review/gates/OpenApiSchemaSanityGate.js +59 -0
  92. package/dist/services/docs/review/gates/OpenQuestionsGate.d.ts +7 -0
  93. package/dist/services/docs/review/gates/OpenQuestionsGate.d.ts.map +1 -0
  94. package/dist/services/docs/review/gates/OpenQuestionsGate.js +200 -0
  95. package/dist/services/docs/review/gates/PdrInterfacesGate.d.ts +7 -0
  96. package/dist/services/docs/review/gates/PdrInterfacesGate.d.ts.map +1 -0
  97. package/dist/services/docs/review/gates/PdrInterfacesGate.js +159 -0
  98. package/dist/services/docs/review/gates/PdrOpenQuestionsGate.d.ts +8 -0
  99. package/dist/services/docs/review/gates/PdrOpenQuestionsGate.d.ts.map +1 -0
  100. package/dist/services/docs/review/gates/PdrOpenQuestionsGate.js +129 -0
  101. package/dist/services/docs/review/gates/PdrOwnershipGate.d.ts +7 -0
  102. package/dist/services/docs/review/gates/PdrOwnershipGate.d.ts.map +1 -0
  103. package/dist/services/docs/review/gates/PdrOwnershipGate.js +169 -0
  104. package/dist/services/docs/review/gates/PlaceholderArtifactGate.d.ts +10 -0
  105. package/dist/services/docs/review/gates/PlaceholderArtifactGate.d.ts.map +1 -0
  106. package/dist/services/docs/review/gates/PlaceholderArtifactGate.js +261 -0
  107. package/dist/services/docs/review/gates/RfpConsentGate.d.ts +6 -0
  108. package/dist/services/docs/review/gates/RfpConsentGate.d.ts.map +1 -0
  109. package/dist/services/docs/review/gates/RfpConsentGate.js +127 -0
  110. package/dist/services/docs/review/gates/RfpDefinitionGate.d.ts +7 -0
  111. package/dist/services/docs/review/gates/RfpDefinitionGate.d.ts.map +1 -0
  112. package/dist/services/docs/review/gates/RfpDefinitionGate.js +173 -0
  113. package/dist/services/docs/review/gates/SdsAdaptersGate.d.ts +7 -0
  114. package/dist/services/docs/review/gates/SdsAdaptersGate.d.ts.map +1 -0
  115. package/dist/services/docs/review/gates/SdsAdaptersGate.js +196 -0
  116. package/dist/services/docs/review/gates/SdsDecisionsGate.d.ts +7 -0
  117. package/dist/services/docs/review/gates/SdsDecisionsGate.d.ts.map +1 -0
  118. package/dist/services/docs/review/gates/SdsDecisionsGate.js +89 -0
  119. package/dist/services/docs/review/gates/SdsOpsGate.d.ts +7 -0
  120. package/dist/services/docs/review/gates/SdsOpsGate.d.ts.map +1 -0
  121. package/dist/services/docs/review/gates/SdsOpsGate.js +162 -0
  122. package/dist/services/docs/review/gates/SdsPolicyTelemetryGate.d.ts +7 -0
  123. package/dist/services/docs/review/gates/SdsPolicyTelemetryGate.d.ts.map +1 -0
  124. package/dist/services/docs/review/gates/SdsPolicyTelemetryGate.js +166 -0
  125. package/dist/services/docs/review/gates/SqlRequiredTablesGate.d.ts +7 -0
  126. package/dist/services/docs/review/gates/SqlRequiredTablesGate.d.ts.map +1 -0
  127. package/dist/services/docs/review/gates/SqlRequiredTablesGate.js +273 -0
  128. package/dist/services/docs/review/gates/SqlSyntaxGate.d.ts +7 -0
  129. package/dist/services/docs/review/gates/SqlSyntaxGate.d.ts.map +1 -0
  130. package/dist/services/docs/review/gates/SqlSyntaxGate.js +203 -0
  131. package/dist/services/docs/review/gates/TerminologyNormalizationGate.d.ts +9 -0
  132. package/dist/services/docs/review/gates/TerminologyNormalizationGate.d.ts.map +1 -0
  133. package/dist/services/docs/review/gates/TerminologyNormalizationGate.js +217 -0
  134. package/dist/services/docs/review/glossary.json +47 -0
  135. package/dist/services/estimate/EstimateService.d.ts +2 -0
  136. package/dist/services/estimate/EstimateService.d.ts.map +1 -1
  137. package/dist/services/estimate/EstimateService.js +66 -18
  138. package/dist/services/estimate/VelocityService.d.ts +4 -0
  139. package/dist/services/estimate/VelocityService.d.ts.map +1 -1
  140. package/dist/services/estimate/VelocityService.js +179 -36
  141. package/dist/services/estimate/types.d.ts +1 -0
  142. package/dist/services/estimate/types.d.ts.map +1 -1
  143. package/dist/services/execution/GatewayTrioService.d.ts +200 -0
  144. package/dist/services/execution/GatewayTrioService.d.ts.map +1 -0
  145. package/dist/services/execution/GatewayTrioService.js +2492 -0
  146. package/dist/services/execution/QaApiRunner.d.ts +30 -0
  147. package/dist/services/execution/QaApiRunner.d.ts.map +1 -0
  148. package/dist/services/execution/QaApiRunner.js +881 -0
  149. package/dist/services/execution/QaFollowupService.d.ts +2 -0
  150. package/dist/services/execution/QaFollowupService.d.ts.map +1 -1
  151. package/dist/services/execution/QaFollowupService.js +9 -2
  152. package/dist/services/execution/QaPlanValidator.d.ts +10 -0
  153. package/dist/services/execution/QaPlanValidator.d.ts.map +1 -0
  154. package/dist/services/execution/QaPlanValidator.js +128 -0
  155. package/dist/services/execution/QaProfileService.d.ts +27 -1
  156. package/dist/services/execution/QaProfileService.d.ts.map +1 -1
  157. package/dist/services/execution/QaProfileService.js +354 -7
  158. package/dist/services/execution/QaTasksService.d.ts +59 -1
  159. package/dist/services/execution/QaTasksService.d.ts.map +1 -1
  160. package/dist/services/execution/QaTasksService.js +3347 -318
  161. package/dist/services/execution/QaTestCommandBuilder.d.ts +51 -0
  162. package/dist/services/execution/QaTestCommandBuilder.d.ts.map +1 -0
  163. package/dist/services/execution/QaTestCommandBuilder.js +495 -0
  164. package/dist/services/execution/TaskSelectionService.d.ts +4 -2
  165. package/dist/services/execution/TaskSelectionService.d.ts.map +1 -1
  166. package/dist/services/execution/TaskSelectionService.js +144 -28
  167. package/dist/services/execution/TaskStateService.d.ts +19 -6
  168. package/dist/services/execution/TaskStateService.d.ts.map +1 -1
  169. package/dist/services/execution/TaskStateService.js +128 -13
  170. package/dist/services/execution/WorkOnTasksService.d.ts +32 -1
  171. package/dist/services/execution/WorkOnTasksService.d.ts.map +1 -1
  172. package/dist/services/execution/WorkOnTasksService.js +4667 -722
  173. package/dist/services/jobs/JobInsightsService.d.ts +4 -0
  174. package/dist/services/jobs/JobInsightsService.d.ts.map +1 -1
  175. package/dist/services/jobs/JobInsightsService.js +51 -5
  176. package/dist/services/jobs/JobResumeService.d.ts.map +1 -1
  177. package/dist/services/jobs/JobResumeService.js +23 -10
  178. package/dist/services/jobs/JobService.d.ts +56 -4
  179. package/dist/services/jobs/JobService.d.ts.map +1 -1
  180. package/dist/services/jobs/JobService.js +232 -1
  181. package/dist/services/openapi/OpenApiService.d.ts +51 -0
  182. package/dist/services/openapi/OpenApiService.d.ts.map +1 -1
  183. package/dist/services/openapi/OpenApiService.js +953 -106
  184. package/dist/services/planning/CreateTasksService.d.ts +21 -0
  185. package/dist/services/planning/CreateTasksService.d.ts.map +1 -1
  186. package/dist/services/planning/CreateTasksService.js +569 -31
  187. package/dist/services/planning/RefineTasksService.d.ts +9 -0
  188. package/dist/services/planning/RefineTasksService.d.ts.map +1 -1
  189. package/dist/services/planning/RefineTasksService.js +409 -59
  190. package/dist/services/review/CodeReviewService.d.ts +18 -0
  191. package/dist/services/review/CodeReviewService.d.ts.map +1 -1
  192. package/dist/services/review/CodeReviewService.js +1309 -167
  193. package/dist/services/review/ReviewNormalizer.d.ts +9 -0
  194. package/dist/services/review/ReviewNormalizer.d.ts.map +1 -0
  195. package/dist/services/review/ReviewNormalizer.js +147 -0
  196. package/dist/services/shared/AuthErrors.d.ts +3 -0
  197. package/dist/services/shared/AuthErrors.d.ts.map +1 -0
  198. package/dist/services/shared/AuthErrors.js +17 -0
  199. package/dist/services/shared/DocdexGuidance.d.ts +7 -0
  200. package/dist/services/shared/DocdexGuidance.d.ts.map +1 -0
  201. package/dist/services/shared/DocdexGuidance.js +12 -0
  202. package/dist/services/shared/ProjectGuidance.d.ts +17 -0
  203. package/dist/services/shared/ProjectGuidance.d.ts.map +1 -0
  204. package/dist/services/shared/ProjectGuidance.js +78 -0
  205. package/dist/services/system/ToolDenylist.d.ts +13 -0
  206. package/dist/services/system/ToolDenylist.d.ts.map +1 -0
  207. package/dist/services/system/ToolDenylist.js +85 -0
  208. package/dist/services/tasks/TaskCommentFormatter.d.ts +20 -0
  209. package/dist/services/tasks/TaskCommentFormatter.d.ts.map +1 -0
  210. package/dist/services/tasks/TaskCommentFormatter.js +54 -0
  211. package/dist/services/telemetry/TelemetryService.d.ts.map +1 -1
  212. package/dist/services/telemetry/TelemetryService.js +39 -7
  213. package/dist/workspace/WorkspaceManager.d.ts +26 -0
  214. package/dist/workspace/WorkspaceManager.d.ts.map +1 -1
  215. package/dist/workspace/WorkspaceManager.js +206 -32
  216. package/package.json +6 -5
@@ -2,58 +2,162 @@ import path from "node:path";
2
2
  import { promises as fs } from "node:fs";
3
3
  import { AgentService } from "@mcoda/agents";
4
4
  import { DocdexClient } from "@mcoda/integrations";
5
+ import { READY_TO_CODE_REVIEW } from "@mcoda/shared";
5
6
  import { GlobalRepository, WorkspaceRepository } from "@mcoda/db";
7
+ import { getCommandRequiredCapabilities, } from "@mcoda/shared";
6
8
  import { JobService } from "../jobs/JobService.js";
7
9
  import { RoutingService } from "../agents/RoutingService.js";
10
+ import { AgentRatingService } from "../agents/AgentRatingService.js";
11
+ import { classifyTask } from "../backlog/TaskOrderingHeuristics.js";
12
+ import { TaskOrderingService } from "../backlog/TaskOrderingService.js";
8
13
  import { createTaskKeyGenerator } from "./KeyHelpers.js";
9
14
  const DEFAULT_STRATEGY = "auto";
10
- const FORBIDDEN_TARGET_STATUSES = new Set(["ready_to_review", "ready_to_qa", "completed"]);
15
+ const FORBIDDEN_TARGET_STATUSES = new Set([READY_TO_CODE_REVIEW, "ready_to_qa", "completed"]);
16
+ const normalizeCreateStatus = (status) => {
17
+ if (!status)
18
+ return "not_started";
19
+ return status.toLowerCase();
20
+ };
11
21
  const DEFAULT_MAX_TASKS = 250;
12
22
  const MAX_AGENT_OUTPUT_CHARS = 10000000;
13
23
  const estimateTokens = (text) => Math.max(1, Math.ceil(text.length / 4));
14
24
  const extractJson = (raw) => {
25
+ const fencedMatches = [...raw.matchAll(/```json([\s\S]*?)```/g)].map((match) => match[1]);
26
+ const stripped = raw.replace(/<think>[\s\S]*?<\/think>/g, "");
27
+ const candidates = [...fencedMatches, stripped, raw].filter((candidate) => candidate.trim().length > 0);
28
+ for (const candidate of candidates) {
29
+ const parsed = tryParseJson(candidate);
30
+ if (parsed !== undefined)
31
+ return parsed;
32
+ }
33
+ return undefined;
34
+ };
35
+ const tryParseJson = (value) => {
15
36
  try {
16
- const fenced = raw.match(/```json([\s\S]*?)```/);
17
- const candidate = fenced ? fenced[1] : raw;
18
- const start = candidate.indexOf("{");
19
- const end = candidate.lastIndexOf("}");
20
- if (start === -1 || end === -1 || end <= start)
21
- return JSON.parse(raw);
22
- return JSON.parse(candidate.slice(start, end + 1));
37
+ return JSON.parse(value);
23
38
  }
24
39
  catch {
40
+ // continue
41
+ }
42
+ const objects = extractJsonObjects(value).reverse();
43
+ for (const obj of objects) {
44
+ try {
45
+ return JSON.parse(obj);
46
+ }
47
+ catch {
48
+ // continue
49
+ }
50
+ }
51
+ return undefined;
52
+ };
53
+ const extractJsonObjects = (value) => {
54
+ const results = [];
55
+ let depth = 0;
56
+ let start = -1;
57
+ let inString = false;
58
+ let escaped = false;
59
+ for (let i = 0; i < value.length; i += 1) {
60
+ const ch = value[i];
61
+ if (inString) {
62
+ if (escaped) {
63
+ escaped = false;
64
+ continue;
65
+ }
66
+ if (ch === "\\") {
67
+ escaped = true;
68
+ continue;
69
+ }
70
+ if (ch === "\"")
71
+ inString = false;
72
+ continue;
73
+ }
74
+ if (ch === "\"") {
75
+ inString = true;
76
+ continue;
77
+ }
78
+ if (ch === "{") {
79
+ if (depth === 0)
80
+ start = i;
81
+ depth += 1;
82
+ }
83
+ else if (ch === "}") {
84
+ if (depth === 0)
85
+ continue;
86
+ depth -= 1;
87
+ if (depth === 0 && start >= 0) {
88
+ results.push(value.slice(start, i + 1));
89
+ start = -1;
90
+ }
91
+ }
92
+ }
93
+ return results;
94
+ };
95
+ const normalizePlanJson = (parsed) => {
96
+ if (!parsed)
25
97
  return undefined;
98
+ if (Array.isArray(parsed)) {
99
+ return { operations: parsed };
26
100
  }
101
+ if (Array.isArray(parsed.operations))
102
+ return parsed;
103
+ if (parsed.op) {
104
+ return { operations: [parsed] };
105
+ }
106
+ return undefined;
27
107
  };
28
108
  const normalizeOperation = (op) => {
29
109
  if (!op || typeof op !== "object")
30
110
  return op;
31
- if (op.op !== "update_task")
32
- return op;
111
+ const opType = op.op;
33
112
  const taskKey = op.taskKey ?? op.key ?? op.task ?? op.targetTaskKey ?? null;
34
- const updates = { ...op.updates };
35
- const inlineFields = ["title", "description", "acceptanceCriteria", "type", "status", "storyPoints", "priority", "dependsOn", "metadata"];
36
- for (const field of inlineFields) {
37
- if (op[field] !== undefined && updates[field] === undefined) {
38
- updates[field] = op[field];
113
+ if (opType === "update_task") {
114
+ const updates = { ...op.updates };
115
+ const inlineFields = [
116
+ "title",
117
+ "description",
118
+ "acceptanceCriteria",
119
+ "type",
120
+ "status",
121
+ "storyPoints",
122
+ "priority",
123
+ "dependsOn",
124
+ "metadata",
125
+ ];
126
+ for (const field of inlineFields) {
127
+ if (op[field] !== undefined && updates[field] === undefined) {
128
+ updates[field] = op[field];
129
+ }
39
130
  }
131
+ return {
132
+ ...op,
133
+ taskKey,
134
+ updates,
135
+ };
136
+ }
137
+ if (opType === "split_task") {
138
+ const children = Array.isArray(op.children)
139
+ ? op.children
140
+ : Array.isArray(op.subtasks)
141
+ ? op.subtasks
142
+ : Array.isArray(op.newTasks)
143
+ ? op.newTasks
144
+ : Array.isArray(op.tasks)
145
+ ? op.tasks
146
+ : undefined;
147
+ return {
148
+ ...op,
149
+ taskKey,
150
+ children,
151
+ };
40
152
  }
41
153
  return {
42
154
  ...op,
43
155
  taskKey,
44
- updates,
45
156
  };
46
157
  };
47
158
  const safeParsePlan = (content) => {
48
- try {
49
- const parsed = JSON.parse(content);
50
- if (parsed && Array.isArray(parsed.operations))
51
- return parsed;
52
- }
53
- catch {
54
- /* ignore */
55
- }
56
- return undefined;
159
+ const parsed = extractJson(content);
160
+ return normalizePlanJson(parsed);
57
161
  };
58
162
  const formatTaskSummary = (task) => {
59
163
  return [
@@ -73,14 +177,17 @@ export class RefineTasksService {
73
177
  this.repo = deps.repo;
74
178
  this.workspaceRepo = deps.workspaceRepo;
75
179
  this.routingService = deps.routingService;
180
+ this.ratingService = deps.ratingService;
76
181
  }
77
182
  static async create(workspace) {
78
183
  const repo = await GlobalRepository.create();
79
184
  const agentService = new AgentService(repo);
80
185
  const routingService = await RoutingService.create();
186
+ const docdexRepoId = workspace.config?.docdexRepoId ?? process.env.MCODA_DOCDEX_REPO_ID ?? process.env.DOCDEX_REPO_ID;
81
187
  const docdex = new DocdexClient({
82
188
  workspaceRoot: workspace.workspaceRoot,
83
189
  baseUrl: workspace.config?.docdexUrl ?? process.env.MCODA_DOCDEX_URL,
190
+ repoId: docdexRepoId,
84
191
  });
85
192
  const workspaceRepo = await WorkspaceRepository.create(workspace.workspaceRoot);
86
193
  const jobService = new JobService(workspace, workspaceRepo);
@@ -111,13 +218,93 @@ export class RefineTasksService {
111
218
  await tryClose(this.routingService);
112
219
  await tryClose(this.docdex);
113
220
  }
221
+ async seedPriorities(projectKey) {
222
+ const ordering = await TaskOrderingService.create(this.workspace, { recordTelemetry: false });
223
+ try {
224
+ await ordering.orderTasks({
225
+ projectKey,
226
+ });
227
+ }
228
+ finally {
229
+ await ordering.close();
230
+ }
231
+ }
114
232
  async resolveAgent(agentName) {
115
- const resolved = await this.routingService.resolveAgentForCommand({
116
- workspace: this.workspace,
117
- commandName: "refine-tasks",
118
- overrideAgentSlug: agentName,
233
+ try {
234
+ const resolved = await this.routingService.resolveAgentForCommand({
235
+ workspace: this.workspace,
236
+ commandName: "refine-tasks",
237
+ overrideAgentSlug: agentName,
238
+ });
239
+ return resolved.agent;
240
+ }
241
+ catch (error) {
242
+ const message = error.message ?? "";
243
+ if (!/No routing defaults/i.test(message)) {
244
+ throw error;
245
+ }
246
+ const requiredCaps = getCommandRequiredCapabilities("refine-tasks");
247
+ const fallback = await this.selectFallbackAgent(requiredCaps);
248
+ if (fallback)
249
+ return fallback;
250
+ throw new Error(`No routing defaults found for command refine-tasks. ` +
251
+ `Set a default agent (mcoda agent set-default <NAME> --workspace <PATH>) ` +
252
+ `or pass --agent <NAME> with ${requiredCaps.length ? `capabilities: ${requiredCaps.join(", ")}` : "required capabilities"}.`);
253
+ }
254
+ }
255
+ async selectFallbackAgent(requiredCaps) {
256
+ const agents = await this.repo.listAgents();
257
+ if (!agents.length)
258
+ return undefined;
259
+ let healthRows = [];
260
+ try {
261
+ healthRows = await this.repo.listAgentHealthSummary();
262
+ }
263
+ catch {
264
+ healthRows = [];
265
+ }
266
+ const healthById = new Map(healthRows.map((row) => [row.agentId, row]));
267
+ const candidates = [];
268
+ for (const agent of agents) {
269
+ const health = healthById.get(agent.id);
270
+ if (health?.status === "unreachable")
271
+ continue;
272
+ const caps = await this.repo.getAgentCapabilities(agent.id);
273
+ const hasCaps = requiredCaps.every((cap) => caps.includes(cap));
274
+ candidates.push({
275
+ agent,
276
+ rating: Number(agent.rating ?? 0),
277
+ reasoning: Number(agent.reasoningRating ?? 0),
278
+ cost: Number(agent.costPerMillion ?? 0),
279
+ hasCaps,
280
+ slug: agent.slug ?? agent.id,
281
+ });
282
+ }
283
+ if (!candidates.length)
284
+ return undefined;
285
+ const eligible = candidates.filter((c) => c.hasCaps);
286
+ const pool = eligible.length ? eligible : candidates;
287
+ pool.sort((a, b) => {
288
+ if (b.rating !== a.rating)
289
+ return b.rating - a.rating;
290
+ if (b.reasoning !== a.reasoning)
291
+ return b.reasoning - a.reasoning;
292
+ if (a.cost !== b.cost)
293
+ return a.cost - b.cost;
294
+ return a.slug.localeCompare(b.slug);
119
295
  });
120
- return resolved.agent;
296
+ return pool[0]?.agent;
297
+ }
298
+ ensureRatingService() {
299
+ if (!this.ratingService) {
300
+ this.ratingService = new AgentRatingService(this.workspace, {
301
+ workspaceRepo: this.workspaceRepo,
302
+ globalRepo: this.repo,
303
+ agentService: this.agentService,
304
+ routingService: this.routingService,
305
+ });
306
+ }
307
+ return this.ratingService;
121
308
  }
122
309
  async selectTasks(projectKey, filters) {
123
310
  const db = this.workspaceRepo.getDb();
@@ -361,6 +548,7 @@ export class RefineTasksService {
361
548
  const updates = seed?.updates ?? seed?.fields ?? {};
362
549
  const epic = await ensureEpic();
363
550
  const story = await ensureStory(epic.id);
551
+ const status = normalizeCreateStatus(updates.status);
364
552
  const [task] = await this.workspaceRepo.insertTasks([
365
553
  {
366
554
  projectId,
@@ -370,7 +558,7 @@ export class RefineTasksService {
370
558
  title: updates.title ?? `Task ${taskKey}`,
371
559
  description: updates.description ?? "",
372
560
  type: updates.type ?? "feature",
373
- status: updates.status ?? "not_started",
561
+ status,
374
562
  storyPoints: updates.storyPoints ?? null,
375
563
  priority: updates.priority ?? null,
376
564
  metadata: updates.metadata ?? undefined,
@@ -391,7 +579,7 @@ export class RefineTasksService {
391
579
  const taskList = group.tasks.map((t) => formatTaskSummary(t)).join("\n");
392
580
  const constraints = [
393
581
  "- Immutable: project_id, epic_id, user_story_id, task keys.",
394
- "- Allowed edits: title, description, acceptanceCriteria, metadata/labels, type, priority, storyPoints, status (but NOT ready_to_review/qa/completed).",
582
+ "- Allowed edits: title, description, acceptanceCriteria, metadata/labels, type, priority, storyPoints, status (but NOT ready_to_code_review/qa/completed).",
395
583
  "- Splits: children stay under same story; keep parent unless keepParent=false; child dependsOn must reference existing tasks or siblings.",
396
584
  "- Merges: target and sources must be in same story; prefer cancelling redundant sources (status=cancelled) and preserve useful details in target updates.",
397
585
  "- Dependencies: maintain DAG; do not introduce cycles or cross-story edges.",
@@ -411,6 +599,8 @@ export class RefineTasksService {
411
599
  group.historySummary || "(none)",
412
600
  "Constraints:",
413
601
  constraints,
602
+ "Example JSON:",
603
+ "{\"operations\":[{\"op\":\"update_task\",\"taskKey\":\"web-01-us-01-t01\",\"updates\":{\"title\":\"Refined title\",\"storyPoints\":3}}]}",
414
604
  "Return JSON ONLY matching: { \"operations\": [UpdateTaskOp | SplitTaskOp | MergeTasksOp | UpdateEstimateOp] } where each item has an `op` discriminator (update_task|split_task|merge_tasks|update_estimate).",
415
605
  ].join("\n\n");
416
606
  }
@@ -528,6 +718,20 @@ export class RefineTasksService {
528
718
  return existing;
529
719
  return { ...(existing ?? {}), ...updates };
530
720
  }
721
+ applyStageMetadata(metadata, content, shouldUpdate) {
722
+ if (!shouldUpdate)
723
+ return metadata;
724
+ const classification = classifyTask({
725
+ title: content.title,
726
+ description: content.description ?? undefined,
727
+ type: content.type ?? undefined,
728
+ });
729
+ return {
730
+ ...(metadata ?? {}),
731
+ stage: classification.stage,
732
+ foundation: classification.foundation,
733
+ };
734
+ }
531
735
  validateOperation(group, op) {
532
736
  const allowedOps = new Set(["update_task", "split_task", "merge_tasks", "update_estimate"]);
533
737
  if (!op || typeof op.op !== "string" || !allowedOps.has(op.op)) {
@@ -634,6 +838,28 @@ export class RefineTasksService {
634
838
  }
635
839
  return false;
636
840
  }
841
+ hasDependencyPath(graph, fromKey, toKey) {
842
+ if (fromKey === toKey)
843
+ return true;
844
+ const visited = new Set();
845
+ const stack = [fromKey];
846
+ while (stack.length > 0) {
847
+ const current = stack.pop();
848
+ if (current === toKey)
849
+ return true;
850
+ if (visited.has(current))
851
+ continue;
852
+ visited.add(current);
853
+ const neighbors = graph.get(current);
854
+ if (!neighbors)
855
+ continue;
856
+ for (const next of neighbors) {
857
+ if (!visited.has(next))
858
+ stack.push(next);
859
+ }
860
+ }
861
+ return false;
862
+ }
637
863
  async applyOperations(projectId, jobId, commandRunId, group, operations) {
638
864
  const created = [];
639
865
  const updated = [];
@@ -645,7 +871,6 @@ export class RefineTasksService {
645
871
  let stage = "start";
646
872
  const newTasks = [];
647
873
  const pendingDeps = [];
648
- const dependencyEdges = [];
649
874
  try {
650
875
  stage = "load:storyKeys";
651
876
  const storyKeyRows = await this.workspaceRepo.getDb().all(`SELECT key FROM tasks WHERE user_story_id = ?`, group.story.id);
@@ -658,7 +883,13 @@ export class RefineTasksService {
658
883
  if (!target)
659
884
  continue;
660
885
  const before = { ...target };
661
- const metadata = this.mergeMetadata(target.metadata, op.updates.metadata);
886
+ const mergedMetadata = this.mergeMetadata(target.metadata, op.updates.metadata);
887
+ const contentUpdated = op.updates.title !== undefined || op.updates.description !== undefined || op.updates.type !== undefined;
888
+ const metadata = this.applyStageMetadata(mergedMetadata, {
889
+ title: op.updates.title ?? target.title,
890
+ description: op.updates.description ?? target.description ?? null,
891
+ type: op.updates.type ?? target.type ?? null,
892
+ }, contentUpdated);
662
893
  const beforeSp = target.storyPoints ?? 0;
663
894
  const afterSp = op.updates.storyPoints ?? target.storyPoints ?? null;
664
895
  storyPointsDelta += (afterSp ?? 0) - (beforeSp ?? 0);
@@ -687,13 +918,22 @@ export class RefineTasksService {
687
918
  continue;
688
919
  if (op.parentUpdates) {
689
920
  const before = { ...target };
921
+ const mergedMetadata = this.mergeMetadata(target.metadata, op.parentUpdates.metadata);
922
+ const contentUpdated = op.parentUpdates.title !== undefined ||
923
+ op.parentUpdates.description !== undefined ||
924
+ op.parentUpdates.type !== undefined;
925
+ const metadata = this.applyStageMetadata(mergedMetadata, {
926
+ title: op.parentUpdates.title ?? target.title,
927
+ description: op.parentUpdates.description ?? target.description ?? null,
928
+ type: op.parentUpdates.type ?? target.type ?? null,
929
+ }, contentUpdated);
690
930
  await this.workspaceRepo.updateTask(target.id, {
691
931
  title: op.parentUpdates.title ?? target.title,
692
932
  description: op.parentUpdates.description ?? target.description ?? null,
693
933
  type: op.parentUpdates.type ?? target.type ?? null,
694
934
  storyPoints: op.parentUpdates.storyPoints ?? target.storyPoints ?? null,
695
935
  priority: op.parentUpdates.priority ?? target.priority ?? null,
696
- metadata: this.mergeMetadata(target.metadata, op.parentUpdates.metadata),
936
+ metadata,
697
937
  });
698
938
  updated.push(target.key);
699
939
  await this.workspaceRepo.insertTaskRevision({
@@ -705,7 +945,7 @@ export class RefineTasksService {
705
945
  ...before,
706
946
  ...op.parentUpdates,
707
947
  storyPoints: op.parentUpdates.storyPoints ?? before.storyPoints,
708
- metadata: this.mergeMetadata(before.metadata, op.parentUpdates.metadata),
948
+ metadata,
709
949
  },
710
950
  createdAt: new Date().toISOString(),
711
951
  });
@@ -716,6 +956,13 @@ export class RefineTasksService {
716
956
  if (childSp) {
717
957
  storyPointsDelta += childSp;
718
958
  }
959
+ const childMetadata = this.mergeMetadata({}, child.metadata);
960
+ const childContent = {
961
+ title: child.title,
962
+ description: child.description ?? target.description ?? "",
963
+ type: child.type ?? target.type ?? "feature",
964
+ };
965
+ const resolvedChildMetadata = this.applyStageMetadata(childMetadata, childContent, true);
719
966
  const childInsert = {
720
967
  projectId,
721
968
  epicId: target.epicId,
@@ -727,7 +974,7 @@ export class RefineTasksService {
727
974
  status: "not_started",
728
975
  storyPoints: childSp,
729
976
  priority: child.priority ?? target.priority ?? null,
730
- metadata: this.mergeMetadata({}, child.metadata),
977
+ metadata: resolvedChildMetadata,
731
978
  assignedAgentId: target.assignedAgentId ?? null,
732
979
  assigneeHuman: target.assigneeHuman ?? null,
733
980
  vcsBranch: null,
@@ -740,8 +987,12 @@ export class RefineTasksService {
740
987
  for (const depKey of dependsOn) {
741
988
  const depTask = taskByKey.get(depKey);
742
989
  if (depTask) {
743
- pendingDeps.push({ childKey, dependsOnId: depTask.id, relationType: "blocks" });
744
- dependencyEdges.push({ from: childKey, to: depTask.key });
990
+ pendingDeps.push({
991
+ childKey,
992
+ dependsOnId: depTask.id,
993
+ dependsOnKey: depTask.key,
994
+ relationType: "blocks",
995
+ });
745
996
  }
746
997
  }
747
998
  taskByKey.set(childKey, {
@@ -762,13 +1013,20 @@ export class RefineTasksService {
762
1013
  continue;
763
1014
  if (op.updates) {
764
1015
  const before = { ...target };
1016
+ const mergedMetadata = this.mergeMetadata(target.metadata, op.updates.metadata);
1017
+ const contentUpdated = op.updates.title !== undefined || op.updates.description !== undefined || op.updates.type !== undefined;
1018
+ const metadata = this.applyStageMetadata(mergedMetadata, {
1019
+ title: op.updates.title ?? target.title,
1020
+ description: op.updates.description ?? target.description ?? null,
1021
+ type: op.updates.type ?? target.type ?? null,
1022
+ }, contentUpdated);
765
1023
  await this.workspaceRepo.updateTask(target.id, {
766
1024
  title: op.updates.title ?? target.title,
767
1025
  description: op.updates.description ?? target.description ?? null,
768
1026
  type: op.updates.type ?? target.type ?? null,
769
1027
  storyPoints: op.updates.storyPoints ?? target.storyPoints ?? null,
770
1028
  priority: op.updates.priority ?? target.priority ?? null,
771
- metadata: this.mergeMetadata(target.metadata, op.updates.metadata),
1029
+ metadata,
772
1030
  });
773
1031
  updated.push(target.key);
774
1032
  await this.workspaceRepo.insertTaskRevision({
@@ -780,7 +1038,7 @@ export class RefineTasksService {
780
1038
  ...before,
781
1039
  ...op.updates,
782
1040
  storyPoints: op.updates.storyPoints ?? before.storyPoints,
783
- metadata: this.mergeMetadata(before.metadata, op.updates.metadata),
1041
+ metadata,
784
1042
  },
785
1043
  createdAt: new Date().toISOString(),
786
1044
  });
@@ -813,10 +1071,17 @@ export class RefineTasksService {
813
1071
  const beforeSp = target.storyPoints ?? 0;
814
1072
  const afterSp = op.storyPoints ?? target.storyPoints ?? null;
815
1073
  storyPointsDelta += (afterSp ?? 0) - (beforeSp ?? 0);
1074
+ const contentUpdated = op.type !== undefined;
1075
+ const metadata = this.applyStageMetadata(target.metadata, {
1076
+ title: target.title,
1077
+ description: target.description ?? null,
1078
+ type: op.type ?? target.type ?? null,
1079
+ }, contentUpdated);
816
1080
  await this.workspaceRepo.updateTask(target.id, {
817
1081
  storyPoints: afterSp,
818
1082
  type: op.type ?? target.type ?? null,
819
1083
  priority: op.priority ?? target.priority ?? null,
1084
+ metadata: contentUpdated ? metadata : undefined,
820
1085
  });
821
1086
  updated.push(target.key);
822
1087
  await this.workspaceRepo.insertTaskRevision({
@@ -824,11 +1089,30 @@ export class RefineTasksService {
824
1089
  jobId,
825
1090
  commandRunId,
826
1091
  snapshotBefore: { ...target },
827
- snapshotAfter: { ...target, storyPoints: afterSp, type: op.type ?? target.type ?? null, priority: op.priority ?? target.priority ?? null },
1092
+ snapshotAfter: {
1093
+ ...target,
1094
+ storyPoints: afterSp,
1095
+ type: op.type ?? target.type ?? null,
1096
+ priority: op.priority ?? target.priority ?? null,
1097
+ metadata: contentUpdated ? metadata : target.metadata,
1098
+ },
828
1099
  createdAt: new Date().toISOString(),
829
1100
  });
830
1101
  }
831
1102
  }
1103
+ const dependencyGraph = new Map();
1104
+ const addEdge = (from, to) => {
1105
+ if (!from || !to)
1106
+ return;
1107
+ const edges = dependencyGraph.get(from) ?? new Set();
1108
+ edges.add(to);
1109
+ dependencyGraph.set(from, edges);
1110
+ };
1111
+ for (const task of group.tasks) {
1112
+ for (const dep of task.dependencies) {
1113
+ addEdge(task.key, dep);
1114
+ }
1115
+ }
832
1116
  if (newTasks.length > 0) {
833
1117
  stage = "insert:newTasks";
834
1118
  const inserted = await this.workspaceRepo.insertTasks(newTasks, false);
@@ -842,7 +1126,27 @@ export class RefineTasksService {
842
1126
  }
843
1127
  }
844
1128
  const deps = [];
1129
+ const allowedDeps = [];
1130
+ const skippedDeps = [];
845
1131
  for (const dep of pendingDeps) {
1132
+ if (!dep.dependsOnKey)
1133
+ continue;
1134
+ if (this.hasDependencyPath(dependencyGraph, dep.dependsOnKey, dep.childKey)) {
1135
+ skippedDeps.push({ childKey: dep.childKey, dependsOnKey: dep.dependsOnKey });
1136
+ continue;
1137
+ }
1138
+ addEdge(dep.childKey, dep.dependsOnKey);
1139
+ allowedDeps.push(dep);
1140
+ }
1141
+ if (skippedDeps.length > 0) {
1142
+ const sample = skippedDeps
1143
+ .slice(0, 5)
1144
+ .map((dep) => `${dep.childKey}->${dep.dependsOnKey}`)
1145
+ .join(", ");
1146
+ warnings.push(`Skipped ${skippedDeps.length} refine dependencies that would create cycles.` +
1147
+ (sample ? ` Sample: ${sample}` : ""));
1148
+ }
1149
+ for (const dep of allowedDeps) {
846
1150
  const childId = idByKey.get(dep.childKey);
847
1151
  if (childId) {
848
1152
  deps.push({ taskId: childId, dependsOnTaskId: dep.dependsOnId, relationType: dep.relationType });
@@ -855,12 +1159,11 @@ export class RefineTasksService {
855
1159
  }
856
1160
  // cycle detection on current + new dependencies (by key)
857
1161
  const edgeSet = [];
858
- for (const task of group.tasks) {
859
- for (const dep of task.dependencies) {
860
- edgeSet.push({ from: task.key, to: dep });
1162
+ for (const [from, deps] of dependencyGraph.entries()) {
1163
+ for (const dep of deps) {
1164
+ edgeSet.push({ from, to: dep });
861
1165
  }
862
1166
  }
863
- edgeSet.push(...dependencyEdges);
864
1167
  const hasCycle = this.detectCycle(edgeSet);
865
1168
  if (hasCycle) {
866
1169
  throw new Error("Dependency cycle detected after refinement; aborting apply.");
@@ -987,9 +1290,15 @@ export class RefineTasksService {
987
1290
  tokensTotal: promptTokens + completionTokens,
988
1291
  durationSeconds,
989
1292
  timestamp: new Date().toISOString(),
990
- metadata: { command: "refine-tasks", action: "agent_refine", ...(metadata ?? {}) },
1293
+ metadata: {
1294
+ command: "refine-tasks",
1295
+ action: "agent_refine",
1296
+ phase: "agent_refine",
1297
+ attempt: 1,
1298
+ ...(metadata ?? {}),
1299
+ },
991
1300
  });
992
- return { raw: output, promptTokens, completionTokens };
1301
+ return { raw: output, promptTokens, completionTokens, agentId: agent.id };
993
1302
  }
994
1303
  async refineTasks(options) {
995
1304
  const strategy = options.strategy ?? DEFAULT_STRATEGY;
@@ -1025,6 +1334,30 @@ export class RefineTasksService {
1025
1334
  planOut: options.planOutPath,
1026
1335
  },
1027
1336
  });
1337
+ let ratingAgentId;
1338
+ const maybeRateAgent = async () => {
1339
+ if (!options.rateAgents || !ratingAgentId)
1340
+ return;
1341
+ try {
1342
+ const ratingService = this.ensureRatingService();
1343
+ await ratingService.rate({
1344
+ workspace: this.workspace,
1345
+ agentId: ratingAgentId,
1346
+ commandName: "refine-tasks",
1347
+ jobId: job.id,
1348
+ commandRunId: commandRun.id,
1349
+ });
1350
+ }
1351
+ catch (error) {
1352
+ const message = `Agent rating failed: ${error.message ?? String(error)}`;
1353
+ try {
1354
+ await this.jobService.appendLog(job.id, `${message}\n`);
1355
+ }
1356
+ catch {
1357
+ /* ignore rating log failures */
1358
+ }
1359
+ }
1360
+ };
1028
1361
  try {
1029
1362
  if (options.fromDb === false) {
1030
1363
  throw new Error("refine-tasks currently only supports DB-backed selection; set --from-db true");
@@ -1166,17 +1499,30 @@ export class RefineTasksService {
1166
1499
  await this.logWarningsToTasks(group.tasks.map((t) => t.id), job.id, commandRun.id, docWarnings.join("; "));
1167
1500
  }
1168
1501
  const prompt = this.buildStoryPrompt(group, strategy, docSummary);
1169
- const { raw } = await this.invokeAgent(options.agentName, prompt, agentStream, job.id, commandRun.id, { epicKey: group.epic.key, storyKey: group.story.key });
1170
- const parsed = extractJson(raw);
1171
- const ops = parsed?.operations && Array.isArray(parsed.operations) ? parsed.operations : [];
1172
- const normalized = ops.map(normalizeOperation);
1173
- const filtered = normalized.filter((op) => {
1174
- const { valid, reason } = this.validateOperation(group, op);
1175
- if (!valid && reason) {
1176
- plan.warnings?.push(`Skipped op for story ${group.story.key}: ${reason}`);
1502
+ const parseOps = (raw) => {
1503
+ const parsed = normalizePlanJson(extractJson(raw));
1504
+ const ops = parsed?.operations && Array.isArray(parsed.operations) ? parsed.operations : [];
1505
+ const normalized = ops.map(normalizeOperation);
1506
+ return normalized.filter((op) => {
1507
+ const { valid, reason } = this.validateOperation(group, op);
1508
+ if (!valid && reason) {
1509
+ plan.warnings?.push(`Skipped op for story ${group.story.key}: ${reason}`);
1510
+ }
1511
+ return valid;
1512
+ });
1513
+ };
1514
+ const { raw, agentId } = await this.invokeAgent(options.agentName, prompt, agentStream, job.id, commandRun.id, { epicKey: group.epic.key, storyKey: group.story.key });
1515
+ ratingAgentId = agentId;
1516
+ let filtered = parseOps(raw);
1517
+ if (filtered.length === 0) {
1518
+ const retryPrompt = `${prompt}\n\nRETRY: Your previous response did not match the JSON schema. Return only a JSON object with an operations array (no prose, no markdown, no <think> tags).`;
1519
+ const retry = await this.invokeAgent(options.agentName, retryPrompt, agentStream, job.id, commandRun.id, { epicKey: group.epic.key, storyKey: group.story.key, retry: true });
1520
+ ratingAgentId = retry.agentId;
1521
+ filtered = parseOps(retry.raw);
1522
+ if (filtered.length === 0) {
1523
+ plan.warnings?.push(`No valid operations returned for story ${group.story.key}.`);
1177
1524
  }
1178
- return valid;
1179
- });
1525
+ }
1180
1526
  plan.operations.push(...filtered);
1181
1527
  }
1182
1528
  catch (error) {
@@ -1198,7 +1544,7 @@ export class RefineTasksService {
1198
1544
  return candidate;
1199
1545
  }
1200
1546
  };
1201
- const defaultPlanPath = path.join(this.workspace.workspaceRoot, ".mcoda", "tasks", options.projectKey, "refinements", job.id, "plan.json");
1547
+ const defaultPlanPath = path.join(this.workspace.mcodaDir, "tasks", options.projectKey, "refinements", job.id, "plan.json");
1202
1548
  const requestedOutPath = options.planOutPath ? path.resolve(options.planOutPath) : defaultPlanPath;
1203
1549
  const outPath = await ensureUniquePath(requestedOutPath);
1204
1550
  await fs.mkdir(path.dirname(outPath), { recursive: true });
@@ -1222,6 +1568,7 @@ export class RefineTasksService {
1222
1568
  lastCheckpoint: "no_operations",
1223
1569
  });
1224
1570
  await this.jobService.finishCommandRun(commandRun.id, "succeeded");
1571
+ await maybeRateAgent();
1225
1572
  return {
1226
1573
  jobId: job.id,
1227
1574
  commandRunId: commandRun.id,
@@ -1246,6 +1593,7 @@ export class RefineTasksService {
1246
1593
  lastCheckpoint: "dry_run",
1247
1594
  });
1248
1595
  await this.jobService.finishCommandRun(commandRun.id, "succeeded");
1596
+ await maybeRateAgent();
1249
1597
  return {
1250
1598
  jobId: job.id,
1251
1599
  commandRunId: commandRun.id,
@@ -1291,6 +1639,7 @@ export class RefineTasksService {
1291
1639
  cancelled.push(...x);
1292
1640
  storyPointsDelta += delta;
1293
1641
  }
1642
+ await this.seedPriorities(options.projectKey);
1294
1643
  await this.jobService.updateJobStatus(job.id, "completed", {
1295
1644
  payload: {
1296
1645
  created: created.length,
@@ -1303,6 +1652,7 @@ export class RefineTasksService {
1303
1652
  lastCheckpoint: "completed",
1304
1653
  });
1305
1654
  await this.jobService.finishCommandRun(commandRun.id, "succeeded");
1655
+ await maybeRateAgent();
1306
1656
  return {
1307
1657
  jobId: job.id,
1308
1658
  commandRunId: commandRun.id,