@mcoda/core 0.1.18 → 0.1.19
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/estimate/EstimateService.d.ts +2 -0
- package/dist/services/estimate/EstimateService.d.ts.map +1 -1
- package/dist/services/estimate/EstimateService.js +54 -0
- package/dist/services/execution/WorkOnTasksService.d.ts.map +1 -1
- package/dist/services/execution/WorkOnTasksService.js +5 -3
- package/dist/services/planning/CreateTasksService.d.ts +5 -0
- package/dist/services/planning/CreateTasksService.d.ts.map +1 -1
- package/dist/services/planning/CreateTasksService.js +112 -37
- package/package.json +6 -6
|
@@ -8,6 +8,8 @@ export declare class EstimateService {
|
|
|
8
8
|
private computeElapsedLaneHours;
|
|
9
9
|
private computeDurations;
|
|
10
10
|
private computeEtas;
|
|
11
|
+
private buildStatusCounts;
|
|
12
|
+
private buildCompletion;
|
|
11
13
|
estimate(options: Omit<EstimateOptions, "workspace">): Promise<EstimateResult>;
|
|
12
14
|
close(): Promise<void>;
|
|
13
15
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EstimateService.d.ts","sourceRoot":"","sources":["../../../src/services/estimate/EstimateService.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAmC,eAAe,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEnG,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAI/E,qBAAa,eAAe;IACN,OAAO,CAAC,SAAS;IAArC,OAAO;WAEM,MAAM,CAAC,SAAS,EAAE,mBAAmB,GAAG,OAAO,CAAC,eAAe,CAAC;IAI7E,OAAO,CAAC,UAAU;YAMJ,uBAAuB;IAmCrC,OAAO,CAAC,gBAAgB;IA8BxB,OAAO,CAAC,WAAW;IA+
|
|
1
|
+
{"version":3,"file":"EstimateService.d.ts","sourceRoot":"","sources":["../../../src/services/estimate/EstimateService.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAmC,eAAe,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEnG,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAI/E,qBAAa,eAAe;IACN,OAAO,CAAC,SAAS;IAArC,OAAO;WAEM,MAAM,CAAC,SAAS,EAAE,mBAAmB,GAAG,OAAO,CAAC,eAAe,CAAC;IAI7E,OAAO,CAAC,UAAU;YAMJ,uBAAuB;IAmCrC,OAAO,CAAC,gBAAgB;IA8BxB,OAAO,CAAC,WAAW;IA+BnB,OAAO,CAAC,iBAAiB;IAoBzB,OAAO,CAAC,eAAe;IA0BjB,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,eAAe,EAAE,WAAW,CAAC,GAAG,OAAO,CAAC,cAAc,CAAC;IA4D9E,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAG7B"}
|
|
@@ -91,6 +91,56 @@ export class EstimateService {
|
|
|
91
91
|
completeEta,
|
|
92
92
|
};
|
|
93
93
|
}
|
|
94
|
+
buildStatusCounts(backlogTasks) {
|
|
95
|
+
const counts = {
|
|
96
|
+
total: backlogTasks.length,
|
|
97
|
+
readyToCodeReview: 0,
|
|
98
|
+
failed: 0,
|
|
99
|
+
inProgress: 0,
|
|
100
|
+
readyToQa: 0,
|
|
101
|
+
completed: 0,
|
|
102
|
+
};
|
|
103
|
+
for (const task of backlogTasks) {
|
|
104
|
+
const status = task.status.trim().toLowerCase();
|
|
105
|
+
if (status === "ready_to_code_review")
|
|
106
|
+
counts.readyToCodeReview += 1;
|
|
107
|
+
if (status === "failed")
|
|
108
|
+
counts.failed += 1;
|
|
109
|
+
if (status === "in_progress")
|
|
110
|
+
counts.inProgress += 1;
|
|
111
|
+
if (status === "ready_to_qa")
|
|
112
|
+
counts.readyToQa += 1;
|
|
113
|
+
if (status === "completed")
|
|
114
|
+
counts.completed += 1;
|
|
115
|
+
}
|
|
116
|
+
return counts;
|
|
117
|
+
}
|
|
118
|
+
buildCompletion(statusCounts) {
|
|
119
|
+
const ratio = (done, total) => {
|
|
120
|
+
if (!total || total <= 0)
|
|
121
|
+
return 0;
|
|
122
|
+
return Math.max(0, Math.min(100, (done / total) * 100));
|
|
123
|
+
};
|
|
124
|
+
const workDone = statusCounts.readyToCodeReview + statusCounts.readyToQa + statusCounts.completed;
|
|
125
|
+
const qaDone = statusCounts.readyToQa + statusCounts.completed;
|
|
126
|
+
return {
|
|
127
|
+
workOnTasks: {
|
|
128
|
+
done: workDone,
|
|
129
|
+
total: statusCounts.total,
|
|
130
|
+
percent: ratio(workDone, statusCounts.total),
|
|
131
|
+
},
|
|
132
|
+
readyToQa: {
|
|
133
|
+
done: qaDone,
|
|
134
|
+
total: statusCounts.total,
|
|
135
|
+
percent: ratio(qaDone, statusCounts.total),
|
|
136
|
+
},
|
|
137
|
+
done: {
|
|
138
|
+
done: statusCounts.completed,
|
|
139
|
+
total: statusCounts.total,
|
|
140
|
+
percent: ratio(statusCounts.completed, statusCounts.total),
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
}
|
|
94
144
|
async estimate(options) {
|
|
95
145
|
const backlogService = await BacklogService.create(this.workspace);
|
|
96
146
|
let backlogTotals;
|
|
@@ -131,6 +181,8 @@ export class EstimateService {
|
|
|
131
181
|
const elapsedImplementationHours = await this.computeElapsedLaneHours(inProgressTaskIds, "in_progress");
|
|
132
182
|
const durationsHours = this.computeDurations(backlogTotals, effectiveVelocity, elapsedImplementationHours);
|
|
133
183
|
const etas = this.computeEtas(backlogTotals, effectiveVelocity, durationsHours);
|
|
184
|
+
const statusCounts = this.buildStatusCounts(backlogTasks);
|
|
185
|
+
const completion = this.buildCompletion(statusCounts);
|
|
134
186
|
return {
|
|
135
187
|
scope: {
|
|
136
188
|
workspaceId: this.workspace.workspaceId,
|
|
@@ -143,6 +195,8 @@ export class EstimateService {
|
|
|
143
195
|
effectiveVelocity,
|
|
144
196
|
durationsHours,
|
|
145
197
|
etas,
|
|
198
|
+
statusCounts,
|
|
199
|
+
completion,
|
|
146
200
|
};
|
|
147
201
|
}
|
|
148
202
|
async close() {
|
|
@@ -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;AAEzD,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAE7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAsFrE,MAAM,WAAW,kBAAmB,SAAQ,oBAAoB;IAC9D,SAAS,EAAE,mBAAmB,CAAC;IAC/B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;IACxC,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,WAAW,GAAG,QAAQ,GAAG,SAAS,CAAC;IAC3C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,iBAAiB,CAAC;IAC7B,OAAO,EAAE,mBAAmB,EAAE,CAAC;IAC/B,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AA6wBD,MAAM,MAAM,kBAAkB,GAAG,WAAW,GAAG,WAAW,GAAG,WAAW,CAAC;AA2rCzE,qBAAa,kBAAkB;IA0B3B,OAAO,CAAC,SAAS;IACjB,OAAO,CAAC,IAAI;IA1Bd,OAAO,CAAC,gBAAgB,CAAuB;IAC/C,OAAO,CAAC,YAAY,CAAmB;IACvC,OAAO,CAAC,UAAU,CAA6B;IAC/C,OAAO,CAAC,GAAG,CAAY;IACvB,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,aAAa,CAAC,CAAqB;YAC7B,eAAe;gBAmBnB,SAAS,EAAE,mBAAmB,EAC9B,IAAI,EAAE;QACZ,YAAY,EAAE,YAAY,CAAC;QAC3B,MAAM,EAAE,YAAY,CAAC;QACrB,UAAU,EAAE,UAAU,CAAC;QACvB,aAAa,EAAE,mBAAmB,CAAC;QACnC,gBAAgB,CAAC,EAAE,oBAAoB,CAAC;QACxC,YAAY,CAAC,EAAE,gBAAgB,CAAC;QAChC,IAAI,EAAE,gBAAgB,CAAC;QACvB,SAAS,CAAC,EAAE,SAAS,CAAC;QACtB,cAAc,EAAE,cAAc,CAAC;QAC/B,aAAa,CAAC,EAAE,kBAAkB,CAAC;KACpC;YASW,WAAW;YAsDX,WAAW;YAIX,mBAAmB;YAOnB,oBAAoB;YAUpB,UAAU;WAUX,MAAM,CAAC,SAAS,EAAE,mBAAmB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IA6B1E,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAkB5B,qBAAqB,CAAC,SAAS,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI;YAQlD,YAAY;IAU1B,OAAO,CAAC,eAAe;IAQvB,OAAO,CAAC,mBAAmB;IAY3B,OAAO,CAAC,qBAAqB;IAW7B,OAAO,CAAC,UAAU;YAMJ,OAAO;YAWP,gBAAgB;YAsChB,eAAe;YAcf,gBAAgB;IAsH9B,OAAO,CAAC,gBAAgB;IA2BxB,OAAO,CAAC,mBAAmB;IAmC3B,OAAO,CAAC,YAAY;YAgBN,kBAAkB;YASlB,WAAW;YAOX,2BAA2B;YAY3B,uBAAuB;IAwGrC,OAAO,CAAC,WAAW;YAsCL,kBAAkB;IAoBhC,OAAO,CAAC,cAAc;YAYR,oBAAoB;YAkDpB,cAAc;IAmM5B,OAAO,CAAC,cAAc;IAQtB,OAAO,CAAC,oBAAoB;IAI5B,OAAO,CAAC,oBAAoB;IAI5B,OAAO,CAAC,uBAAuB;IAI/B,OAAO,CAAC,mBAAmB;IAI3B,OAAO,CAAC,gBAAgB;IAIxB,OAAO,CAAC,oBAAoB;YAGd,gBAAgB;IA6D9B,OAAO,CAAC,aAAa;YAiBP,yBAAyB;YA4CzB,sBAAsB;YA4DtB,YAAY;YAsOZ,eAAe;IAuE7B,OAAO,CAAC,mBAAmB;IAK3B,OAAO,CAAC,qBAAqB;YASf,qBAAqB;YA0BrB,2BAA2B;YAkE3B,QAAQ;IA4ChB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"WorkOnTasksService.d.ts","sourceRoot":"","sources":["../../../src/services/execution/WorkOnTasksService.ts"],"names":[],"mappings":"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"}
|
|
@@ -3405,7 +3405,9 @@ export class WorkOnTasksService {
|
|
|
3405
3405
|
if (request.missingTestsPolicy && !requestedMissingTestsPolicy) {
|
|
3406
3406
|
runnerWarnings.push(`Unknown missing-tests policy "${String(request.missingTestsPolicy)}"; using "${missingTestsPolicy}".`);
|
|
3407
3407
|
}
|
|
3408
|
-
const
|
|
3408
|
+
const explicitTaskSelection = Boolean(request.taskKeys?.length);
|
|
3409
|
+
const ignoreDependencies = request.ignoreDependencies === true || explicitTaskSelection;
|
|
3410
|
+
const ignoreStatusFilter = explicitTaskSelection || request.ignoreStatusFilter === true;
|
|
3409
3411
|
const { filtered: statusFilter, rejected } = ignoreStatusFilter
|
|
3410
3412
|
? { filtered: request.statusFilter ?? [], rejected: [] }
|
|
3411
3413
|
: filterTaskStatuses(request.statusFilter, WORK_ALLOWED_STATUSES, WORK_ALLOWED_STATUSES);
|
|
@@ -3443,7 +3445,7 @@ export class WorkOnTasksService {
|
|
|
3443
3445
|
ignoreStatusFilter,
|
|
3444
3446
|
includeTypes,
|
|
3445
3447
|
excludeTypes,
|
|
3446
|
-
ignoreDependencies
|
|
3448
|
+
ignoreDependencies,
|
|
3447
3449
|
limit: request.limit,
|
|
3448
3450
|
parallel: request.parallel,
|
|
3449
3451
|
noCommit: request.noCommit ?? false,
|
|
@@ -3527,7 +3529,7 @@ export class WorkOnTasksService {
|
|
|
3527
3529
|
ignoreStatusFilter,
|
|
3528
3530
|
includeTypes,
|
|
3529
3531
|
excludeTypes,
|
|
3530
|
-
ignoreDependencies
|
|
3532
|
+
ignoreDependencies,
|
|
3531
3533
|
limit: request.limit,
|
|
3532
3534
|
parallel: request.parallel,
|
|
3533
3535
|
});
|
|
@@ -58,6 +58,10 @@ export declare class CreateTasksService {
|
|
|
58
58
|
});
|
|
59
59
|
static create(workspace: WorkspaceResolution): Promise<CreateTasksService>;
|
|
60
60
|
close(): Promise<void>;
|
|
61
|
+
private storyScopeKey;
|
|
62
|
+
private taskScopeKey;
|
|
63
|
+
private scopeStory;
|
|
64
|
+
private scopeTask;
|
|
61
65
|
private seedPriorities;
|
|
62
66
|
private resolveAgent;
|
|
63
67
|
private ensureRatingService;
|
|
@@ -81,6 +85,7 @@ export declare class CreateTasksService {
|
|
|
81
85
|
private shouldInjectStructureBootstrap;
|
|
82
86
|
private injectStructureBootstrapPlan;
|
|
83
87
|
private enforceStoryScopedDependencies;
|
|
88
|
+
private validatePlanLocalIdentifiers;
|
|
84
89
|
private buildQaPreflight;
|
|
85
90
|
private buildQaOverrides;
|
|
86
91
|
private buildDocContext;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CreateTasksService.d.ts","sourceRoot":"","sources":["../../../src/services/planning/CreateTasksService.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"CreateTasksService.d.ts","sourceRoot":"","sources":["../../../src/services/planning/CreateTasksService.ts"],"names":[],"mappings":"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"}
|
|
@@ -593,6 +593,18 @@ export class CreateTasksService {
|
|
|
593
593
|
const docdex = this.docdex;
|
|
594
594
|
await swallow(docdex?.close?.bind(docdex));
|
|
595
595
|
}
|
|
596
|
+
storyScopeKey(epicLocalId, storyLocalId) {
|
|
597
|
+
return `${epicLocalId}::${storyLocalId}`;
|
|
598
|
+
}
|
|
599
|
+
taskScopeKey(epicLocalId, storyLocalId, taskLocalId) {
|
|
600
|
+
return `${epicLocalId}::${storyLocalId}::${taskLocalId}`;
|
|
601
|
+
}
|
|
602
|
+
scopeStory(story) {
|
|
603
|
+
return this.storyScopeKey(story.epicLocalId, story.localId);
|
|
604
|
+
}
|
|
605
|
+
scopeTask(task) {
|
|
606
|
+
return this.taskScopeKey(task.epicLocalId, task.storyLocalId, task.localId);
|
|
607
|
+
}
|
|
596
608
|
async seedPriorities(projectKey) {
|
|
597
609
|
const ordering = await this.taskOrderingFactory(this.workspace, { recordTelemetry: false });
|
|
598
610
|
try {
|
|
@@ -1068,7 +1080,7 @@ export class CreateTasksService {
|
|
|
1068
1080
|
const services = this.sortServicesByDependency(Array.from(aliases.keys()), dependencies);
|
|
1069
1081
|
return { services, dependencies, aliases };
|
|
1070
1082
|
}
|
|
1071
|
-
orderStoryTasksByDependencies(storyTasks, serviceRank,
|
|
1083
|
+
orderStoryTasksByDependencies(storyTasks, serviceRank, taskServiceByScope) {
|
|
1072
1084
|
const byLocalId = new Map(storyTasks.map((task) => [task.localId, task]));
|
|
1073
1085
|
const indegree = new Map();
|
|
1074
1086
|
const outgoing = new Map();
|
|
@@ -1090,8 +1102,8 @@ export class CreateTasksService {
|
|
|
1090
1102
|
const classB = classifyTask({ title: b.title ?? "", description: b.description, type: b.type });
|
|
1091
1103
|
if (classA.foundation !== classB.foundation)
|
|
1092
1104
|
return classA.foundation ? -1 : 1;
|
|
1093
|
-
const rankA = serviceRank.get(
|
|
1094
|
-
const rankB = serviceRank.get(
|
|
1105
|
+
const rankA = serviceRank.get(taskServiceByScope.get(this.scopeTask(a)) ?? "") ?? Number.MAX_SAFE_INTEGER;
|
|
1106
|
+
const rankB = serviceRank.get(taskServiceByScope.get(this.scopeTask(b)) ?? "") ?? Number.MAX_SAFE_INTEGER;
|
|
1095
1107
|
if (rankA !== rankB)
|
|
1096
1108
|
return rankA - rankB;
|
|
1097
1109
|
const priorityA = a.priorityHint ?? Number.MAX_SAFE_INTEGER;
|
|
@@ -1137,22 +1149,23 @@ export class CreateTasksService {
|
|
|
1137
1149
|
const epics = plan.epics.map((epic) => ({ ...epic }));
|
|
1138
1150
|
const stories = plan.stories.map((story) => ({ ...story }));
|
|
1139
1151
|
const tasks = plan.tasks.map((task) => ({ ...task, dependsOnKeys: uniqueStrings(task.dependsOnKeys ?? []) }));
|
|
1140
|
-
const
|
|
1141
|
-
const
|
|
1152
|
+
const storyByScope = new Map(stories.map((story) => [this.scopeStory(story), story]));
|
|
1153
|
+
const taskServiceByScope = new Map();
|
|
1142
1154
|
for (const task of tasks) {
|
|
1143
1155
|
const text = `${task.title ?? ""}\n${task.description ?? ""}`;
|
|
1144
|
-
|
|
1156
|
+
taskServiceByScope.set(this.scopeTask(task), resolveEntityService(text));
|
|
1145
1157
|
}
|
|
1146
1158
|
const tasksByStory = new Map();
|
|
1147
1159
|
for (const task of tasks) {
|
|
1148
|
-
const
|
|
1160
|
+
const storyScope = this.storyScopeKey(task.epicLocalId, task.storyLocalId);
|
|
1161
|
+
const bucket = tasksByStory.get(storyScope) ?? [];
|
|
1149
1162
|
bucket.push(task);
|
|
1150
|
-
tasksByStory.set(
|
|
1163
|
+
tasksByStory.set(storyScope, bucket);
|
|
1151
1164
|
}
|
|
1152
1165
|
for (const storyTasks of tasksByStory.values()) {
|
|
1153
1166
|
const tasksByService = new Map();
|
|
1154
1167
|
for (const task of storyTasks) {
|
|
1155
|
-
const service =
|
|
1168
|
+
const service = taskServiceByScope.get(this.scopeTask(task));
|
|
1156
1169
|
if (!service)
|
|
1157
1170
|
continue;
|
|
1158
1171
|
const serviceTasks = tasksByService.get(service) ?? [];
|
|
@@ -1163,7 +1176,7 @@ export class CreateTasksService {
|
|
|
1163
1176
|
serviceTasks.sort((a, b) => (a.priorityHint ?? Number.MAX_SAFE_INTEGER) - (b.priorityHint ?? Number.MAX_SAFE_INTEGER));
|
|
1164
1177
|
}
|
|
1165
1178
|
for (const task of storyTasks) {
|
|
1166
|
-
const service =
|
|
1179
|
+
const service = taskServiceByScope.get(this.scopeTask(task));
|
|
1167
1180
|
if (!service)
|
|
1168
1181
|
continue;
|
|
1169
1182
|
const requiredServices = graph.dependencies.get(service);
|
|
@@ -1179,21 +1192,22 @@ export class CreateTasksService {
|
|
|
1179
1192
|
}
|
|
1180
1193
|
}
|
|
1181
1194
|
}
|
|
1182
|
-
const
|
|
1195
|
+
const storyRankByScope = new Map();
|
|
1183
1196
|
for (const story of stories) {
|
|
1184
|
-
const
|
|
1197
|
+
const storyScope = this.scopeStory(story);
|
|
1198
|
+
const storyTasks = tasksByStory.get(storyScope) ?? [];
|
|
1185
1199
|
const taskRanks = storyTasks
|
|
1186
|
-
.map((task) => serviceRank.get(
|
|
1200
|
+
.map((task) => serviceRank.get(taskServiceByScope.get(this.scopeTask(task)) ?? ""))
|
|
1187
1201
|
.filter((value) => typeof value === "number");
|
|
1188
1202
|
const storyTextRank = serviceRank.get(resolveEntityService(`${story.title}\n${story.description ?? ""}\n${story.userStory ?? ""}`) ?? "");
|
|
1189
1203
|
const rank = taskRanks.length > 0 ? Math.min(...taskRanks) : storyTextRank ?? Number.MAX_SAFE_INTEGER;
|
|
1190
|
-
|
|
1204
|
+
storyRankByScope.set(storyScope, rank);
|
|
1191
1205
|
}
|
|
1192
1206
|
const epicRankByLocalId = new Map();
|
|
1193
1207
|
for (const epic of epics) {
|
|
1194
1208
|
const epicStories = stories.filter((story) => story.epicLocalId === epic.localId);
|
|
1195
1209
|
const storyRanks = epicStories
|
|
1196
|
-
.map((story) =>
|
|
1210
|
+
.map((story) => storyRankByScope.get(this.scopeStory(story)))
|
|
1197
1211
|
.filter((value) => typeof value === "number");
|
|
1198
1212
|
const epicTextRank = serviceRank.get(resolveEntityService(`${epic.title}\n${epic.description ?? ""}`) ?? "");
|
|
1199
1213
|
const rank = storyRanks.length > 0 ? Math.min(...storyRanks) : epicTextRank ?? Number.MAX_SAFE_INTEGER;
|
|
@@ -1228,8 +1242,8 @@ export class CreateTasksService {
|
|
|
1228
1242
|
const bootstrapB = isBootstrap(`${b.title} ${b.description ?? ""}`);
|
|
1229
1243
|
if (bootstrapA !== bootstrapB)
|
|
1230
1244
|
return bootstrapA ? -1 : 1;
|
|
1231
|
-
const rankA =
|
|
1232
|
-
const rankB =
|
|
1245
|
+
const rankA = storyRankByScope.get(this.scopeStory(a)) ?? Number.MAX_SAFE_INTEGER;
|
|
1246
|
+
const rankB = storyRankByScope.get(this.scopeStory(b)) ?? Number.MAX_SAFE_INTEGER;
|
|
1233
1247
|
if (rankA !== rankB)
|
|
1234
1248
|
return rankA - rankB;
|
|
1235
1249
|
const priorityA = a.priorityHint ?? Number.MAX_SAFE_INTEGER;
|
|
@@ -1241,31 +1255,31 @@ export class CreateTasksService {
|
|
|
1241
1255
|
epicStories.forEach((story, index) => {
|
|
1242
1256
|
story.priorityHint = index + 1;
|
|
1243
1257
|
storiesOrdered.push(story);
|
|
1244
|
-
const storyTasks = tasksByStory.get(story
|
|
1245
|
-
const orderedTasks = this.orderStoryTasksByDependencies(storyTasks, serviceRank,
|
|
1258
|
+
const storyTasks = tasksByStory.get(this.scopeStory(story)) ?? [];
|
|
1259
|
+
const orderedTasks = this.orderStoryTasksByDependencies(storyTasks, serviceRank, taskServiceByScope);
|
|
1246
1260
|
orderedTasks.forEach((task, taskIndex) => {
|
|
1247
1261
|
task.priorityHint = taskIndex + 1;
|
|
1248
1262
|
tasksOrdered.push(task);
|
|
1249
1263
|
});
|
|
1250
1264
|
});
|
|
1251
1265
|
}
|
|
1252
|
-
const
|
|
1266
|
+
const orderedStoryScopes = new Set(storiesOrdered.map((story) => this.scopeStory(story)));
|
|
1253
1267
|
for (const story of stories) {
|
|
1254
|
-
if (
|
|
1268
|
+
if (orderedStoryScopes.has(this.scopeStory(story)))
|
|
1255
1269
|
continue;
|
|
1256
1270
|
storiesOrdered.push(story);
|
|
1257
1271
|
}
|
|
1258
|
-
const
|
|
1272
|
+
const orderedTaskScopes = new Set(tasksOrdered.map((task) => this.scopeTask(task)));
|
|
1259
1273
|
for (const task of tasks) {
|
|
1260
|
-
if (
|
|
1274
|
+
if (orderedTaskScopes.has(this.scopeTask(task)))
|
|
1261
1275
|
continue;
|
|
1262
1276
|
tasksOrdered.push(task);
|
|
1263
1277
|
}
|
|
1264
1278
|
// Keep parent linkage intact even if malformed story references exist.
|
|
1265
1279
|
for (const story of storiesOrdered) {
|
|
1266
|
-
if (!
|
|
1280
|
+
if (!storyByScope.has(this.scopeStory(story)))
|
|
1267
1281
|
continue;
|
|
1268
|
-
story.epicLocalId =
|
|
1282
|
+
story.epicLocalId = storyByScope.get(this.scopeStory(story))?.epicLocalId ?? story.epicLocalId;
|
|
1269
1283
|
}
|
|
1270
1284
|
return { epics, stories: storiesOrdered, tasks: tasksOrdered };
|
|
1271
1285
|
}
|
|
@@ -1395,9 +1409,8 @@ export class CreateTasksService {
|
|
|
1395
1409
|
};
|
|
1396
1410
|
}
|
|
1397
1411
|
enforceStoryScopedDependencies(plan) {
|
|
1398
|
-
const scopedLocalKey = (storyLocalId, localId) => `${storyLocalId}::${localId}`;
|
|
1399
1412
|
const taskMap = new Map(plan.tasks.map((task) => [
|
|
1400
|
-
|
|
1413
|
+
this.scopeTask(task),
|
|
1401
1414
|
{
|
|
1402
1415
|
...task,
|
|
1403
1416
|
dependsOnKeys: uniqueStrings((task.dependsOnKeys ?? []).filter(Boolean)),
|
|
@@ -1405,9 +1418,10 @@ export class CreateTasksService {
|
|
|
1405
1418
|
]));
|
|
1406
1419
|
const tasksByStory = new Map();
|
|
1407
1420
|
for (const task of taskMap.values()) {
|
|
1408
|
-
const
|
|
1421
|
+
const storyScope = this.storyScopeKey(task.epicLocalId, task.storyLocalId);
|
|
1422
|
+
const storyTasks = tasksByStory.get(storyScope) ?? [];
|
|
1409
1423
|
storyTasks.push(task);
|
|
1410
|
-
tasksByStory.set(
|
|
1424
|
+
tasksByStory.set(storyScope, storyTasks);
|
|
1411
1425
|
}
|
|
1412
1426
|
for (const storyTasks of tasksByStory.values()) {
|
|
1413
1427
|
const localIds = new Set(storyTasks.map((task) => task.localId));
|
|
@@ -1438,9 +1452,67 @@ export class CreateTasksService {
|
|
|
1438
1452
|
}
|
|
1439
1453
|
return {
|
|
1440
1454
|
...plan,
|
|
1441
|
-
tasks: plan.tasks.map((task) => taskMap.get(
|
|
1455
|
+
tasks: plan.tasks.map((task) => taskMap.get(this.scopeTask(task)) ?? task),
|
|
1442
1456
|
};
|
|
1443
1457
|
}
|
|
1458
|
+
validatePlanLocalIdentifiers(plan) {
|
|
1459
|
+
const errors = [];
|
|
1460
|
+
const epicIds = new Set();
|
|
1461
|
+
for (const epic of plan.epics) {
|
|
1462
|
+
if (!epic.localId || !epic.localId.trim()) {
|
|
1463
|
+
errors.push("epic has missing localId");
|
|
1464
|
+
continue;
|
|
1465
|
+
}
|
|
1466
|
+
if (epicIds.has(epic.localId)) {
|
|
1467
|
+
errors.push(`duplicate epic localId: ${epic.localId}`);
|
|
1468
|
+
continue;
|
|
1469
|
+
}
|
|
1470
|
+
epicIds.add(epic.localId);
|
|
1471
|
+
}
|
|
1472
|
+
const storyScopes = new Set();
|
|
1473
|
+
for (const story of plan.stories) {
|
|
1474
|
+
const scope = this.scopeStory(story);
|
|
1475
|
+
if (!epicIds.has(story.epicLocalId)) {
|
|
1476
|
+
errors.push(`story ${scope} references unknown epicLocalId ${story.epicLocalId}`);
|
|
1477
|
+
}
|
|
1478
|
+
if (storyScopes.has(scope)) {
|
|
1479
|
+
errors.push(`duplicate story scope: ${scope}`);
|
|
1480
|
+
continue;
|
|
1481
|
+
}
|
|
1482
|
+
storyScopes.add(scope);
|
|
1483
|
+
}
|
|
1484
|
+
const taskScopes = new Set();
|
|
1485
|
+
const storyTaskLocals = new Map();
|
|
1486
|
+
for (const task of plan.tasks) {
|
|
1487
|
+
const storyScope = this.storyScopeKey(task.epicLocalId, task.storyLocalId);
|
|
1488
|
+
const taskScope = this.scopeTask(task);
|
|
1489
|
+
if (!storyScopes.has(storyScope)) {
|
|
1490
|
+
errors.push(`task ${taskScope} references unknown story scope ${storyScope}`);
|
|
1491
|
+
}
|
|
1492
|
+
if (taskScopes.has(taskScope)) {
|
|
1493
|
+
errors.push(`duplicate task scope: ${taskScope}`);
|
|
1494
|
+
continue;
|
|
1495
|
+
}
|
|
1496
|
+
taskScopes.add(taskScope);
|
|
1497
|
+
const locals = storyTaskLocals.get(storyScope) ?? new Set();
|
|
1498
|
+
locals.add(task.localId);
|
|
1499
|
+
storyTaskLocals.set(storyScope, locals);
|
|
1500
|
+
}
|
|
1501
|
+
for (const task of plan.tasks) {
|
|
1502
|
+
const storyScope = this.storyScopeKey(task.epicLocalId, task.storyLocalId);
|
|
1503
|
+
const localIds = storyTaskLocals.get(storyScope) ?? new Set();
|
|
1504
|
+
for (const dep of task.dependsOnKeys ?? []) {
|
|
1505
|
+
if (!dep || dep === task.localId)
|
|
1506
|
+
continue;
|
|
1507
|
+
if (!localIds.has(dep)) {
|
|
1508
|
+
errors.push(`task ${this.scopeTask(task)} has dependency ${dep} that is outside story scope ${storyScope}`);
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
if (errors.length > 0) {
|
|
1513
|
+
throw new Error(`Invalid generated plan local identifiers:\n- ${errors.join("\n- ")}`);
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1444
1516
|
async buildQaPreflight() {
|
|
1445
1517
|
const preflight = {
|
|
1446
1518
|
scripts: {},
|
|
@@ -1984,13 +2056,14 @@ export class CreateTasksService {
|
|
|
1984
2056
|
for (const story of storyMeta) {
|
|
1985
2057
|
const storyId = storyIdByKey.get(story.storyKey);
|
|
1986
2058
|
const existingTaskKeys = storyId ? await this.workspaceRepo.listTaskKeys(storyId) : [];
|
|
1987
|
-
const tasks = plan.tasks.filter((t) => t.storyLocalId === story.node.localId);
|
|
2059
|
+
const tasks = plan.tasks.filter((t) => t.storyLocalId === story.node.localId && t.epicLocalId === story.node.epicLocalId);
|
|
1988
2060
|
const taskKeyGen = createTaskKeyGenerator(story.storyKey, existingTaskKeys);
|
|
1989
2061
|
for (const task of tasks) {
|
|
1990
2062
|
const key = taskKeyGen();
|
|
1991
2063
|
const localId = task.localId ?? key;
|
|
1992
2064
|
taskDetails.push({
|
|
1993
2065
|
localId,
|
|
2066
|
+
epicLocalId: story.node.epicLocalId,
|
|
1994
2067
|
key,
|
|
1995
2068
|
storyLocalId: story.node.localId,
|
|
1996
2069
|
storyKey: story.storyKey,
|
|
@@ -1999,8 +2072,8 @@ export class CreateTasksService {
|
|
|
1999
2072
|
});
|
|
2000
2073
|
}
|
|
2001
2074
|
}
|
|
2002
|
-
const scopedLocalKey = (storyLocalId, localId) =>
|
|
2003
|
-
const localToKey = new Map(taskDetails.map((t) => [scopedLocalKey(t.storyLocalId, t.localId), t.key]));
|
|
2075
|
+
const scopedLocalKey = (epicLocalId, storyLocalId, localId) => this.taskScopeKey(epicLocalId, storyLocalId, localId);
|
|
2076
|
+
const localToKey = new Map(taskDetails.map((t) => [scopedLocalKey(t.epicLocalId, t.storyLocalId, t.localId), t.key]));
|
|
2004
2077
|
const taskInserts = [];
|
|
2005
2078
|
const testCommandBuilder = new QaTestCommandBuilder(this.workspace.workspaceRoot);
|
|
2006
2079
|
for (const task of taskDetails) {
|
|
@@ -2060,7 +2133,7 @@ export class CreateTasksService {
|
|
|
2060
2133
|
blockers: qaBlockers.length ? qaBlockers : undefined,
|
|
2061
2134
|
};
|
|
2062
2135
|
const depSlugs = (task.plan.dependsOnKeys ?? [])
|
|
2063
|
-
.map((dep) => localToKey.get(scopedLocalKey(task.storyLocalId, dep)))
|
|
2136
|
+
.map((dep) => localToKey.get(scopedLocalKey(task.plan.epicLocalId, task.storyLocalId, dep)))
|
|
2064
2137
|
.filter((value) => Boolean(value));
|
|
2065
2138
|
const metadata = {
|
|
2066
2139
|
doc_links: task.plan.relatedDocs ?? [],
|
|
@@ -2097,17 +2170,17 @@ export class CreateTasksService {
|
|
|
2097
2170
|
for (const detail of taskDetails) {
|
|
2098
2171
|
const row = taskRows.find((t) => t.key === detail.key);
|
|
2099
2172
|
if (row) {
|
|
2100
|
-
taskByLocal.set(scopedLocalKey(detail.storyLocalId, detail.localId), row);
|
|
2173
|
+
taskByLocal.set(scopedLocalKey(detail.epicLocalId, detail.storyLocalId, detail.localId), row);
|
|
2101
2174
|
}
|
|
2102
2175
|
}
|
|
2103
2176
|
const depKeys = new Set();
|
|
2104
2177
|
const dependencies = [];
|
|
2105
2178
|
for (const detail of taskDetails) {
|
|
2106
|
-
const current = taskByLocal.get(scopedLocalKey(detail.storyLocalId, detail.localId));
|
|
2179
|
+
const current = taskByLocal.get(scopedLocalKey(detail.epicLocalId, detail.storyLocalId, detail.localId));
|
|
2107
2180
|
if (!current)
|
|
2108
2181
|
continue;
|
|
2109
2182
|
for (const dep of detail.plan.dependsOnKeys ?? []) {
|
|
2110
|
-
const target = taskByLocal.get(scopedLocalKey(detail.storyLocalId, dep));
|
|
2183
|
+
const target = taskByLocal.get(scopedLocalKey(detail.plan.epicLocalId, detail.storyLocalId, dep));
|
|
2111
2184
|
if (!target || target.id === current.id)
|
|
2112
2185
|
continue;
|
|
2113
2186
|
const depKey = `${current.id}|${target.id}|blocks`;
|
|
@@ -2214,6 +2287,7 @@ export class CreateTasksService {
|
|
|
2214
2287
|
plan = this.enforceStoryScopedDependencies(plan);
|
|
2215
2288
|
plan = this.applyServiceDependencySequencing(plan, docs);
|
|
2216
2289
|
plan = this.enforceStoryScopedDependencies(plan);
|
|
2290
|
+
this.validatePlanLocalIdentifiers(plan);
|
|
2217
2291
|
await this.jobService.writeCheckpoint(job.id, {
|
|
2218
2292
|
stage: "stories_generated",
|
|
2219
2293
|
timestamp: new Date().toISOString(),
|
|
@@ -2343,6 +2417,7 @@ export class CreateTasksService {
|
|
|
2343
2417
|
plan = this.enforceStoryScopedDependencies(plan);
|
|
2344
2418
|
plan = this.applyServiceDependencySequencing(plan, []);
|
|
2345
2419
|
plan = this.enforceStoryScopedDependencies(plan);
|
|
2420
|
+
this.validatePlanLocalIdentifiers(plan);
|
|
2346
2421
|
const loadRefinePlans = async () => {
|
|
2347
2422
|
const candidates = [];
|
|
2348
2423
|
if (options.refinePlanPath)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mcoda/core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.19",
|
|
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/
|
|
38
|
-
"@mcoda/generators": "0.1.
|
|
39
|
-
"@mcoda/
|
|
35
|
+
"@mcoda/shared": "0.1.19",
|
|
36
|
+
"@mcoda/db": "0.1.19",
|
|
37
|
+
"@mcoda/agents": "0.1.19",
|
|
38
|
+
"@mcoda/generators": "0.1.19",
|
|
39
|
+
"@mcoda/integrations": "0.1.19"
|
|
40
40
|
},
|
|
41
41
|
"scripts": {
|
|
42
42
|
"build": "tsc -p tsconfig.json",
|