@mcoda/core 0.1.8 → 0.1.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (216) hide show
  1. package/CHANGELOG.md +3 -0
  2. package/README.md +2 -2
  3. package/dist/api/AgentsApi.d.ts +9 -1
  4. package/dist/api/AgentsApi.d.ts.map +1 -1
  5. package/dist/api/AgentsApi.js +201 -6
  6. package/dist/api/QaTasksApi.d.ts.map +1 -1
  7. package/dist/api/QaTasksApi.js +6 -0
  8. package/dist/api/TasksApi.d.ts.map +1 -1
  9. package/dist/api/TasksApi.js +1 -0
  10. package/dist/index.d.ts +4 -0
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +4 -0
  13. package/dist/prompts/PdrPrompts.d.ts.map +1 -1
  14. package/dist/prompts/PdrPrompts.js +9 -1
  15. package/dist/prompts/SdsPrompts.d.ts.map +1 -1
  16. package/dist/prompts/SdsPrompts.js +9 -0
  17. package/dist/services/agents/AgentRatingFormula.d.ts +27 -0
  18. package/dist/services/agents/AgentRatingFormula.d.ts.map +1 -0
  19. package/dist/services/agents/AgentRatingFormula.js +45 -0
  20. package/dist/services/agents/AgentRatingService.d.ts +60 -0
  21. package/dist/services/agents/AgentRatingService.d.ts.map +1 -0
  22. package/dist/services/agents/AgentRatingService.js +363 -0
  23. package/dist/services/agents/GatewayAgentService.d.ts +11 -0
  24. package/dist/services/agents/GatewayAgentService.d.ts.map +1 -1
  25. package/dist/services/agents/GatewayAgentService.js +525 -84
  26. package/dist/services/agents/GatewayHandoff.d.ts +11 -0
  27. package/dist/services/agents/GatewayHandoff.d.ts.map +1 -0
  28. package/dist/services/agents/GatewayHandoff.js +141 -0
  29. package/dist/services/agents/RoutingService.d.ts +1 -0
  30. package/dist/services/agents/RoutingService.d.ts.map +1 -1
  31. package/dist/services/agents/RoutingService.js +4 -4
  32. package/dist/services/backlog/BacklogService.d.ts +23 -0
  33. package/dist/services/backlog/BacklogService.d.ts.map +1 -1
  34. package/dist/services/backlog/BacklogService.js +62 -7
  35. package/dist/services/backlog/TaskOrderingHeuristics.d.ts +12 -0
  36. package/dist/services/backlog/TaskOrderingHeuristics.d.ts.map +1 -0
  37. package/dist/services/backlog/TaskOrderingHeuristics.js +56 -0
  38. package/dist/services/backlog/TaskOrderingService.d.ts +17 -4
  39. package/dist/services/backlog/TaskOrderingService.d.ts.map +1 -1
  40. package/dist/services/backlog/TaskOrderingService.js +538 -79
  41. package/dist/services/docs/DocInventory.d.ts +11 -0
  42. package/dist/services/docs/DocInventory.d.ts.map +1 -0
  43. package/dist/services/docs/DocInventory.js +230 -0
  44. package/dist/services/docs/DocgenRunContext.d.ts +59 -0
  45. package/dist/services/docs/DocgenRunContext.d.ts.map +1 -0
  46. package/dist/services/docs/DocgenRunContext.js +4 -0
  47. package/dist/services/docs/DocsService.d.ts +70 -3
  48. package/dist/services/docs/DocsService.d.ts.map +1 -1
  49. package/dist/services/docs/DocsService.js +1930 -89
  50. package/dist/services/docs/alignment/DocAlignmentGraph.d.ts +23 -0
  51. package/dist/services/docs/alignment/DocAlignmentGraph.d.ts.map +1 -0
  52. package/dist/services/docs/alignment/DocAlignmentGraph.js +78 -0
  53. package/dist/services/docs/alignment/DocAlignmentPatcher.d.ts +19 -0
  54. package/dist/services/docs/alignment/DocAlignmentPatcher.d.ts.map +1 -0
  55. package/dist/services/docs/alignment/DocAlignmentPatcher.js +222 -0
  56. package/dist/services/docs/patch/DocPatchEngine.d.ts +57 -0
  57. package/dist/services/docs/patch/DocPatchEngine.d.ts.map +1 -0
  58. package/dist/services/docs/patch/DocPatchEngine.js +331 -0
  59. package/dist/services/docs/review/Glossary.d.ts +16 -0
  60. package/dist/services/docs/review/Glossary.d.ts.map +1 -0
  61. package/dist/services/docs/review/Glossary.js +47 -0
  62. package/dist/services/docs/review/ReviewReportRenderer.d.ts +3 -0
  63. package/dist/services/docs/review/ReviewReportRenderer.d.ts.map +1 -0
  64. package/dist/services/docs/review/ReviewReportRenderer.js +133 -0
  65. package/dist/services/docs/review/ReviewReportSchema.d.ts +39 -0
  66. package/dist/services/docs/review/ReviewReportSchema.d.ts.map +1 -0
  67. package/dist/services/docs/review/ReviewReportSchema.js +47 -0
  68. package/dist/services/docs/review/ReviewTypes.d.ts +76 -0
  69. package/dist/services/docs/review/ReviewTypes.d.ts.map +1 -0
  70. package/dist/services/docs/review/ReviewTypes.js +94 -0
  71. package/dist/services/docs/review/gates/AdminOpenApiSpecGate.d.ts +7 -0
  72. package/dist/services/docs/review/gates/AdminOpenApiSpecGate.d.ts.map +1 -0
  73. package/dist/services/docs/review/gates/AdminOpenApiSpecGate.js +93 -0
  74. package/dist/services/docs/review/gates/ApiPathConsistencyGate.d.ts +7 -0
  75. package/dist/services/docs/review/gates/ApiPathConsistencyGate.d.ts.map +1 -0
  76. package/dist/services/docs/review/gates/ApiPathConsistencyGate.js +308 -0
  77. package/dist/services/docs/review/gates/BuildReadyCompletenessGate.d.ts +8 -0
  78. package/dist/services/docs/review/gates/BuildReadyCompletenessGate.d.ts.map +1 -0
  79. package/dist/services/docs/review/gates/BuildReadyCompletenessGate.js +278 -0
  80. package/dist/services/docs/review/gates/DeploymentBlueprintGate.d.ts +8 -0
  81. package/dist/services/docs/review/gates/DeploymentBlueprintGate.d.ts.map +1 -0
  82. package/dist/services/docs/review/gates/DeploymentBlueprintGate.js +487 -0
  83. package/dist/services/docs/review/gates/NoMaybesGate.d.ts +8 -0
  84. package/dist/services/docs/review/gates/NoMaybesGate.d.ts.map +1 -0
  85. package/dist/services/docs/review/gates/NoMaybesGate.js +145 -0
  86. package/dist/services/docs/review/gates/OpenApiCoverageGate.d.ts +7 -0
  87. package/dist/services/docs/review/gates/OpenApiCoverageGate.d.ts.map +1 -0
  88. package/dist/services/docs/review/gates/OpenApiCoverageGate.js +266 -0
  89. package/dist/services/docs/review/gates/OpenApiSchemaSanityGate.d.ts +7 -0
  90. package/dist/services/docs/review/gates/OpenApiSchemaSanityGate.d.ts.map +1 -0
  91. package/dist/services/docs/review/gates/OpenApiSchemaSanityGate.js +59 -0
  92. package/dist/services/docs/review/gates/OpenQuestionsGate.d.ts +7 -0
  93. package/dist/services/docs/review/gates/OpenQuestionsGate.d.ts.map +1 -0
  94. package/dist/services/docs/review/gates/OpenQuestionsGate.js +200 -0
  95. package/dist/services/docs/review/gates/PdrInterfacesGate.d.ts +7 -0
  96. package/dist/services/docs/review/gates/PdrInterfacesGate.d.ts.map +1 -0
  97. package/dist/services/docs/review/gates/PdrInterfacesGate.js +159 -0
  98. package/dist/services/docs/review/gates/PdrOpenQuestionsGate.d.ts +8 -0
  99. package/dist/services/docs/review/gates/PdrOpenQuestionsGate.d.ts.map +1 -0
  100. package/dist/services/docs/review/gates/PdrOpenQuestionsGate.js +129 -0
  101. package/dist/services/docs/review/gates/PdrOwnershipGate.d.ts +7 -0
  102. package/dist/services/docs/review/gates/PdrOwnershipGate.d.ts.map +1 -0
  103. package/dist/services/docs/review/gates/PdrOwnershipGate.js +169 -0
  104. package/dist/services/docs/review/gates/PlaceholderArtifactGate.d.ts +10 -0
  105. package/dist/services/docs/review/gates/PlaceholderArtifactGate.d.ts.map +1 -0
  106. package/dist/services/docs/review/gates/PlaceholderArtifactGate.js +261 -0
  107. package/dist/services/docs/review/gates/RfpConsentGate.d.ts +6 -0
  108. package/dist/services/docs/review/gates/RfpConsentGate.d.ts.map +1 -0
  109. package/dist/services/docs/review/gates/RfpConsentGate.js +127 -0
  110. package/dist/services/docs/review/gates/RfpDefinitionGate.d.ts +7 -0
  111. package/dist/services/docs/review/gates/RfpDefinitionGate.d.ts.map +1 -0
  112. package/dist/services/docs/review/gates/RfpDefinitionGate.js +173 -0
  113. package/dist/services/docs/review/gates/SdsAdaptersGate.d.ts +7 -0
  114. package/dist/services/docs/review/gates/SdsAdaptersGate.d.ts.map +1 -0
  115. package/dist/services/docs/review/gates/SdsAdaptersGate.js +196 -0
  116. package/dist/services/docs/review/gates/SdsDecisionsGate.d.ts +7 -0
  117. package/dist/services/docs/review/gates/SdsDecisionsGate.d.ts.map +1 -0
  118. package/dist/services/docs/review/gates/SdsDecisionsGate.js +89 -0
  119. package/dist/services/docs/review/gates/SdsOpsGate.d.ts +7 -0
  120. package/dist/services/docs/review/gates/SdsOpsGate.d.ts.map +1 -0
  121. package/dist/services/docs/review/gates/SdsOpsGate.js +162 -0
  122. package/dist/services/docs/review/gates/SdsPolicyTelemetryGate.d.ts +7 -0
  123. package/dist/services/docs/review/gates/SdsPolicyTelemetryGate.d.ts.map +1 -0
  124. package/dist/services/docs/review/gates/SdsPolicyTelemetryGate.js +166 -0
  125. package/dist/services/docs/review/gates/SqlRequiredTablesGate.d.ts +7 -0
  126. package/dist/services/docs/review/gates/SqlRequiredTablesGate.d.ts.map +1 -0
  127. package/dist/services/docs/review/gates/SqlRequiredTablesGate.js +273 -0
  128. package/dist/services/docs/review/gates/SqlSyntaxGate.d.ts +7 -0
  129. package/dist/services/docs/review/gates/SqlSyntaxGate.d.ts.map +1 -0
  130. package/dist/services/docs/review/gates/SqlSyntaxGate.js +203 -0
  131. package/dist/services/docs/review/gates/TerminologyNormalizationGate.d.ts +9 -0
  132. package/dist/services/docs/review/gates/TerminologyNormalizationGate.d.ts.map +1 -0
  133. package/dist/services/docs/review/gates/TerminologyNormalizationGate.js +217 -0
  134. package/dist/services/docs/review/glossary.json +47 -0
  135. package/dist/services/estimate/EstimateService.d.ts +2 -0
  136. package/dist/services/estimate/EstimateService.d.ts.map +1 -1
  137. package/dist/services/estimate/EstimateService.js +66 -18
  138. package/dist/services/estimate/VelocityService.d.ts +4 -0
  139. package/dist/services/estimate/VelocityService.d.ts.map +1 -1
  140. package/dist/services/estimate/VelocityService.js +179 -36
  141. package/dist/services/estimate/types.d.ts +1 -0
  142. package/dist/services/estimate/types.d.ts.map +1 -1
  143. package/dist/services/execution/GatewayTrioService.d.ts +200 -0
  144. package/dist/services/execution/GatewayTrioService.d.ts.map +1 -0
  145. package/dist/services/execution/GatewayTrioService.js +2492 -0
  146. package/dist/services/execution/QaApiRunner.d.ts +30 -0
  147. package/dist/services/execution/QaApiRunner.d.ts.map +1 -0
  148. package/dist/services/execution/QaApiRunner.js +881 -0
  149. package/dist/services/execution/QaFollowupService.d.ts +2 -0
  150. package/dist/services/execution/QaFollowupService.d.ts.map +1 -1
  151. package/dist/services/execution/QaFollowupService.js +9 -2
  152. package/dist/services/execution/QaPlanValidator.d.ts +10 -0
  153. package/dist/services/execution/QaPlanValidator.d.ts.map +1 -0
  154. package/dist/services/execution/QaPlanValidator.js +128 -0
  155. package/dist/services/execution/QaProfileService.d.ts +27 -1
  156. package/dist/services/execution/QaProfileService.d.ts.map +1 -1
  157. package/dist/services/execution/QaProfileService.js +354 -7
  158. package/dist/services/execution/QaTasksService.d.ts +59 -1
  159. package/dist/services/execution/QaTasksService.d.ts.map +1 -1
  160. package/dist/services/execution/QaTasksService.js +3347 -318
  161. package/dist/services/execution/QaTestCommandBuilder.d.ts +51 -0
  162. package/dist/services/execution/QaTestCommandBuilder.d.ts.map +1 -0
  163. package/dist/services/execution/QaTestCommandBuilder.js +495 -0
  164. package/dist/services/execution/TaskSelectionService.d.ts +4 -2
  165. package/dist/services/execution/TaskSelectionService.d.ts.map +1 -1
  166. package/dist/services/execution/TaskSelectionService.js +144 -28
  167. package/dist/services/execution/TaskStateService.d.ts +19 -6
  168. package/dist/services/execution/TaskStateService.d.ts.map +1 -1
  169. package/dist/services/execution/TaskStateService.js +128 -13
  170. package/dist/services/execution/WorkOnTasksService.d.ts +32 -1
  171. package/dist/services/execution/WorkOnTasksService.d.ts.map +1 -1
  172. package/dist/services/execution/WorkOnTasksService.js +4667 -722
  173. package/dist/services/jobs/JobInsightsService.d.ts +4 -0
  174. package/dist/services/jobs/JobInsightsService.d.ts.map +1 -1
  175. package/dist/services/jobs/JobInsightsService.js +51 -5
  176. package/dist/services/jobs/JobResumeService.d.ts.map +1 -1
  177. package/dist/services/jobs/JobResumeService.js +23 -10
  178. package/dist/services/jobs/JobService.d.ts +56 -4
  179. package/dist/services/jobs/JobService.d.ts.map +1 -1
  180. package/dist/services/jobs/JobService.js +232 -1
  181. package/dist/services/openapi/OpenApiService.d.ts +51 -0
  182. package/dist/services/openapi/OpenApiService.d.ts.map +1 -1
  183. package/dist/services/openapi/OpenApiService.js +953 -106
  184. package/dist/services/planning/CreateTasksService.d.ts +21 -0
  185. package/dist/services/planning/CreateTasksService.d.ts.map +1 -1
  186. package/dist/services/planning/CreateTasksService.js +569 -31
  187. package/dist/services/planning/RefineTasksService.d.ts +9 -0
  188. package/dist/services/planning/RefineTasksService.d.ts.map +1 -1
  189. package/dist/services/planning/RefineTasksService.js +409 -59
  190. package/dist/services/review/CodeReviewService.d.ts +18 -0
  191. package/dist/services/review/CodeReviewService.d.ts.map +1 -1
  192. package/dist/services/review/CodeReviewService.js +1309 -167
  193. package/dist/services/review/ReviewNormalizer.d.ts +9 -0
  194. package/dist/services/review/ReviewNormalizer.d.ts.map +1 -0
  195. package/dist/services/review/ReviewNormalizer.js +147 -0
  196. package/dist/services/shared/AuthErrors.d.ts +3 -0
  197. package/dist/services/shared/AuthErrors.d.ts.map +1 -0
  198. package/dist/services/shared/AuthErrors.js +17 -0
  199. package/dist/services/shared/DocdexGuidance.d.ts +7 -0
  200. package/dist/services/shared/DocdexGuidance.d.ts.map +1 -0
  201. package/dist/services/shared/DocdexGuidance.js +12 -0
  202. package/dist/services/shared/ProjectGuidance.d.ts +17 -0
  203. package/dist/services/shared/ProjectGuidance.d.ts.map +1 -0
  204. package/dist/services/shared/ProjectGuidance.js +78 -0
  205. package/dist/services/system/ToolDenylist.d.ts +13 -0
  206. package/dist/services/system/ToolDenylist.d.ts.map +1 -0
  207. package/dist/services/system/ToolDenylist.js +85 -0
  208. package/dist/services/tasks/TaskCommentFormatter.d.ts +20 -0
  209. package/dist/services/tasks/TaskCommentFormatter.d.ts.map +1 -0
  210. package/dist/services/tasks/TaskCommentFormatter.js +54 -0
  211. package/dist/services/telemetry/TelemetryService.d.ts.map +1 -1
  212. package/dist/services/telemetry/TelemetryService.js +39 -7
  213. package/dist/workspace/WorkspaceManager.d.ts +26 -0
  214. package/dist/workspace/WorkspaceManager.d.ts.map +1 -1
  215. package/dist/workspace/WorkspaceManager.js +206 -32
  216. package/package.json +6 -5
