@mcoda/core 0.1.35 → 0.1.37
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/prompts/PdrPrompts.js +1 -1
- package/dist/services/planning/CreateTasksService.d.ts +160 -1
- package/dist/services/planning/CreateTasksService.d.ts.map +1 -1
- package/dist/services/planning/CreateTasksService.js +6109 -616
- package/dist/services/planning/RefineTasksService.d.ts +10 -1
- package/dist/services/planning/RefineTasksService.d.ts.map +1 -1
- package/dist/services/planning/RefineTasksService.js +927 -216
- package/dist/services/planning/SdsPreflightService.js +6 -6
- package/dist/services/planning/TaskSufficiencyService.d.ts +16 -0
- package/dist/services/planning/TaskSufficiencyService.d.ts.map +1 -1
- package/dist/services/planning/TaskSufficiencyService.js +353 -17
- package/package.json +6 -6
|
@@ -566,17 +566,17 @@ export class SdsPreflightService {
|
|
|
566
566
|
if (technologies.length > 0) {
|
|
567
567
|
return [
|
|
568
568
|
"## Technology Stack",
|
|
569
|
-
`-
|
|
570
|
-
"-
|
|
571
|
-
"-
|
|
569
|
+
`- Observed source-backed technology signals: ${technologies.join(", ")}.`,
|
|
570
|
+
"- Keep the chosen stack explicit in the source docs for runtime, language, persistence, interface, and tooling layers.",
|
|
571
|
+
"- Record alternatives only when the source docs name them; do not invent default stack choices during preflight.",
|
|
572
572
|
"",
|
|
573
573
|
];
|
|
574
574
|
}
|
|
575
575
|
return [
|
|
576
576
|
"## Technology Stack",
|
|
577
|
-
"-
|
|
578
|
-
"-
|
|
579
|
-
"-
|
|
577
|
+
"- Source docs do not yet make the technology stack explicit.",
|
|
578
|
+
"- Record runtime, language, persistence, interface, and tooling decisions explicitly in the source docs without assuming defaults.",
|
|
579
|
+
"- Preflight must not invent a chosen stack baseline when the source is silent.",
|
|
580
580
|
"",
|
|
581
581
|
];
|
|
582
582
|
}
|
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
import { WorkspaceRepository } from "@mcoda/db";
|
|
2
2
|
import { WorkspaceResolution } from "../../workspace/WorkspaceManager.js";
|
|
3
3
|
import { JobService } from "../jobs/JobService.js";
|
|
4
|
+
export interface TaskSufficiencyPlannedGapBundle {
|
|
5
|
+
kind: "section" | "folder" | "mixed";
|
|
6
|
+
domain: string;
|
|
7
|
+
values: string[];
|
|
8
|
+
anchors: string[];
|
|
9
|
+
implementationTargets: string[];
|
|
10
|
+
}
|
|
11
|
+
export interface TaskSufficiencyUnresolvedBundle {
|
|
12
|
+
kind: "section" | "folder" | "mixed";
|
|
13
|
+
domain: string;
|
|
14
|
+
values: string[];
|
|
15
|
+
anchors: string[];
|
|
16
|
+
}
|
|
4
17
|
export interface TaskSufficiencyAuditRequest {
|
|
5
18
|
workspace: WorkspaceResolution;
|
|
6
19
|
projectKey: string;
|
|
@@ -16,6 +29,7 @@ export interface TaskSufficiencyAuditIteration {
|
|
|
16
29
|
totalSignals: number;
|
|
17
30
|
missingSectionCount: number;
|
|
18
31
|
missingFolderCount: number;
|
|
32
|
+
unresolvedBundleCount: number;
|
|
19
33
|
createdTaskKeys: string[];
|
|
20
34
|
}
|
|
21
35
|
export interface TaskSufficiencyAuditResult {
|
|
@@ -38,6 +52,8 @@ export interface TaskSufficiencyAuditResult {
|
|
|
38
52
|
folders: number;
|
|
39
53
|
total: number;
|
|
40
54
|
};
|
|
55
|
+
plannedGapBundles: TaskSufficiencyPlannedGapBundle[];
|
|
56
|
+
unresolvedBundles: TaskSufficiencyUnresolvedBundle[];
|
|
41
57
|
iterations: TaskSufficiencyAuditIteration[];
|
|
42
58
|
reportPath: string;
|
|
43
59
|
reportHistoryPath?: string;
|
|
@@ -1 +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;
|
|
1
|
+
{"version":3,"file":"TaskSufficiencyService.d.ts","sourceRoot":"","sources":["../../../src/services/planning/TaskSufficiencyService.ts"],"names":[],"mappings":"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"}
|
|
@@ -20,8 +20,170 @@ const sdsFilenamePattern = /(sds|software[-_ ]design|system[-_ ]design|design[-_
|
|
|
20
20
|
const sdsContentPattern = /(software design specification|system design specification|^#\s*sds\b)/im;
|
|
21
21
|
const supportRootSegments = new Set(["docs", "fixtures", "policies", "policy", "runbooks", "pdr", "rfp", "sds"]);
|
|
22
22
|
const headingNoiseTokens = new Set(["and", "for", "from", "into", "the", "with"]);
|
|
23
|
+
const runtimePathSegments = new Set([
|
|
24
|
+
"api",
|
|
25
|
+
"app",
|
|
26
|
+
"apps",
|
|
27
|
+
"bin",
|
|
28
|
+
"cli",
|
|
29
|
+
"client",
|
|
30
|
+
"clients",
|
|
31
|
+
"cmd",
|
|
32
|
+
"command",
|
|
33
|
+
"commands",
|
|
34
|
+
"engine",
|
|
35
|
+
"engines",
|
|
36
|
+
"feature",
|
|
37
|
+
"features",
|
|
38
|
+
"gateway",
|
|
39
|
+
"gateways",
|
|
40
|
+
"handler",
|
|
41
|
+
"handlers",
|
|
42
|
+
"module",
|
|
43
|
+
"modules",
|
|
44
|
+
"pipeline",
|
|
45
|
+
"pipelines",
|
|
46
|
+
"processor",
|
|
47
|
+
"processors",
|
|
48
|
+
"route",
|
|
49
|
+
"routes",
|
|
50
|
+
"server",
|
|
51
|
+
"servers",
|
|
52
|
+
"service",
|
|
53
|
+
"services",
|
|
54
|
+
"src",
|
|
55
|
+
"ui",
|
|
56
|
+
"web",
|
|
57
|
+
"worker",
|
|
58
|
+
"workers",
|
|
59
|
+
]);
|
|
60
|
+
const interfacePathSegments = new Set([
|
|
61
|
+
"contract",
|
|
62
|
+
"contracts",
|
|
63
|
+
"dto",
|
|
64
|
+
"dtos",
|
|
65
|
+
"interface",
|
|
66
|
+
"interfaces",
|
|
67
|
+
"policy",
|
|
68
|
+
"policies",
|
|
69
|
+
"proto",
|
|
70
|
+
"protocol",
|
|
71
|
+
"protocols",
|
|
72
|
+
"schema",
|
|
73
|
+
"schemas",
|
|
74
|
+
"spec",
|
|
75
|
+
"specs",
|
|
76
|
+
"type",
|
|
77
|
+
"types",
|
|
78
|
+
]);
|
|
79
|
+
const dataPathSegments = new Set([
|
|
80
|
+
"cache",
|
|
81
|
+
"caches",
|
|
82
|
+
"data",
|
|
83
|
+
"db",
|
|
84
|
+
"ledger",
|
|
85
|
+
"migration",
|
|
86
|
+
"migrations",
|
|
87
|
+
"model",
|
|
88
|
+
"models",
|
|
89
|
+
"persistence",
|
|
90
|
+
"repository",
|
|
91
|
+
"repositories",
|
|
92
|
+
"storage",
|
|
93
|
+
]);
|
|
94
|
+
const testPathSegments = new Set(["acceptance", "e2e", "integration", "spec", "specs", "test", "tests"]);
|
|
95
|
+
const opsPathSegments = new Set([
|
|
96
|
+
"automation",
|
|
97
|
+
"deploy",
|
|
98
|
+
"deployment",
|
|
99
|
+
"deployments",
|
|
100
|
+
"infra",
|
|
101
|
+
"ops",
|
|
102
|
+
"operation",
|
|
103
|
+
"operations",
|
|
104
|
+
"orchestration",
|
|
105
|
+
"orchestrations",
|
|
106
|
+
"provision",
|
|
107
|
+
"provisioning",
|
|
108
|
+
"runbook",
|
|
109
|
+
"runbooks",
|
|
110
|
+
"script",
|
|
111
|
+
"scripts",
|
|
112
|
+
]);
|
|
113
|
+
const manifestSignalTokens = new Set([
|
|
114
|
+
"build",
|
|
115
|
+
"config",
|
|
116
|
+
"configuration",
|
|
117
|
+
"dependency",
|
|
118
|
+
"dependencies",
|
|
119
|
+
"environment",
|
|
120
|
+
"lock",
|
|
121
|
+
"lockfile",
|
|
122
|
+
"manifest",
|
|
123
|
+
"package",
|
|
124
|
+
"project",
|
|
125
|
+
"settings",
|
|
126
|
+
"tooling",
|
|
127
|
+
"workspace",
|
|
128
|
+
"worktree",
|
|
129
|
+
]);
|
|
130
|
+
const manifestMachineFilePattern = /\.(?:cfg|cnf|conf|gradle|ini|json|kts|lock|mod|properties|sum|toml|txt|xml|ya?ml)$/i;
|
|
131
|
+
const nonManifestTokens = new Set([
|
|
132
|
+
"acceptance",
|
|
133
|
+
"archive",
|
|
134
|
+
"changelog",
|
|
135
|
+
"contract",
|
|
136
|
+
"contracts",
|
|
137
|
+
"example",
|
|
138
|
+
"examples",
|
|
139
|
+
"fixture",
|
|
140
|
+
"fixtures",
|
|
141
|
+
"guide",
|
|
142
|
+
"guides",
|
|
143
|
+
"license",
|
|
144
|
+
"manual",
|
|
145
|
+
"manuals",
|
|
146
|
+
"notice",
|
|
147
|
+
"readme",
|
|
148
|
+
"reference",
|
|
149
|
+
"references",
|
|
150
|
+
"sample",
|
|
151
|
+
"samples",
|
|
152
|
+
"schema",
|
|
153
|
+
"schemas",
|
|
154
|
+
"spec",
|
|
155
|
+
"specs",
|
|
156
|
+
"test",
|
|
157
|
+
"tests",
|
|
158
|
+
]);
|
|
159
|
+
const serviceArtifactSignalPattern = /(?:^|[._-])(compose|daemon|orchestrator|scheduler|service|socket|timer|worker)(?:[._-]|$)|\.(?:service|socket|timer)$/i;
|
|
160
|
+
const tokenizeBasename = (value) => value
|
|
161
|
+
.toLowerCase()
|
|
162
|
+
.split(/[^a-z0-9]+/)
|
|
163
|
+
.map((segment) => segment.trim())
|
|
164
|
+
.filter(Boolean);
|
|
165
|
+
const isManifestLikeBasename = (basename, segments = []) => {
|
|
166
|
+
const normalized = basename.toLowerCase();
|
|
167
|
+
const tokens = tokenizeBasename(normalized);
|
|
168
|
+
if (tokens.some((token) => manifestSignalTokens.has(token)))
|
|
169
|
+
return true;
|
|
170
|
+
if (!manifestMachineFilePattern.test(normalized))
|
|
171
|
+
return false;
|
|
172
|
+
if (segments.length <= 2 && tokens.length <= 3 && !tokens.some((token) => nonManifestTokens.has(token))) {
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
return false;
|
|
176
|
+
};
|
|
177
|
+
const isServiceArtifactBasename = (basename) => serviceArtifactSignalPattern.test(basename.toLowerCase());
|
|
23
178
|
const normalizeText = (value) => normalizeCoverageText(value);
|
|
24
179
|
const normalizeAnchor = normalizeCoverageAnchor;
|
|
180
|
+
const toPlannedGapBundle = (entry) => ({
|
|
181
|
+
kind: entry.bundle.kind,
|
|
182
|
+
domain: entry.bundle.domain,
|
|
183
|
+
values: entry.bundle.values,
|
|
184
|
+
anchors: entry.bundle.normalizedAnchors,
|
|
185
|
+
implementationTargets: entry.implementationTargets,
|
|
186
|
+
});
|
|
25
187
|
const unique = (items) => Array.from(new Set(items.filter(Boolean)));
|
|
26
188
|
const tokenizeCoverageSignal = (value) => unique(normalizeCoverageText(value)
|
|
27
189
|
.split(/\s+/)
|
|
@@ -70,6 +232,44 @@ const implementationRootWeight = (target) => {
|
|
|
70
232
|
}
|
|
71
233
|
return score;
|
|
72
234
|
};
|
|
235
|
+
const classifyImplementationTarget = (target) => {
|
|
236
|
+
const normalized = normalizeFolderEntry(target)?.toLowerCase() ?? normalizeText(target);
|
|
237
|
+
const segments = normalized.split("/").filter(Boolean);
|
|
238
|
+
const basename = segments[segments.length - 1] ?? normalized;
|
|
239
|
+
const isServiceArtifact = isServiceArtifactBasename(basename);
|
|
240
|
+
if (segments.some((segment) => supportRootSegments.has(segment))) {
|
|
241
|
+
return { normalized, basename, segments, kind: "doc", isServiceArtifact };
|
|
242
|
+
}
|
|
243
|
+
if (isManifestLikeBasename(basename, segments) || isServiceArtifact) {
|
|
244
|
+
return { normalized, basename, segments, kind: "manifest", isServiceArtifact };
|
|
245
|
+
}
|
|
246
|
+
if (segments.some((segment) => testPathSegments.has(segment))) {
|
|
247
|
+
return { normalized, basename, segments, kind: "test", isServiceArtifact };
|
|
248
|
+
}
|
|
249
|
+
if (segments.some((segment) => opsPathSegments.has(segment))) {
|
|
250
|
+
return { normalized, basename, segments, kind: "ops", isServiceArtifact };
|
|
251
|
+
}
|
|
252
|
+
if (segments.some((segment) => interfacePathSegments.has(segment))) {
|
|
253
|
+
return { normalized, basename, segments, kind: "interface", isServiceArtifact };
|
|
254
|
+
}
|
|
255
|
+
if (segments.some((segment) => dataPathSegments.has(segment))) {
|
|
256
|
+
return { normalized, basename, segments, kind: "data", isServiceArtifact };
|
|
257
|
+
}
|
|
258
|
+
if (segments.some((segment) => runtimePathSegments.has(segment))) {
|
|
259
|
+
return { normalized, basename, segments, kind: "runtime", isServiceArtifact };
|
|
260
|
+
}
|
|
261
|
+
return { normalized, basename, segments, kind: "unknown", isServiceArtifact };
|
|
262
|
+
};
|
|
263
|
+
const deriveSemanticTargetNeeds = (bundle) => {
|
|
264
|
+
const corpus = normalizeText([bundle.domain, ...bundle.values].join(" "));
|
|
265
|
+
return {
|
|
266
|
+
wantsVerification: /\b(verify|verification|acceptance|scenario|suite|test|tests|quality|gate|matrix)\b/.test(corpus),
|
|
267
|
+
wantsOps: /\b(rollback|recovery|replay|restart|rotation|drill|runbook|failover|release|startup|deploy|deployment|operations?|incident|compromise)\b/.test(corpus),
|
|
268
|
+
wantsInterface: /\b(contract|interface|schema|policy|policies|oracle|gateway|api|protocol)\b/.test(corpus),
|
|
269
|
+
wantsData: /\b(data|storage|cache|db|database|ledger|pipeline|metering|pricing)\b/.test(corpus),
|
|
270
|
+
wantsProvider: /\b(provider|providers|gateway|gateways|rpc|adapter|adapters|sanctions|moderation|kyt)\b/.test(corpus),
|
|
271
|
+
};
|
|
272
|
+
};
|
|
73
273
|
const inferImplementationTargets = (bundle, availablePaths, limit = 3) => {
|
|
74
274
|
const explicitTargets = bundle.values
|
|
75
275
|
.map((value) => normalizeFolderEntry(value))
|
|
@@ -79,24 +279,135 @@ const inferImplementationTargets = (bundle, availablePaths, limit = 3) => {
|
|
|
79
279
|
}
|
|
80
280
|
const anchorTokens = tokenizeCoverageSignal(normalizeText([bundle.domain, ...bundle.values].join(" ")).replace(/[-/]+/g, " "));
|
|
81
281
|
const domainNeedle = bundle.domain.replace(/[-_]+/g, " ").trim();
|
|
282
|
+
const targetNeeds = deriveSemanticTargetNeeds(bundle);
|
|
82
283
|
const scored = availablePaths
|
|
83
284
|
.map((candidate) => normalizeFolderEntry(candidate))
|
|
84
285
|
.filter((candidate) => Boolean(candidate))
|
|
85
286
|
.filter((candidate) => bundle.kind === "folder" || !candidate.startsWith("docs/"))
|
|
86
287
|
.map((candidate) => {
|
|
87
|
-
const
|
|
288
|
+
const classification = classifyImplementationTarget(candidate);
|
|
289
|
+
const normalizedCandidate = classification.normalized.replace(/\//g, " ");
|
|
88
290
|
const overlap = anchorTokens.filter((token) => normalizedCandidate.includes(token)).length;
|
|
89
291
|
const hasDomainMatch = domainNeedle.length > 0 && normalizedCandidate.includes(domainNeedle);
|
|
90
|
-
const
|
|
292
|
+
const semanticEvidence = (targetNeeds.wantsVerification && classification.kind === "test") ||
|
|
293
|
+
(targetNeeds.wantsOps && classification.kind === "ops") ||
|
|
294
|
+
(targetNeeds.wantsInterface && classification.kind === "interface") ||
|
|
295
|
+
(targetNeeds.wantsData && classification.kind === "data") ||
|
|
296
|
+
(targetNeeds.wantsProvider &&
|
|
297
|
+
(classification.kind === "interface" ||
|
|
298
|
+
classification.kind === "runtime" ||
|
|
299
|
+
classification.kind === "ops"));
|
|
300
|
+
const hasEvidence = overlap > 0 || hasDomainMatch || semanticEvidence;
|
|
91
301
|
const score = implementationRootWeight(candidate) +
|
|
92
302
|
overlap * 20 +
|
|
93
303
|
(hasDomainMatch ? 15 : 0) -
|
|
94
|
-
(candidate.startsWith("docs/") ? 25 : 0)
|
|
95
|
-
|
|
304
|
+
(candidate.startsWith("docs/") ? 25 : 0) +
|
|
305
|
+
(targetNeeds.wantsVerification && classification.kind === "test" ? 45 : 0) +
|
|
306
|
+
(targetNeeds.wantsOps && classification.kind === "ops" ? 60 : 0) +
|
|
307
|
+
(targetNeeds.wantsInterface && classification.kind === "interface" ? 55 : 0) +
|
|
308
|
+
(targetNeeds.wantsData && classification.kind === "data" ? 55 : 0) +
|
|
309
|
+
(targetNeeds.wantsProvider &&
|
|
310
|
+
(classification.kind === "interface" || classification.kind === "runtime")
|
|
311
|
+
? 35
|
|
312
|
+
: 0) -
|
|
313
|
+
(classification.kind === "manifest" ? 120 : 0) -
|
|
314
|
+
(classification.kind === "doc" ? 120 : 0);
|
|
315
|
+
return { candidate, classification, score, hasEvidence };
|
|
96
316
|
})
|
|
97
317
|
.filter((entry) => entry.hasEvidence)
|
|
98
318
|
.sort((left, right) => right.score - left.score || left.candidate.localeCompare(right.candidate));
|
|
99
|
-
|
|
319
|
+
const hasStrongCandidates = scored.some((entry) => entry.score > 0 &&
|
|
320
|
+
(entry.classification.kind === "runtime" ||
|
|
321
|
+
entry.classification.kind === "interface" ||
|
|
322
|
+
entry.classification.kind === "data" ||
|
|
323
|
+
entry.classification.kind === "test" ||
|
|
324
|
+
entry.classification.kind === "ops"));
|
|
325
|
+
const filtered = scored.filter((entry) => {
|
|
326
|
+
if (entry.score <= 0)
|
|
327
|
+
return false;
|
|
328
|
+
if (!hasStrongCandidates)
|
|
329
|
+
return true;
|
|
330
|
+
if (entry.classification.kind === "manifest")
|
|
331
|
+
return false;
|
|
332
|
+
if (entry.classification.kind === "doc")
|
|
333
|
+
return false;
|
|
334
|
+
return true;
|
|
335
|
+
});
|
|
336
|
+
const prioritized = (filtered.length > 0 ? filtered : scored.filter((entry) => entry.score > 0)).filter((entry, _, entries) => {
|
|
337
|
+
if (isStructuredFilePath(entry.classification.basename))
|
|
338
|
+
return true;
|
|
339
|
+
return !entries.some((other) => {
|
|
340
|
+
if (other === entry)
|
|
341
|
+
return false;
|
|
342
|
+
if (other.score + 5 < entry.score)
|
|
343
|
+
return false;
|
|
344
|
+
if (!other.candidate.startsWith(`${entry.candidate}/`))
|
|
345
|
+
return false;
|
|
346
|
+
return (other.classification.kind === entry.classification.kind ||
|
|
347
|
+
isStructuredFilePath(other.classification.basename));
|
|
348
|
+
});
|
|
349
|
+
});
|
|
350
|
+
const prioritizedFiles = prioritized.filter((entry) => isStructuredFilePath(entry.classification.basename));
|
|
351
|
+
const fileLifted = prioritized.map((entry) => {
|
|
352
|
+
if (isStructuredFilePath(entry.classification.basename))
|
|
353
|
+
return entry;
|
|
354
|
+
const replacement = prioritizedFiles.find((other) => {
|
|
355
|
+
if (!other.candidate.startsWith(`${entry.candidate}/`))
|
|
356
|
+
return false;
|
|
357
|
+
if (other.score + 10 < entry.score)
|
|
358
|
+
return false;
|
|
359
|
+
return (other.classification.kind === entry.classification.kind ||
|
|
360
|
+
other.classification.kind === "runtime" ||
|
|
361
|
+
entry.classification.kind === "unknown");
|
|
362
|
+
});
|
|
363
|
+
return replacement ?? entry;
|
|
364
|
+
});
|
|
365
|
+
const collapsed = fileLifted.filter((entry, _, entries) => {
|
|
366
|
+
if (isStructuredFilePath(entry.classification.basename))
|
|
367
|
+
return true;
|
|
368
|
+
return !entries.some((other) => {
|
|
369
|
+
if (other === entry)
|
|
370
|
+
return false;
|
|
371
|
+
if (!isStructuredFilePath(other.classification.basename))
|
|
372
|
+
return false;
|
|
373
|
+
return other.candidate.startsWith(`${entry.candidate}/`);
|
|
374
|
+
});
|
|
375
|
+
});
|
|
376
|
+
const descendantFiles = unique(availablePaths
|
|
377
|
+
.map((candidate) => normalizeFolderEntry(candidate))
|
|
378
|
+
.filter((candidate) => Boolean(candidate))
|
|
379
|
+
.filter((candidate) => isStructuredFilePath(path.basename(candidate)))
|
|
380
|
+
.map((candidate) => {
|
|
381
|
+
const classification = classifyImplementationTarget(candidate);
|
|
382
|
+
const normalizedCandidate = classification.normalized.replace(/\//g, " ");
|
|
383
|
+
const overlap = anchorTokens.filter((token) => normalizedCandidate.includes(token)).length;
|
|
384
|
+
const hasDomainMatch = domainNeedle.length > 0 && normalizedCandidate.includes(domainNeedle);
|
|
385
|
+
const semanticBonus = (targetNeeds.wantsVerification && classification.kind === "test" ? 40 : 0) +
|
|
386
|
+
(targetNeeds.wantsOps && classification.kind === "ops" ? 50 : 0) +
|
|
387
|
+
(targetNeeds.wantsInterface && classification.kind === "interface" ? 45 : 0) +
|
|
388
|
+
(targetNeeds.wantsData && classification.kind === "data" ? 45 : 0) +
|
|
389
|
+
(targetNeeds.wantsProvider &&
|
|
390
|
+
(classification.kind === "runtime" || classification.kind === "interface")
|
|
391
|
+
? 25
|
|
392
|
+
: 0);
|
|
393
|
+
return {
|
|
394
|
+
candidate,
|
|
395
|
+
score: implementationRootWeight(candidate) + overlap * 20 + (hasDomainMatch ? 20 : 0) + semanticBonus,
|
|
396
|
+
};
|
|
397
|
+
})
|
|
398
|
+
.sort((left, right) => right.score - left.score || left.candidate.localeCompare(right.candidate))
|
|
399
|
+
.map((entry) => entry.candidate));
|
|
400
|
+
const promoted = unique(collapsed.map((entry) => {
|
|
401
|
+
if (isStructuredFilePath(entry.classification.basename))
|
|
402
|
+
return entry.candidate;
|
|
403
|
+
const replacement = descendantFiles.find((candidate) => candidate.startsWith(`${entry.candidate}/`) || candidate.startsWith(`${entry.candidate.replace(/\/+$/g, "")}/`));
|
|
404
|
+
return replacement ?? entry.candidate;
|
|
405
|
+
})).filter((candidate, _, entries) => {
|
|
406
|
+
if (isStructuredFilePath(path.basename(candidate)))
|
|
407
|
+
return true;
|
|
408
|
+
return !entries.some((other) => other !== candidate && isStructuredFilePath(path.basename(other)) && other.startsWith(`${candidate}/`));
|
|
409
|
+
});
|
|
410
|
+
return promoted.slice(0, limit);
|
|
100
411
|
};
|
|
101
412
|
const summarizeImplementationTargets = (targets) => {
|
|
102
413
|
if (targets.length === 0)
|
|
@@ -137,6 +448,12 @@ const summarizeAnchorBundle = (bundle) => {
|
|
|
137
448
|
}
|
|
138
449
|
return `${bundle.normalizedAnchors[0]} (+${bundle.normalizedAnchors.length - 1} more)`;
|
|
139
450
|
};
|
|
451
|
+
const toUnresolvedBundle = (bundle) => ({
|
|
452
|
+
kind: bundle.kind,
|
|
453
|
+
domain: bundle.domain,
|
|
454
|
+
values: [...bundle.values],
|
|
455
|
+
anchors: [...bundle.normalizedAnchors],
|
|
456
|
+
});
|
|
140
457
|
const buildTargetedTestGuidance = (implementationTargets) => {
|
|
141
458
|
const guidance = new Set();
|
|
142
459
|
for (const target of implementationTargets) {
|
|
@@ -666,6 +983,7 @@ export class TaskSufficiencyService {
|
|
|
666
983
|
let totalTasksAdded = 0;
|
|
667
984
|
const totalTasksUpdated = 0;
|
|
668
985
|
let satisfied = false;
|
|
986
|
+
let latestUnresolvedBundles = [];
|
|
669
987
|
for (let iteration = 1; iteration <= maxIterations; iteration += 1) {
|
|
670
988
|
const snapshot = await this.loadProjectSnapshot(request.projectKey);
|
|
671
989
|
const coverage = this.evaluateCoverage(snapshot.corpus, sectionHeadings, folderEntries, snapshot.existingAnchors);
|
|
@@ -679,6 +997,7 @@ export class TaskSufficiencyService {
|
|
|
679
997
|
totalSignals: coverage.totalSignals,
|
|
680
998
|
missingSectionCount: coverage.missingSectionHeadings.length,
|
|
681
999
|
missingFolderCount: coverage.missingFolderEntries.length,
|
|
1000
|
+
unresolvedBundleCount: 0,
|
|
682
1001
|
createdTaskKeys: [],
|
|
683
1002
|
});
|
|
684
1003
|
await this.jobService.writeCheckpoint(job.id, {
|
|
@@ -690,6 +1009,7 @@ export class TaskSufficiencyService {
|
|
|
690
1009
|
totalSignals: coverage.totalSignals,
|
|
691
1010
|
missingSectionCount: coverage.missingSectionHeadings.length,
|
|
692
1011
|
missingFolderCount: coverage.missingFolderEntries.length,
|
|
1012
|
+
unresolvedBundleCount: 0,
|
|
693
1013
|
action: "complete",
|
|
694
1014
|
},
|
|
695
1015
|
});
|
|
@@ -698,6 +1018,7 @@ export class TaskSufficiencyService {
|
|
|
698
1018
|
const gapItems = this.buildGapItems(coverage, snapshot.existingAnchors, maxTasksPerIteration);
|
|
699
1019
|
const gapBundles = this.bundleGapItems(gapItems, maxTasksPerIteration);
|
|
700
1020
|
if (gapBundles.length === 0) {
|
|
1021
|
+
latestUnresolvedBundles = [];
|
|
701
1022
|
warnings.push(`Iteration ${iteration}: unresolved SDS gaps remain but no insertable gap items were identified.`);
|
|
702
1023
|
iterations.push({
|
|
703
1024
|
iteration,
|
|
@@ -705,6 +1026,7 @@ export class TaskSufficiencyService {
|
|
|
705
1026
|
totalSignals: coverage.totalSignals,
|
|
706
1027
|
missingSectionCount: coverage.missingSectionHeadings.length,
|
|
707
1028
|
missingFolderCount: coverage.missingFolderEntries.length,
|
|
1029
|
+
unresolvedBundleCount: 0,
|
|
708
1030
|
createdTaskKeys: [],
|
|
709
1031
|
});
|
|
710
1032
|
break;
|
|
@@ -715,6 +1037,7 @@ export class TaskSufficiencyService {
|
|
|
715
1037
|
}));
|
|
716
1038
|
const actionableGapBundles = plannedGapBundles.filter((entry) => entry.implementationTargets.length > 0);
|
|
717
1039
|
const unresolvedGapBundles = plannedGapBundles.filter((entry) => entry.implementationTargets.length === 0);
|
|
1040
|
+
latestUnresolvedBundles = unresolvedGapBundles.map((entry) => toUnresolvedBundle(entry.bundle));
|
|
718
1041
|
if (unresolvedGapBundles.length > 0) {
|
|
719
1042
|
warnings.push(`Iteration ${iteration}: ${unresolvedGapBundles.length} SDS gap bundle(s) remain unresolved because no concrete implementation targets were inferred (${unresolvedGapBundles
|
|
720
1043
|
.map((entry) => summarizeAnchorBundle(entry.bundle))
|
|
@@ -728,6 +1051,7 @@ export class TaskSufficiencyService {
|
|
|
728
1051
|
totalSignals: coverage.totalSignals,
|
|
729
1052
|
missingSectionCount: coverage.missingSectionHeadings.length,
|
|
730
1053
|
missingFolderCount: coverage.missingFolderEntries.length,
|
|
1054
|
+
unresolvedBundleCount: latestUnresolvedBundles.length,
|
|
731
1055
|
createdTaskKeys: [],
|
|
732
1056
|
});
|
|
733
1057
|
await this.jobService.writeCheckpoint(job.id, {
|
|
@@ -739,13 +1063,9 @@ export class TaskSufficiencyService {
|
|
|
739
1063
|
totalSignals: coverage.totalSignals,
|
|
740
1064
|
missingSectionCount: coverage.missingSectionHeadings.length,
|
|
741
1065
|
missingFolderCount: coverage.missingFolderEntries.length,
|
|
1066
|
+
unresolvedBundleCount: latestUnresolvedBundles.length,
|
|
742
1067
|
action: "unresolved",
|
|
743
|
-
unresolvedGapItems:
|
|
744
|
-
kind: entry.bundle.kind,
|
|
745
|
-
domain: entry.bundle.domain,
|
|
746
|
-
values: entry.bundle.values,
|
|
747
|
-
anchors: entry.bundle.normalizedAnchors,
|
|
748
|
-
})),
|
|
1068
|
+
unresolvedGapItems: latestUnresolvedBundles,
|
|
749
1069
|
},
|
|
750
1070
|
});
|
|
751
1071
|
break;
|
|
@@ -757,6 +1077,7 @@ export class TaskSufficiencyService {
|
|
|
757
1077
|
totalSignals: coverage.totalSignals,
|
|
758
1078
|
missingSectionCount: coverage.missingSectionHeadings.length,
|
|
759
1079
|
missingFolderCount: coverage.missingFolderEntries.length,
|
|
1080
|
+
unresolvedBundleCount: latestUnresolvedBundles.length,
|
|
760
1081
|
createdTaskKeys: [],
|
|
761
1082
|
});
|
|
762
1083
|
await this.jobService.writeCheckpoint(job.id, {
|
|
@@ -768,6 +1089,7 @@ export class TaskSufficiencyService {
|
|
|
768
1089
|
totalSignals: coverage.totalSignals,
|
|
769
1090
|
missingSectionCount: coverage.missingSectionHeadings.length,
|
|
770
1091
|
missingFolderCount: coverage.missingFolderEntries.length,
|
|
1092
|
+
unresolvedBundleCount: latestUnresolvedBundles.length,
|
|
771
1093
|
action: "dry_run",
|
|
772
1094
|
proposedGapItems: actionableGapBundles.map((entry) => ({
|
|
773
1095
|
kind: entry.bundle.kind,
|
|
@@ -775,12 +1097,7 @@ export class TaskSufficiencyService {
|
|
|
775
1097
|
values: entry.bundle.values,
|
|
776
1098
|
implementationTargets: entry.implementationTargets,
|
|
777
1099
|
})),
|
|
778
|
-
unresolvedGapItems:
|
|
779
|
-
kind: entry.bundle.kind,
|
|
780
|
-
domain: entry.bundle.domain,
|
|
781
|
-
values: entry.bundle.values,
|
|
782
|
-
anchors: entry.bundle.normalizedAnchors,
|
|
783
|
-
})),
|
|
1100
|
+
unresolvedGapItems: latestUnresolvedBundles,
|
|
784
1101
|
},
|
|
785
1102
|
});
|
|
786
1103
|
break;
|
|
@@ -805,6 +1122,7 @@ export class TaskSufficiencyService {
|
|
|
805
1122
|
totalSignals: coverage.totalSignals,
|
|
806
1123
|
missingSectionCount: coverage.missingSectionHeadings.length,
|
|
807
1124
|
missingFolderCount: coverage.missingFolderEntries.length,
|
|
1125
|
+
unresolvedBundleCount: latestUnresolvedBundles.length,
|
|
808
1126
|
createdTaskKeys,
|
|
809
1127
|
});
|
|
810
1128
|
await this.jobService.writeCheckpoint(job.id, {
|
|
@@ -816,6 +1134,7 @@ export class TaskSufficiencyService {
|
|
|
816
1134
|
totalSignals: coverage.totalSignals,
|
|
817
1135
|
missingSectionCount: coverage.missingSectionHeadings.length,
|
|
818
1136
|
missingFolderCount: coverage.missingFolderEntries.length,
|
|
1137
|
+
unresolvedBundleCount: latestUnresolvedBundles.length,
|
|
819
1138
|
createdTaskKeys,
|
|
820
1139
|
addedCount: createdTaskKeys.length,
|
|
821
1140
|
},
|
|
@@ -831,6 +1150,17 @@ export class TaskSufficiencyService {
|
|
|
831
1150
|
if (!satisfied) {
|
|
832
1151
|
warnings.push(`Sufficiency target not reached (coverage=${finalCoverage.coverageRatio}, threshold=${minCoverageRatio}) after ${iterations.length} iteration(s).`);
|
|
833
1152
|
}
|
|
1153
|
+
const finalGapItemLimit = Math.max(1, finalCoverage.missingSectionHeadings.length + finalCoverage.missingFolderEntries.length);
|
|
1154
|
+
const finalGapItems = this.buildGapItems(finalCoverage, finalSnapshot.existingAnchors, finalGapItemLimit);
|
|
1155
|
+
const finalGapBundles = this.bundleGapItems(finalGapItems, finalGapItemLimit);
|
|
1156
|
+
const finalPlannedGapBundles = finalGapBundles.map((bundle) => ({
|
|
1157
|
+
bundle,
|
|
1158
|
+
implementationTargets: inferImplementationTargets(bundle, folderEntries, 3),
|
|
1159
|
+
}));
|
|
1160
|
+
const plannedGapBundles = finalPlannedGapBundles.map(toPlannedGapBundle);
|
|
1161
|
+
const unresolvedBundles = finalPlannedGapBundles
|
|
1162
|
+
.filter((entry) => entry.implementationTargets.length === 0)
|
|
1163
|
+
.map((entry) => toUnresolvedBundle(entry.bundle));
|
|
834
1164
|
const report = {
|
|
835
1165
|
projectKey: request.projectKey,
|
|
836
1166
|
sourceCommand,
|
|
@@ -862,6 +1192,8 @@ export class TaskSufficiencyService {
|
|
|
862
1192
|
missingSectionHeadings: finalCoverage.missingSectionHeadings,
|
|
863
1193
|
missingFolderEntries: finalCoverage.missingFolderEntries,
|
|
864
1194
|
},
|
|
1195
|
+
plannedGapBundles,
|
|
1196
|
+
unresolvedBundles,
|
|
865
1197
|
iterations,
|
|
866
1198
|
warnings,
|
|
867
1199
|
};
|
|
@@ -877,6 +1209,7 @@ export class TaskSufficiencyService {
|
|
|
877
1209
|
totalTasksUpdated,
|
|
878
1210
|
finalTotalSignals: finalCoverage.totalSignals,
|
|
879
1211
|
finalCoverageRatio: finalCoverage.coverageRatio,
|
|
1212
|
+
unresolvedBundleCount: unresolvedBundles.length,
|
|
880
1213
|
},
|
|
881
1214
|
});
|
|
882
1215
|
const result = {
|
|
@@ -899,6 +1232,8 @@ export class TaskSufficiencyService {
|
|
|
899
1232
|
folders: finalCoverage.missingFolderEntries.length,
|
|
900
1233
|
total: finalCoverage.missingSectionHeadings.length + finalCoverage.missingFolderEntries.length,
|
|
901
1234
|
},
|
|
1235
|
+
plannedGapBundles,
|
|
1236
|
+
unresolvedBundles,
|
|
902
1237
|
iterations,
|
|
903
1238
|
reportPath,
|
|
904
1239
|
reportHistoryPath: historyPath,
|
|
@@ -917,6 +1252,7 @@ export class TaskSufficiencyService {
|
|
|
917
1252
|
finalCoverageRatio: finalCoverage.coverageRatio,
|
|
918
1253
|
remainingSectionCount: finalCoverage.missingSectionHeadings.length,
|
|
919
1254
|
remainingFolderCount: finalCoverage.missingFolderEntries.length,
|
|
1255
|
+
unresolvedBundleCount: unresolvedBundles.length,
|
|
920
1256
|
reportPath,
|
|
921
1257
|
reportHistoryPath: historyPath,
|
|
922
1258
|
warnings,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mcoda/core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.37",
|
|
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/
|
|
39
|
-
"@mcoda/integrations": "0.1.
|
|
35
|
+
"@mcoda/db": "0.1.37",
|
|
36
|
+
"@mcoda/shared": "0.1.37",
|
|
37
|
+
"@mcoda/generators": "0.1.37",
|
|
38
|
+
"@mcoda/agents": "0.1.37",
|
|
39
|
+
"@mcoda/integrations": "0.1.37"
|
|
40
40
|
},
|
|
41
41
|
"scripts": {
|
|
42
42
|
"build": "tsc -p tsconfig.json",
|