@mcoda/core 0.1.23 → 0.1.26
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/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/services/execution/TaskSelectionService.d.ts.map +1 -1
- package/dist/services/execution/TaskSelectionService.js +2 -4
- package/dist/services/planning/CreateTasksService.d.ts +8 -0
- package/dist/services/planning/CreateTasksService.d.ts.map +1 -1
- package/dist/services/planning/CreateTasksService.js +387 -47
- package/dist/services/planning/RefineTasksService.d.ts.map +1 -1
- package/dist/services/planning/RefineTasksService.js +89 -15
- package/dist/services/planning/TaskSufficiencyService.d.ts +73 -0
- package/dist/services/planning/TaskSufficiencyService.d.ts.map +1 -0
- package/dist/services/planning/TaskSufficiencyService.js +704 -0
- package/package.json +6 -6
|
@@ -194,6 +194,22 @@ const formatTaskSummary = (task) => {
|
|
|
194
194
|
.filter(Boolean)
|
|
195
195
|
.join("\n");
|
|
196
196
|
};
|
|
197
|
+
const splitChildReferenceFields = ["taskKey", "key", "localId", "id", "slug", "alias", "ref"];
|
|
198
|
+
const normalizeSplitDependencyRef = (value) => value.trim().toLowerCase();
|
|
199
|
+
const collectSplitChildReferences = (child) => {
|
|
200
|
+
const references = [];
|
|
201
|
+
const childRecord = child;
|
|
202
|
+
for (const field of splitChildReferenceFields) {
|
|
203
|
+
const candidate = childRecord[field];
|
|
204
|
+
if (typeof candidate === "string" && candidate.trim().length > 0) {
|
|
205
|
+
references.push(candidate.trim());
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
if (typeof child.title === "string" && child.title.trim().length > 0) {
|
|
209
|
+
references.push(child.title.trim());
|
|
210
|
+
}
|
|
211
|
+
return Array.from(new Set(references));
|
|
212
|
+
};
|
|
197
213
|
export class RefineTasksService {
|
|
198
214
|
constructor(workspace, deps) {
|
|
199
215
|
this.workspace = workspace;
|
|
@@ -880,10 +896,39 @@ export class RefineTasksService {
|
|
|
880
896
|
}
|
|
881
897
|
if (op.op === "split_task") {
|
|
882
898
|
const split = op;
|
|
883
|
-
const
|
|
899
|
+
const taskKeysByNormalized = new Map();
|
|
900
|
+
for (const key of keySet) {
|
|
901
|
+
taskKeysByNormalized.set(normalizeSplitDependencyRef(key), key);
|
|
902
|
+
}
|
|
903
|
+
const siblingReferences = new Set();
|
|
904
|
+
const selfReferencesByChild = split.children.map((child) => {
|
|
905
|
+
const selfReferences = new Set();
|
|
906
|
+
for (const reference of collectSplitChildReferences(child)) {
|
|
907
|
+
const normalized = normalizeSplitDependencyRef(reference);
|
|
908
|
+
if (!normalized)
|
|
909
|
+
continue;
|
|
910
|
+
selfReferences.add(normalized);
|
|
911
|
+
siblingReferences.add(normalized);
|
|
912
|
+
}
|
|
913
|
+
return selfReferences;
|
|
914
|
+
});
|
|
915
|
+
const invalidDep = split.children.some((child) => child.dependsOn?.some((dep) => {
|
|
916
|
+
const normalized = normalizeSplitDependencyRef(dep);
|
|
917
|
+
if (!normalized)
|
|
918
|
+
return false;
|
|
919
|
+
if (taskKeysByNormalized.has(normalized))
|
|
920
|
+
return false;
|
|
921
|
+
if (siblingReferences.has(normalized))
|
|
922
|
+
return false;
|
|
923
|
+
return true;
|
|
924
|
+
}));
|
|
884
925
|
if (invalidDep) {
|
|
885
926
|
return { valid: false, reason: "Split child references unknown dependency" };
|
|
886
927
|
}
|
|
928
|
+
const selfDependency = split.children.some((child, index) => child.dependsOn?.some((dep) => selfReferencesByChild[index]?.has(normalizeSplitDependencyRef(dep))));
|
|
929
|
+
if (selfDependency) {
|
|
930
|
+
return { valid: false, reason: "Split child cannot depend on itself" };
|
|
931
|
+
}
|
|
887
932
|
if (split.children.some((child) => child.storyPoints !== undefined && child.storyPoints !== null && (child.storyPoints < 0 || child.storyPoints > 13))) {
|
|
888
933
|
return { valid: false, reason: "Child story points out of bounds" };
|
|
889
934
|
}
|
|
@@ -1049,8 +1094,25 @@ export class RefineTasksService {
|
|
|
1049
1094
|
createdAt: new Date().toISOString(),
|
|
1050
1095
|
});
|
|
1051
1096
|
}
|
|
1052
|
-
|
|
1053
|
-
|
|
1097
|
+
const existingTaskKeyByNormalized = new Map();
|
|
1098
|
+
for (const key of taskByKey.keys()) {
|
|
1099
|
+
existingTaskKeyByNormalized.set(normalizeSplitDependencyRef(key), key);
|
|
1100
|
+
}
|
|
1101
|
+
const childKeys = op.children.map(() => keyGen());
|
|
1102
|
+
const childRefToKey = new Map();
|
|
1103
|
+
op.children.forEach((child, index) => {
|
|
1104
|
+
const childKey = childKeys[index];
|
|
1105
|
+
childRefToKey.set(normalizeSplitDependencyRef(childKey), childKey);
|
|
1106
|
+
for (const reference of collectSplitChildReferences(child)) {
|
|
1107
|
+
const normalized = normalizeSplitDependencyRef(reference);
|
|
1108
|
+
if (!normalized || childRefToKey.has(normalized))
|
|
1109
|
+
continue;
|
|
1110
|
+
childRefToKey.set(normalized, childKey);
|
|
1111
|
+
}
|
|
1112
|
+
});
|
|
1113
|
+
for (let index = 0; index < op.children.length; index += 1) {
|
|
1114
|
+
const child = op.children[index];
|
|
1115
|
+
const childKey = childKeys[index];
|
|
1054
1116
|
const childSp = child.storyPoints ?? null;
|
|
1055
1117
|
if (childSp) {
|
|
1056
1118
|
storyPointsDelta += childSp;
|
|
@@ -1082,17 +1144,24 @@ export class RefineTasksService {
|
|
|
1082
1144
|
openapiVersionAtCreation: target.openapiVersionAtCreation ?? null,
|
|
1083
1145
|
};
|
|
1084
1146
|
newTasks.push(childInsert);
|
|
1085
|
-
const
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1147
|
+
for (const dependencyReference of child.dependsOn ?? []) {
|
|
1148
|
+
const normalizedReference = normalizeSplitDependencyRef(dependencyReference);
|
|
1149
|
+
if (!normalizedReference)
|
|
1150
|
+
continue;
|
|
1151
|
+
const dependencyKey = existingTaskKeyByNormalized.get(normalizedReference) ?? childRefToKey.get(normalizedReference);
|
|
1152
|
+
if (!dependencyKey) {
|
|
1153
|
+
warnings.push(`Skipped split dependency ${childKey}->${dependencyReference}: unresolved sibling or task reference.`);
|
|
1154
|
+
continue;
|
|
1155
|
+
}
|
|
1156
|
+
if (dependencyKey === childKey) {
|
|
1157
|
+
warnings.push(`Skipped split dependency ${childKey}->${dependencyReference}: self dependency.`);
|
|
1158
|
+
continue;
|
|
1095
1159
|
}
|
|
1160
|
+
pendingDeps.push({
|
|
1161
|
+
childKey,
|
|
1162
|
+
dependsOnKey: dependencyKey,
|
|
1163
|
+
relationType: "blocks",
|
|
1164
|
+
});
|
|
1096
1165
|
}
|
|
1097
1166
|
taskByKey.set(childKey, {
|
|
1098
1167
|
...childInsert,
|
|
@@ -1247,9 +1316,14 @@ export class RefineTasksService {
|
|
|
1247
1316
|
}
|
|
1248
1317
|
for (const dep of allowedDeps) {
|
|
1249
1318
|
const childId = idByKey.get(dep.childKey);
|
|
1250
|
-
if (childId)
|
|
1251
|
-
|
|
1319
|
+
if (!childId)
|
|
1320
|
+
continue;
|
|
1321
|
+
const dependsOnId = idByKey.get(dep.dependsOnKey) ?? taskByKey.get(dep.dependsOnKey)?.id;
|
|
1322
|
+
if (!dependsOnId) {
|
|
1323
|
+
warnings.push(`Skipped refine dependency ${dep.childKey}->${dep.dependsOnKey}: dependency task not found.`);
|
|
1324
|
+
continue;
|
|
1252
1325
|
}
|
|
1326
|
+
deps.push({ taskId: childId, dependsOnTaskId: dependsOnId, relationType: dep.relationType });
|
|
1253
1327
|
}
|
|
1254
1328
|
if (deps.length > 0) {
|
|
1255
1329
|
stage = "insert:deps";
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { WorkspaceRepository } from "@mcoda/db";
|
|
2
|
+
import { WorkspaceResolution } from "../../workspace/WorkspaceManager.js";
|
|
3
|
+
import { JobService } from "../jobs/JobService.js";
|
|
4
|
+
export interface TaskSufficiencyAuditRequest {
|
|
5
|
+
workspace: WorkspaceResolution;
|
|
6
|
+
projectKey: string;
|
|
7
|
+
dryRun?: boolean;
|
|
8
|
+
maxIterations?: number;
|
|
9
|
+
maxTasksPerIteration?: number;
|
|
10
|
+
minCoverageRatio?: number;
|
|
11
|
+
sourceCommand?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface TaskSufficiencyAuditIteration {
|
|
14
|
+
iteration: number;
|
|
15
|
+
coverageRatio: number;
|
|
16
|
+
totalSignals: number;
|
|
17
|
+
missingSectionCount: number;
|
|
18
|
+
missingFolderCount: number;
|
|
19
|
+
createdTaskKeys: string[];
|
|
20
|
+
}
|
|
21
|
+
export interface TaskSufficiencyAuditResult {
|
|
22
|
+
jobId: string;
|
|
23
|
+
commandRunId: string;
|
|
24
|
+
projectKey: string;
|
|
25
|
+
sourceCommand?: string;
|
|
26
|
+
satisfied: boolean;
|
|
27
|
+
dryRun: boolean;
|
|
28
|
+
totalTasksAdded: number;
|
|
29
|
+
totalTasksUpdated: number;
|
|
30
|
+
maxIterations: number;
|
|
31
|
+
minCoverageRatio: number;
|
|
32
|
+
finalCoverageRatio: number;
|
|
33
|
+
remainingSectionHeadings: string[];
|
|
34
|
+
remainingFolderEntries: string[];
|
|
35
|
+
remainingGaps: {
|
|
36
|
+
sections: number;
|
|
37
|
+
folders: number;
|
|
38
|
+
total: number;
|
|
39
|
+
};
|
|
40
|
+
iterations: TaskSufficiencyAuditIteration[];
|
|
41
|
+
reportPath: string;
|
|
42
|
+
reportHistoryPath?: string;
|
|
43
|
+
warnings: string[];
|
|
44
|
+
}
|
|
45
|
+
type TaskSufficiencyDeps = {
|
|
46
|
+
workspaceRepo: WorkspaceRepository;
|
|
47
|
+
jobService: JobService;
|
|
48
|
+
};
|
|
49
|
+
export declare class TaskSufficiencyService {
|
|
50
|
+
private readonly workspaceRepo;
|
|
51
|
+
private readonly jobService;
|
|
52
|
+
private readonly ownsWorkspaceRepo;
|
|
53
|
+
private readonly ownsJobService;
|
|
54
|
+
private readonly workspace;
|
|
55
|
+
constructor(workspace: WorkspaceResolution, deps: TaskSufficiencyDeps, ownership?: {
|
|
56
|
+
ownsWorkspaceRepo?: boolean;
|
|
57
|
+
ownsJobService?: boolean;
|
|
58
|
+
});
|
|
59
|
+
static create(workspace: WorkspaceResolution): Promise<TaskSufficiencyService>;
|
|
60
|
+
close(): Promise<void>;
|
|
61
|
+
private discoverSdsPaths;
|
|
62
|
+
private walkSdsCandidates;
|
|
63
|
+
private loadSdsSources;
|
|
64
|
+
private loadProjectSnapshot;
|
|
65
|
+
private evaluateCoverage;
|
|
66
|
+
private buildGapItems;
|
|
67
|
+
private ensureTargetStory;
|
|
68
|
+
private insertGapTasks;
|
|
69
|
+
private writeReportArtifacts;
|
|
70
|
+
runAudit(request: TaskSufficiencyAuditRequest): Promise<TaskSufficiencyAuditResult>;
|
|
71
|
+
}
|
|
72
|
+
export {};
|
|
73
|
+
//# sourceMappingURL=TaskSufficiencyService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TaskSufficiencyService.d.ts","sourceRoot":"","sources":["../../../src/services/planning/TaskSufficiencyService.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mBAAmB,EAAiC,MAAM,WAAW,CAAC;AAE/E,OAAO,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAC1E,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAsCnD,MAAM,WAAW,2BAA2B;IAC1C,SAAS,EAAE,mBAAmB,CAAC;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,6BAA6B;IAC5C,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,0BAA0B;IACzC,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,OAAO,CAAC;IAChB,eAAe,EAAE,MAAM,CAAC;IACxB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,wBAAwB,EAAE,MAAM,EAAE,CAAC;IACnC,sBAAsB,EAAE,MAAM,EAAE,CAAC;IACjC,aAAa,EAAE;QACb,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;IACF,UAAU,EAAE,6BAA6B,EAAE,CAAC;IAC5C,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,KAAK,mBAAmB,GAAG;IACzB,aAAa,EAAE,mBAAmB,CAAC;IACnC,UAAU,EAAE,UAAU,CAAC;CACxB,CAAC;AAqFF,qBAAa,sBAAsB;IACjC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAsB;IACpD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAa;IACxC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAU;IAC5C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAU;IACzC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAsB;gBAG9C,SAAS,EAAE,mBAAmB,EAC9B,IAAI,EAAE,mBAAmB,EACzB,SAAS,GAAE;QAAE,iBAAiB,CAAC,EAAE,OAAO,CAAC;QAAC,cAAc,CAAC,EAAE,OAAO,CAAA;KAAO;WAS9D,MAAM,CAAC,SAAS,EAAE,mBAAmB,GAAG,OAAO,CAAC,sBAAsB,CAAC;IAU9E,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YASd,gBAAgB;YA4BhB,iBAAiB;YAmCjB,cAAc;YAcd,mBAAmB;IAgEjC,OAAO,CAAC,gBAAgB;IA2BxB,OAAO,CAAC,aAAa;YAqBP,iBAAiB;YAyFjB,cAAc;YAkGd,oBAAoB;IAkB5B,QAAQ,CAAC,OAAO,EAAE,2BAA2B,GAAG,OAAO,CAAC,0BAA0B,CAAC;CA4R1F"}
|