@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.
- package/CHANGELOG.md +3 -0
- package/README.md +2 -2
- package/dist/api/AgentsApi.d.ts +9 -1
- package/dist/api/AgentsApi.d.ts.map +1 -1
- package/dist/api/AgentsApi.js +201 -6
- package/dist/api/QaTasksApi.d.ts.map +1 -1
- package/dist/api/QaTasksApi.js +6 -0
- package/dist/api/TasksApi.d.ts.map +1 -1
- package/dist/api/TasksApi.js +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/prompts/PdrPrompts.d.ts.map +1 -1
- package/dist/prompts/PdrPrompts.js +9 -1
- package/dist/prompts/SdsPrompts.d.ts.map +1 -1
- package/dist/prompts/SdsPrompts.js +9 -0
- package/dist/services/agents/AgentRatingFormula.d.ts +27 -0
- package/dist/services/agents/AgentRatingFormula.d.ts.map +1 -0
- package/dist/services/agents/AgentRatingFormula.js +45 -0
- package/dist/services/agents/AgentRatingService.d.ts +60 -0
- package/dist/services/agents/AgentRatingService.d.ts.map +1 -0
- package/dist/services/agents/AgentRatingService.js +363 -0
- package/dist/services/agents/GatewayAgentService.d.ts +11 -0
- package/dist/services/agents/GatewayAgentService.d.ts.map +1 -1
- package/dist/services/agents/GatewayAgentService.js +525 -84
- package/dist/services/agents/GatewayHandoff.d.ts +11 -0
- package/dist/services/agents/GatewayHandoff.d.ts.map +1 -0
- package/dist/services/agents/GatewayHandoff.js +141 -0
- package/dist/services/agents/RoutingService.d.ts +1 -0
- package/dist/services/agents/RoutingService.d.ts.map +1 -1
- package/dist/services/agents/RoutingService.js +4 -4
- package/dist/services/backlog/BacklogService.d.ts +23 -0
- package/dist/services/backlog/BacklogService.d.ts.map +1 -1
- package/dist/services/backlog/BacklogService.js +62 -7
- package/dist/services/backlog/TaskOrderingHeuristics.d.ts +12 -0
- package/dist/services/backlog/TaskOrderingHeuristics.d.ts.map +1 -0
- package/dist/services/backlog/TaskOrderingHeuristics.js +56 -0
- package/dist/services/backlog/TaskOrderingService.d.ts +17 -4
- package/dist/services/backlog/TaskOrderingService.d.ts.map +1 -1
- package/dist/services/backlog/TaskOrderingService.js +538 -79
- package/dist/services/docs/DocInventory.d.ts +11 -0
- package/dist/services/docs/DocInventory.d.ts.map +1 -0
- package/dist/services/docs/DocInventory.js +230 -0
- package/dist/services/docs/DocgenRunContext.d.ts +59 -0
- package/dist/services/docs/DocgenRunContext.d.ts.map +1 -0
- package/dist/services/docs/DocgenRunContext.js +4 -0
- package/dist/services/docs/DocsService.d.ts +70 -3
- package/dist/services/docs/DocsService.d.ts.map +1 -1
- package/dist/services/docs/DocsService.js +1930 -89
- package/dist/services/docs/alignment/DocAlignmentGraph.d.ts +23 -0
- package/dist/services/docs/alignment/DocAlignmentGraph.d.ts.map +1 -0
- package/dist/services/docs/alignment/DocAlignmentGraph.js +78 -0
- package/dist/services/docs/alignment/DocAlignmentPatcher.d.ts +19 -0
- package/dist/services/docs/alignment/DocAlignmentPatcher.d.ts.map +1 -0
- package/dist/services/docs/alignment/DocAlignmentPatcher.js +222 -0
- package/dist/services/docs/patch/DocPatchEngine.d.ts +57 -0
- package/dist/services/docs/patch/DocPatchEngine.d.ts.map +1 -0
- package/dist/services/docs/patch/DocPatchEngine.js +331 -0
- package/dist/services/docs/review/Glossary.d.ts +16 -0
- package/dist/services/docs/review/Glossary.d.ts.map +1 -0
- package/dist/services/docs/review/Glossary.js +47 -0
- package/dist/services/docs/review/ReviewReportRenderer.d.ts +3 -0
- package/dist/services/docs/review/ReviewReportRenderer.d.ts.map +1 -0
- package/dist/services/docs/review/ReviewReportRenderer.js +133 -0
- package/dist/services/docs/review/ReviewReportSchema.d.ts +39 -0
- package/dist/services/docs/review/ReviewReportSchema.d.ts.map +1 -0
- package/dist/services/docs/review/ReviewReportSchema.js +47 -0
- package/dist/services/docs/review/ReviewTypes.d.ts +76 -0
- package/dist/services/docs/review/ReviewTypes.d.ts.map +1 -0
- package/dist/services/docs/review/ReviewTypes.js +94 -0
- package/dist/services/docs/review/gates/AdminOpenApiSpecGate.d.ts +7 -0
- package/dist/services/docs/review/gates/AdminOpenApiSpecGate.d.ts.map +1 -0
- package/dist/services/docs/review/gates/AdminOpenApiSpecGate.js +93 -0
- package/dist/services/docs/review/gates/ApiPathConsistencyGate.d.ts +7 -0
- package/dist/services/docs/review/gates/ApiPathConsistencyGate.d.ts.map +1 -0
- package/dist/services/docs/review/gates/ApiPathConsistencyGate.js +308 -0
- package/dist/services/docs/review/gates/BuildReadyCompletenessGate.d.ts +8 -0
- package/dist/services/docs/review/gates/BuildReadyCompletenessGate.d.ts.map +1 -0
- package/dist/services/docs/review/gates/BuildReadyCompletenessGate.js +278 -0
- package/dist/services/docs/review/gates/DeploymentBlueprintGate.d.ts +8 -0
- package/dist/services/docs/review/gates/DeploymentBlueprintGate.d.ts.map +1 -0
- package/dist/services/docs/review/gates/DeploymentBlueprintGate.js +487 -0
- package/dist/services/docs/review/gates/NoMaybesGate.d.ts +8 -0
- package/dist/services/docs/review/gates/NoMaybesGate.d.ts.map +1 -0
- package/dist/services/docs/review/gates/NoMaybesGate.js +145 -0
- package/dist/services/docs/review/gates/OpenApiCoverageGate.d.ts +7 -0
- package/dist/services/docs/review/gates/OpenApiCoverageGate.d.ts.map +1 -0
- package/dist/services/docs/review/gates/OpenApiCoverageGate.js +266 -0
- package/dist/services/docs/review/gates/OpenApiSchemaSanityGate.d.ts +7 -0
- package/dist/services/docs/review/gates/OpenApiSchemaSanityGate.d.ts.map +1 -0
- package/dist/services/docs/review/gates/OpenApiSchemaSanityGate.js +59 -0
- package/dist/services/docs/review/gates/OpenQuestionsGate.d.ts +7 -0
- package/dist/services/docs/review/gates/OpenQuestionsGate.d.ts.map +1 -0
- package/dist/services/docs/review/gates/OpenQuestionsGate.js +200 -0
- package/dist/services/docs/review/gates/PdrInterfacesGate.d.ts +7 -0
- package/dist/services/docs/review/gates/PdrInterfacesGate.d.ts.map +1 -0
- package/dist/services/docs/review/gates/PdrInterfacesGate.js +159 -0
- package/dist/services/docs/review/gates/PdrOpenQuestionsGate.d.ts +8 -0
- package/dist/services/docs/review/gates/PdrOpenQuestionsGate.d.ts.map +1 -0
- package/dist/services/docs/review/gates/PdrOpenQuestionsGate.js +129 -0
- package/dist/services/docs/review/gates/PdrOwnershipGate.d.ts +7 -0
- package/dist/services/docs/review/gates/PdrOwnershipGate.d.ts.map +1 -0
- package/dist/services/docs/review/gates/PdrOwnershipGate.js +169 -0
- package/dist/services/docs/review/gates/PlaceholderArtifactGate.d.ts +10 -0
- package/dist/services/docs/review/gates/PlaceholderArtifactGate.d.ts.map +1 -0
- package/dist/services/docs/review/gates/PlaceholderArtifactGate.js +261 -0
- package/dist/services/docs/review/gates/RfpConsentGate.d.ts +6 -0
- package/dist/services/docs/review/gates/RfpConsentGate.d.ts.map +1 -0
- package/dist/services/docs/review/gates/RfpConsentGate.js +127 -0
- package/dist/services/docs/review/gates/RfpDefinitionGate.d.ts +7 -0
- package/dist/services/docs/review/gates/RfpDefinitionGate.d.ts.map +1 -0
- package/dist/services/docs/review/gates/RfpDefinitionGate.js +173 -0
- package/dist/services/docs/review/gates/SdsAdaptersGate.d.ts +7 -0
- package/dist/services/docs/review/gates/SdsAdaptersGate.d.ts.map +1 -0
- package/dist/services/docs/review/gates/SdsAdaptersGate.js +196 -0
- package/dist/services/docs/review/gates/SdsDecisionsGate.d.ts +7 -0
- package/dist/services/docs/review/gates/SdsDecisionsGate.d.ts.map +1 -0
- package/dist/services/docs/review/gates/SdsDecisionsGate.js +89 -0
- package/dist/services/docs/review/gates/SdsOpsGate.d.ts +7 -0
- package/dist/services/docs/review/gates/SdsOpsGate.d.ts.map +1 -0
- package/dist/services/docs/review/gates/SdsOpsGate.js +162 -0
- package/dist/services/docs/review/gates/SdsPolicyTelemetryGate.d.ts +7 -0
- package/dist/services/docs/review/gates/SdsPolicyTelemetryGate.d.ts.map +1 -0
- package/dist/services/docs/review/gates/SdsPolicyTelemetryGate.js +166 -0
- package/dist/services/docs/review/gates/SqlRequiredTablesGate.d.ts +7 -0
- package/dist/services/docs/review/gates/SqlRequiredTablesGate.d.ts.map +1 -0
- package/dist/services/docs/review/gates/SqlRequiredTablesGate.js +273 -0
- package/dist/services/docs/review/gates/SqlSyntaxGate.d.ts +7 -0
- package/dist/services/docs/review/gates/SqlSyntaxGate.d.ts.map +1 -0
- package/dist/services/docs/review/gates/SqlSyntaxGate.js +203 -0
- package/dist/services/docs/review/gates/TerminologyNormalizationGate.d.ts +9 -0
- package/dist/services/docs/review/gates/TerminologyNormalizationGate.d.ts.map +1 -0
- package/dist/services/docs/review/gates/TerminologyNormalizationGate.js +217 -0
- package/dist/services/docs/review/glossary.json +47 -0
- 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 +66 -18
- package/dist/services/estimate/VelocityService.d.ts +4 -0
- package/dist/services/estimate/VelocityService.d.ts.map +1 -1
- package/dist/services/estimate/VelocityService.js +179 -36
- package/dist/services/estimate/types.d.ts +1 -0
- package/dist/services/estimate/types.d.ts.map +1 -1
- package/dist/services/execution/GatewayTrioService.d.ts +200 -0
- package/dist/services/execution/GatewayTrioService.d.ts.map +1 -0
- package/dist/services/execution/GatewayTrioService.js +2492 -0
- package/dist/services/execution/QaApiRunner.d.ts +30 -0
- package/dist/services/execution/QaApiRunner.d.ts.map +1 -0
- package/dist/services/execution/QaApiRunner.js +881 -0
- package/dist/services/execution/QaFollowupService.d.ts +2 -0
- package/dist/services/execution/QaFollowupService.d.ts.map +1 -1
- package/dist/services/execution/QaFollowupService.js +9 -2
- package/dist/services/execution/QaPlanValidator.d.ts +10 -0
- package/dist/services/execution/QaPlanValidator.d.ts.map +1 -0
- package/dist/services/execution/QaPlanValidator.js +128 -0
- package/dist/services/execution/QaProfileService.d.ts +27 -1
- package/dist/services/execution/QaProfileService.d.ts.map +1 -1
- package/dist/services/execution/QaProfileService.js +354 -7
- package/dist/services/execution/QaTasksService.d.ts +59 -1
- package/dist/services/execution/QaTasksService.d.ts.map +1 -1
- package/dist/services/execution/QaTasksService.js +3347 -318
- package/dist/services/execution/QaTestCommandBuilder.d.ts +51 -0
- package/dist/services/execution/QaTestCommandBuilder.d.ts.map +1 -0
- package/dist/services/execution/QaTestCommandBuilder.js +495 -0
- package/dist/services/execution/TaskSelectionService.d.ts +4 -2
- package/dist/services/execution/TaskSelectionService.d.ts.map +1 -1
- package/dist/services/execution/TaskSelectionService.js +144 -28
- package/dist/services/execution/TaskStateService.d.ts +19 -6
- package/dist/services/execution/TaskStateService.d.ts.map +1 -1
- package/dist/services/execution/TaskStateService.js +128 -13
- package/dist/services/execution/WorkOnTasksService.d.ts +32 -1
- package/dist/services/execution/WorkOnTasksService.d.ts.map +1 -1
- package/dist/services/execution/WorkOnTasksService.js +4667 -722
- package/dist/services/jobs/JobInsightsService.d.ts +4 -0
- package/dist/services/jobs/JobInsightsService.d.ts.map +1 -1
- package/dist/services/jobs/JobInsightsService.js +51 -5
- package/dist/services/jobs/JobResumeService.d.ts.map +1 -1
- package/dist/services/jobs/JobResumeService.js +23 -10
- package/dist/services/jobs/JobService.d.ts +56 -4
- package/dist/services/jobs/JobService.d.ts.map +1 -1
- package/dist/services/jobs/JobService.js +232 -1
- package/dist/services/openapi/OpenApiService.d.ts +51 -0
- package/dist/services/openapi/OpenApiService.d.ts.map +1 -1
- package/dist/services/openapi/OpenApiService.js +953 -106
- package/dist/services/planning/CreateTasksService.d.ts +21 -0
- package/dist/services/planning/CreateTasksService.d.ts.map +1 -1
- package/dist/services/planning/CreateTasksService.js +569 -31
- package/dist/services/planning/RefineTasksService.d.ts +9 -0
- package/dist/services/planning/RefineTasksService.d.ts.map +1 -1
- package/dist/services/planning/RefineTasksService.js +409 -59
- package/dist/services/review/CodeReviewService.d.ts +18 -0
- package/dist/services/review/CodeReviewService.d.ts.map +1 -1
- package/dist/services/review/CodeReviewService.js +1309 -167
- package/dist/services/review/ReviewNormalizer.d.ts +9 -0
- package/dist/services/review/ReviewNormalizer.d.ts.map +1 -0
- package/dist/services/review/ReviewNormalizer.js +147 -0
- package/dist/services/shared/AuthErrors.d.ts +3 -0
- package/dist/services/shared/AuthErrors.d.ts.map +1 -0
- package/dist/services/shared/AuthErrors.js +17 -0
- package/dist/services/shared/DocdexGuidance.d.ts +7 -0
- package/dist/services/shared/DocdexGuidance.d.ts.map +1 -0
- package/dist/services/shared/DocdexGuidance.js +12 -0
- package/dist/services/shared/ProjectGuidance.d.ts +17 -0
- package/dist/services/shared/ProjectGuidance.d.ts.map +1 -0
- package/dist/services/shared/ProjectGuidance.js +78 -0
- package/dist/services/system/ToolDenylist.d.ts +13 -0
- package/dist/services/system/ToolDenylist.d.ts.map +1 -0
- package/dist/services/system/ToolDenylist.js +85 -0
- package/dist/services/tasks/TaskCommentFormatter.d.ts +20 -0
- package/dist/services/tasks/TaskCommentFormatter.d.ts.map +1 -0
- package/dist/services/tasks/TaskCommentFormatter.js +54 -0
- package/dist/services/telemetry/TelemetryService.d.ts.map +1 -1
- package/dist/services/telemetry/TelemetryService.js +39 -7
- package/dist/workspace/WorkspaceManager.d.ts +26 -0
- package/dist/workspace/WorkspaceManager.d.ts.map +1 -1
- package/dist/workspace/WorkspaceManager.js +206 -32
- package/package.json +6 -5
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { promises as fs } from "node:fs";
|
|
2
|
+
import { loadGlossary } from "../Glossary.js";
|
|
3
|
+
const CONTRADICTION_GROUPS = [
|
|
4
|
+
{
|
|
5
|
+
id: "token_identity",
|
|
6
|
+
label: "Anonymous vs identified token terminology",
|
|
7
|
+
keys: ["anonymous_token", "identified_token"],
|
|
8
|
+
},
|
|
9
|
+
];
|
|
10
|
+
const SEVERITY_ALIAS = "medium";
|
|
11
|
+
const SEVERITY_CONTRADICTION = "blocker";
|
|
12
|
+
const escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
13
|
+
const buildPattern = (term) => new RegExp(`\\b${escapeRegExp(term)}\\b`, "i");
|
|
14
|
+
const buildStripper = (pattern) => new RegExp(pattern.source, "gi");
|
|
15
|
+
const isFenceLine = (line) => /^```|^~~~/.test(line.trim());
|
|
16
|
+
const isExampleHeading = (heading) => /example|sample/i.test(heading);
|
|
17
|
+
const buildPatterns = (glossary) => {
|
|
18
|
+
return glossary.entries.map((entry) => {
|
|
19
|
+
const canonical = buildPattern(entry.term);
|
|
20
|
+
const aliases = (entry.aliases ?? []).map((alias) => ({
|
|
21
|
+
alias,
|
|
22
|
+
pattern: buildPattern(alias),
|
|
23
|
+
}));
|
|
24
|
+
return {
|
|
25
|
+
key: entry.key,
|
|
26
|
+
term: entry.term,
|
|
27
|
+
canonical,
|
|
28
|
+
canonicalStripper: buildStripper(canonical),
|
|
29
|
+
aliases,
|
|
30
|
+
};
|
|
31
|
+
});
|
|
32
|
+
};
|
|
33
|
+
const buildAliasIssue = (input) => {
|
|
34
|
+
const message = `Non-canonical term "${input.alias}" detected; use "${input.canonical}".`;
|
|
35
|
+
return {
|
|
36
|
+
id: `gate-terminology-normalization-${input.record.kind}-${input.key}-${input.lineNumber}`,
|
|
37
|
+
gateId: "gate-terminology-normalization",
|
|
38
|
+
severity: SEVERITY_ALIAS,
|
|
39
|
+
category: "terminology",
|
|
40
|
+
artifact: input.record.kind,
|
|
41
|
+
message,
|
|
42
|
+
remediation: `Replace "${input.alias}" with the canonical term "${input.canonical}".`,
|
|
43
|
+
location: {
|
|
44
|
+
kind: "line_range",
|
|
45
|
+
path: input.record.path,
|
|
46
|
+
lineStart: input.lineNumber,
|
|
47
|
+
lineEnd: input.lineNumber,
|
|
48
|
+
excerpt: input.alias,
|
|
49
|
+
},
|
|
50
|
+
metadata: {
|
|
51
|
+
entryKey: input.key,
|
|
52
|
+
canonicalTerm: input.canonical,
|
|
53
|
+
alias: input.alias,
|
|
54
|
+
heading: input.heading,
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
const buildContradictionIssue = (input) => {
|
|
59
|
+
const termList = input.terms.join(" vs ");
|
|
60
|
+
return {
|
|
61
|
+
id: `gate-terminology-normalization-contradiction-${input.lineNumber}`,
|
|
62
|
+
gateId: "gate-terminology-normalization",
|
|
63
|
+
severity: SEVERITY_CONTRADICTION,
|
|
64
|
+
category: "terminology",
|
|
65
|
+
artifact: input.record.kind,
|
|
66
|
+
message: `Conflicting canonical terminology detected (${termList}).`,
|
|
67
|
+
remediation: `Select one canonical term or explicitly define how both apply. (${input.label})`,
|
|
68
|
+
location: {
|
|
69
|
+
kind: "line_range",
|
|
70
|
+
path: input.record.path,
|
|
71
|
+
lineStart: input.lineNumber,
|
|
72
|
+
lineEnd: input.lineNumber,
|
|
73
|
+
excerpt: termList,
|
|
74
|
+
},
|
|
75
|
+
metadata: {
|
|
76
|
+
issueType: "contradiction",
|
|
77
|
+
conflictKeys: input.keys,
|
|
78
|
+
conflictTerms: input.terms,
|
|
79
|
+
label: input.label,
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
const scanRecord = async (record, patterns) => {
|
|
84
|
+
const issues = [];
|
|
85
|
+
const canonicalHits = [];
|
|
86
|
+
const notes = [];
|
|
87
|
+
try {
|
|
88
|
+
const content = await fs.readFile(record.path, "utf8");
|
|
89
|
+
const lines = content.split(/\r?\n/);
|
|
90
|
+
let inFence = false;
|
|
91
|
+
let allowSection = false;
|
|
92
|
+
let currentHeading;
|
|
93
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
94
|
+
const line = lines[i] ?? "";
|
|
95
|
+
const trimmed = line.trim();
|
|
96
|
+
if (!trimmed)
|
|
97
|
+
continue;
|
|
98
|
+
if (isFenceLine(trimmed)) {
|
|
99
|
+
inFence = !inFence;
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
const headingMatch = trimmed.match(/^#{1,6}\s+(.*)$/);
|
|
103
|
+
if (headingMatch) {
|
|
104
|
+
currentHeading = headingMatch[1]?.trim() || undefined;
|
|
105
|
+
allowSection = currentHeading ? isExampleHeading(currentHeading) : false;
|
|
106
|
+
}
|
|
107
|
+
if (inFence || allowSection)
|
|
108
|
+
continue;
|
|
109
|
+
for (const pattern of patterns) {
|
|
110
|
+
if (pattern.canonical.test(line)) {
|
|
111
|
+
canonicalHits.push({
|
|
112
|
+
key: pattern.key,
|
|
113
|
+
term: pattern.term,
|
|
114
|
+
record,
|
|
115
|
+
lineNumber: i + 1,
|
|
116
|
+
heading: currentHeading,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
if (pattern.aliases.length === 0)
|
|
120
|
+
continue;
|
|
121
|
+
const lineWithoutCanonical = line.replace(pattern.canonicalStripper, "");
|
|
122
|
+
for (const alias of pattern.aliases) {
|
|
123
|
+
if (!alias.pattern.test(lineWithoutCanonical))
|
|
124
|
+
continue;
|
|
125
|
+
issues.push(buildAliasIssue({
|
|
126
|
+
record,
|
|
127
|
+
lineNumber: i + 1,
|
|
128
|
+
heading: currentHeading,
|
|
129
|
+
alias: alias.alias,
|
|
130
|
+
canonical: pattern.term,
|
|
131
|
+
key: pattern.key,
|
|
132
|
+
}));
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
notes.push(`Unable to scan ${record.path}: ${error.message ?? String(error)}`);
|
|
139
|
+
}
|
|
140
|
+
return { issues, canonicalHits, notes };
|
|
141
|
+
};
|
|
142
|
+
const groupCanonicalHits = (hits) => {
|
|
143
|
+
const grouped = new Map();
|
|
144
|
+
for (const hit of hits) {
|
|
145
|
+
const existing = grouped.get(hit.key);
|
|
146
|
+
if (existing) {
|
|
147
|
+
existing.push(hit);
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
grouped.set(hit.key, [hit]);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return grouped;
|
|
154
|
+
};
|
|
155
|
+
export const runTerminologyNormalizationGate = async (input) => {
|
|
156
|
+
const glossary = input.glossary ?? loadGlossary();
|
|
157
|
+
if (!glossary.entries.length) {
|
|
158
|
+
return {
|
|
159
|
+
gateId: "gate-terminology-normalization",
|
|
160
|
+
gateName: "Terminology Normalization",
|
|
161
|
+
status: "skipped",
|
|
162
|
+
issues: [],
|
|
163
|
+
notes: ["Glossary is empty; terminology normalization skipped."],
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
const patterns = buildPatterns(glossary);
|
|
167
|
+
const records = [input.artifacts.pdr, input.artifacts.sds].filter((record) => Boolean(record));
|
|
168
|
+
if (records.length === 0) {
|
|
169
|
+
return {
|
|
170
|
+
gateId: "gate-terminology-normalization",
|
|
171
|
+
gateName: "Terminology Normalization",
|
|
172
|
+
status: "skipped",
|
|
173
|
+
issues: [],
|
|
174
|
+
notes: ["No PDR/SDS artifacts available for terminology checks."],
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
const issues = [];
|
|
178
|
+
const notes = [];
|
|
179
|
+
const canonicalHits = [];
|
|
180
|
+
for (const record of records) {
|
|
181
|
+
const result = await scanRecord(record, patterns);
|
|
182
|
+
issues.push(...result.issues);
|
|
183
|
+
canonicalHits.push(...result.canonicalHits);
|
|
184
|
+
notes.push(...result.notes);
|
|
185
|
+
}
|
|
186
|
+
const groupedHits = groupCanonicalHits(canonicalHits);
|
|
187
|
+
for (const group of CONTRADICTION_GROUPS) {
|
|
188
|
+
const hits = group.keys
|
|
189
|
+
.map((key) => groupedHits.get(key) ?? [])
|
|
190
|
+
.filter((entry) => entry.length > 0);
|
|
191
|
+
if (hits.length < 2)
|
|
192
|
+
continue;
|
|
193
|
+
const flattened = hits.flat();
|
|
194
|
+
const first = flattened[0];
|
|
195
|
+
const terms = hits.map((entry) => entry[0]?.term).filter(Boolean);
|
|
196
|
+
issues.push(buildContradictionIssue({
|
|
197
|
+
record: first.record,
|
|
198
|
+
lineNumber: first.lineNumber,
|
|
199
|
+
terms,
|
|
200
|
+
keys: group.keys,
|
|
201
|
+
label: group.label,
|
|
202
|
+
}));
|
|
203
|
+
}
|
|
204
|
+
const hasContradiction = issues.some((issue) => issue.severity === SEVERITY_CONTRADICTION);
|
|
205
|
+
const status = hasContradiction ? "fail" : issues.length > 0 ? "warn" : "pass";
|
|
206
|
+
return {
|
|
207
|
+
gateId: "gate-terminology-normalization",
|
|
208
|
+
gateName: "Terminology Normalization",
|
|
209
|
+
status,
|
|
210
|
+
issues,
|
|
211
|
+
notes: notes.length > 0 ? notes : undefined,
|
|
212
|
+
metadata: {
|
|
213
|
+
glossaryEntries: glossary.entries.length,
|
|
214
|
+
contradictionGroups: CONTRADICTION_GROUPS.length,
|
|
215
|
+
},
|
|
216
|
+
};
|
|
217
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"entries": [
|
|
4
|
+
{
|
|
5
|
+
"key": "consent_token",
|
|
6
|
+
"term": "consent token",
|
|
7
|
+
"description": "Install-time token issued to capture user consent, including TTL and revoke behavior.",
|
|
8
|
+
"aliases": ["consent id", "consent key", "consent handle"]
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"key": "anonymous_token",
|
|
12
|
+
"term": "anonymous token",
|
|
13
|
+
"description": "Token that represents a user without storing direct identifiers.",
|
|
14
|
+
"aliases": ["anon token", "anonymous identifier"]
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"key": "identified_token",
|
|
18
|
+
"term": "identified token",
|
|
19
|
+
"description": "Token tied to a known identity with explicit consent.",
|
|
20
|
+
"aliases": ["non-anonymous token", "identified id"]
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"key": "policy_owner",
|
|
24
|
+
"term": "policy owner",
|
|
25
|
+
"description": "Role responsible for approving policy and cache rule changes.",
|
|
26
|
+
"aliases": ["policy maintainer", "policy steward"]
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"key": "data_pipeline",
|
|
30
|
+
"term": "data pipeline",
|
|
31
|
+
"description": "End-to-end flow for ingestion, normalization, and storage.",
|
|
32
|
+
"aliases": ["pipeline", "processing pipeline"]
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"key": "telemetry_metering",
|
|
36
|
+
"term": "telemetry metering",
|
|
37
|
+
"description": "Explicit rules for event capture limits, sampling, and rate enforcement.",
|
|
38
|
+
"aliases": ["usage metering", "event metering"]
|
|
39
|
+
}
|
|
40
|
+
],
|
|
41
|
+
"canonicalPhrases": {
|
|
42
|
+
"consent_flow": "install-time consent token issuance and revoke flow",
|
|
43
|
+
"anonymity": "anonymous vs identified token handling",
|
|
44
|
+
"pipeline": "data pipeline with ownership for normalization rules",
|
|
45
|
+
"telemetry": "telemetry metering and sampling policy"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -4,6 +4,8 @@ export declare class EstimateService {
|
|
|
4
4
|
private workspace;
|
|
5
5
|
private constructor();
|
|
6
6
|
static create(workspace: WorkspaceResolution): Promise<EstimateService>;
|
|
7
|
+
private safeDivide;
|
|
8
|
+
private computeElapsedLaneHours;
|
|
7
9
|
private computeDurations;
|
|
8
10
|
private computeEtas;
|
|
9
11
|
estimate(options: Omit<EstimateOptions, "workspace">): Promise<EstimateResult>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EstimateService.d.ts","sourceRoot":"","sources":["../../../src/services/estimate/EstimateService.ts"],"names":[],"mappings":"
|
|
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+Bb,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,eAAe,EAAE,WAAW,CAAC,GAAG,OAAO,CAAC,cAAc,CAAC;IAwD9E,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAG7B"}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { Connection } from "@mcoda/db";
|
|
2
|
+
import { PathHelper } from "@mcoda/shared";
|
|
1
3
|
import { BacklogService } from "../backlog/BacklogService.js";
|
|
2
4
|
import { VelocityService } from "./VelocityService.js";
|
|
3
5
|
const HOURS_IN_MS = 3600 * 1000;
|
|
@@ -8,18 +10,55 @@ export class EstimateService {
|
|
|
8
10
|
static async create(workspace) {
|
|
9
11
|
return new EstimateService(workspace);
|
|
10
12
|
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
|
|
13
|
+
safeDivide(sp, spPerHour) {
|
|
14
|
+
if (!sp || sp <= 0)
|
|
15
|
+
return 0;
|
|
16
|
+
if (!spPerHour || spPerHour <= 0)
|
|
17
|
+
return null;
|
|
18
|
+
return sp / spPerHour;
|
|
19
|
+
}
|
|
20
|
+
async computeElapsedLaneHours(taskIds, status) {
|
|
21
|
+
if (taskIds.length === 0)
|
|
22
|
+
return 0;
|
|
23
|
+
const dbPath = PathHelper.getWorkspaceDbPath(this.workspace.workspaceRoot);
|
|
24
|
+
let connection;
|
|
25
|
+
try {
|
|
26
|
+
connection = await Connection.open(dbPath);
|
|
27
|
+
const placeholders = taskIds.map(() => "?").join(", ");
|
|
28
|
+
const rows = await connection.db.all(`SELECT task_id, MAX(timestamp) as started_at
|
|
29
|
+
FROM task_status_events
|
|
30
|
+
WHERE to_status = ?
|
|
31
|
+
AND task_id IN (${placeholders})
|
|
32
|
+
GROUP BY task_id`, status, ...taskIds);
|
|
33
|
+
const now = Date.now();
|
|
34
|
+
let totalMs = 0;
|
|
35
|
+
for (const row of rows) {
|
|
36
|
+
if (!row.started_at)
|
|
37
|
+
continue;
|
|
38
|
+
const startMs = Date.parse(row.started_at);
|
|
39
|
+
if (!Number.isNaN(startMs) && startMs <= now) {
|
|
40
|
+
totalMs += now - startMs;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return totalMs / HOURS_IN_MS;
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return 0;
|
|
47
|
+
}
|
|
48
|
+
finally {
|
|
49
|
+
if (connection) {
|
|
50
|
+
await connection.close();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
computeDurations(totals, velocity, elapsedImplementationHours) {
|
|
55
|
+
const implementationRaw = this.safeDivide(totals.implementation.story_points, velocity.implementationSpPerHour);
|
|
56
|
+
const implementationHours = implementationRaw === null ? null : Math.max((implementationRaw ?? 0) - elapsedImplementationHours, 0);
|
|
57
|
+
const reviewHours = this.safeDivide(totals.review.story_points, velocity.reviewSpPerHour);
|
|
58
|
+
const qaHours = this.safeDivide(totals.qa.story_points, velocity.qaSpPerHour);
|
|
59
|
+
const reviewPipelineHours = this.safeDivide(totals.implementation.story_points + totals.review.story_points, velocity.reviewSpPerHour);
|
|
60
|
+
const qaPipelineHours = this.safeDivide(totals.implementation.story_points + totals.review.story_points + totals.qa.story_points, velocity.qaSpPerHour);
|
|
61
|
+
const durationsList = [implementationHours, reviewPipelineHours, qaPipelineHours];
|
|
23
62
|
const hasNull = durationsList.some((value) => value === null);
|
|
24
63
|
const numeric = durationsList.filter((value) => value !== null);
|
|
25
64
|
const totalHours = hasNull || numeric.length === 0 ? null : Math.max(...numeric);
|
|
@@ -30,18 +69,22 @@ export class EstimateService {
|
|
|
30
69
|
totalHours,
|
|
31
70
|
};
|
|
32
71
|
}
|
|
33
|
-
computeEtas(durations) {
|
|
72
|
+
computeEtas(totals, velocity, durations) {
|
|
34
73
|
const now = Date.now();
|
|
35
74
|
const addHours = (hours) => {
|
|
36
75
|
if (hours === null || hours === undefined || hours < 0)
|
|
37
76
|
return undefined;
|
|
38
77
|
return new Date(now + hours * HOURS_IN_MS).toISOString();
|
|
39
78
|
};
|
|
79
|
+
const reviewPipelineHours = this.safeDivide(totals.implementation.story_points + totals.review.story_points, velocity.reviewSpPerHour);
|
|
80
|
+
const qaPipelineHours = this.safeDivide(totals.implementation.story_points + totals.review.story_points + totals.qa.story_points, velocity.qaSpPerHour);
|
|
40
81
|
const readyToReviewEta = durations.implementationHours !== null ? addHours(durations.implementationHours ?? undefined) : undefined;
|
|
41
|
-
const readyToQaEta = durations.implementationHours !== null &&
|
|
42
|
-
? addHours(Math.max(durations.implementationHours ?? 0,
|
|
82
|
+
const readyToQaEta = durations.implementationHours !== null && reviewPipelineHours !== null
|
|
83
|
+
? addHours(Math.max(durations.implementationHours ?? 0, reviewPipelineHours ?? 0))
|
|
84
|
+
: undefined;
|
|
85
|
+
const completeEta = durations.implementationHours !== null && reviewPipelineHours !== null && qaPipelineHours !== null
|
|
86
|
+
? addHours(Math.max(durations.implementationHours ?? 0, reviewPipelineHours ?? 0, qaPipelineHours ?? 0))
|
|
43
87
|
: undefined;
|
|
44
|
-
const completeEta = durations.totalHours !== null ? addHours(durations.totalHours ?? undefined) : undefined;
|
|
45
88
|
return {
|
|
46
89
|
readyToReviewEta,
|
|
47
90
|
readyToQaEta,
|
|
@@ -51,6 +94,7 @@ export class EstimateService {
|
|
|
51
94
|
async estimate(options) {
|
|
52
95
|
const backlogService = await BacklogService.create(this.workspace);
|
|
53
96
|
let backlogTotals;
|
|
97
|
+
let backlogTasks = [];
|
|
54
98
|
try {
|
|
55
99
|
const { summary } = await backlogService.getBacklog({
|
|
56
100
|
projectKey: options.projectKey,
|
|
@@ -59,6 +103,7 @@ export class EstimateService {
|
|
|
59
103
|
assignee: options.assignee,
|
|
60
104
|
});
|
|
61
105
|
backlogTotals = summary.totals;
|
|
106
|
+
backlogTasks = summary.tasks.map((task) => ({ task_id: task.task_id, status: task.status }));
|
|
62
107
|
}
|
|
63
108
|
finally {
|
|
64
109
|
await backlogService.close();
|
|
@@ -74,6 +119,7 @@ export class EstimateService {
|
|
|
74
119
|
mode: options.mode,
|
|
75
120
|
windowTasks: options.windowTasks,
|
|
76
121
|
spPerHourAll: options.spPerHourAll,
|
|
122
|
+
spPerHourImplementation: options.spPerHourImplementation,
|
|
77
123
|
spPerHourReview: options.spPerHourReview,
|
|
78
124
|
spPerHourQa: options.spPerHourQa,
|
|
79
125
|
});
|
|
@@ -81,8 +127,10 @@ export class EstimateService {
|
|
|
81
127
|
finally {
|
|
82
128
|
await velocityService.close();
|
|
83
129
|
}
|
|
84
|
-
const
|
|
85
|
-
const
|
|
130
|
+
const inProgressTaskIds = backlogTasks.filter((task) => task.status === "in_progress").map((task) => task.task_id);
|
|
131
|
+
const elapsedImplementationHours = await this.computeElapsedLaneHours(inProgressTaskIds, "in_progress");
|
|
132
|
+
const durationsHours = this.computeDurations(backlogTotals, effectiveVelocity, elapsedImplementationHours);
|
|
133
|
+
const etas = this.computeEtas(backlogTotals, effectiveVelocity, durationsHours);
|
|
86
134
|
return {
|
|
87
135
|
scope: {
|
|
88
136
|
workspaceId: this.workspace.workspaceId,
|
|
@@ -12,6 +12,10 @@ export declare class VelocityService {
|
|
|
12
12
|
private resolveConfig;
|
|
13
13
|
private resolveScopeIds;
|
|
14
14
|
private buildTaskFilters;
|
|
15
|
+
private insertStatusEvent;
|
|
16
|
+
private maybeBackfillStatusEvents;
|
|
17
|
+
private computeLaneVelocityFromTaskRuns;
|
|
18
|
+
private computeLaneVelocityFromStatusEvents;
|
|
15
19
|
private computeLaneVelocity;
|
|
16
20
|
private deriveDurationSeconds;
|
|
17
21
|
getEffectiveVelocity(options?: VelocityOptions): Promise<EffectiveVelocity>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VelocityService.d.ts","sourceRoot":"","sources":["../../../src/services/estimate/VelocityService.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"VelocityService.d.ts","sourceRoot":"","sources":["../../../src/services/estimate/VelocityService.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAC1E,OAAO,KAAK,EAAE,iBAAiB,EAAkB,eAAe,EAAoB,MAAM,YAAY,CAAC;AAKvG,qBAAa,eAAe;IAExB,OAAO,CAAC,SAAS;IACjB,OAAO,CAAC,EAAE;IACV,OAAO,CAAC,UAAU;IAClB,OAAO,CAAC,cAAc,CAAC;IAJzB,OAAO;WAOM,MAAM,CAAC,SAAS,EAAE,mBAAmB,GAAG,OAAO,CAAC,eAAe,CAAC;IAOvE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;mBAIP,wBAAwB;IAiB7C,OAAO,CAAC,aAAa;YAuCP,eAAe;IAgC7B,OAAO,CAAC,gBAAgB;YAuBV,iBAAiB;YA2BjB,yBAAyB;YAsHzB,+BAA+B;YAmD/B,mCAAmC;YAoEnC,mBAAmB;IAqBjC,OAAO,CAAC,qBAAqB;IAQvB,oBAAoB,CAAC,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,iBAAiB,CAAC;CAmFtF"}
|