@@ -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":"AAGA,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,gBAAgB;IAqBxB,OAAO,CAAC,WAAW;IAmBb,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,eAAe,EAAE,WAAW,CAAC,GAAG,OAAO,CAAC,cAAc,CAAC;IAmD9E,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAG7B"}
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
- computeDurations(totals, velocity) {
12
- const safeDivide = (sp, spPerHour) => {
13
- if (!sp || sp <= 0)
14
- return 0;
15
- if (!spPerHour || spPerHour <= 0)
16
- return null;
17
- return sp / spPerHour;
18
- };
19
- const implementationHours = safeDivide(totals.implementation.story_points, velocity.implementationSpPerHour);
20
- const reviewHours = safeDivide(totals.review.story_points, velocity.reviewSpPerHour);
21
- const qaHours = safeDivide(totals.qa.story_points, velocity.qaSpPerHour);
22
- const durationsList = [implementationHours, reviewHours, qaHours];
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 && durations.reviewHours !== null
42
- ? addHours(Math.max(durations.implementationHours ?? 0, durations.reviewHours ?? 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 durationsHours = this.computeDurations(backlogTotals, effectiveVelocity);
85
- const etas = this.computeEtas(durationsHours);
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":"AAIA,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;YAoCP,eAAe;IAgC7B,OAAO,CAAC,gBAAgB;YAuBV,mBAAmB;IAmEjC,OAAO,CAAC,qBAAqB;IAQvB,oBAAoB,CAAC,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,iBAAiB,CAAC;CA2DtF"}
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"}