@devtrack-solution/codesdd 1.2.3 → 1.2.4

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 (213) hide show
  1. package/.sdd/skills/curated/devtrack-api/SKILL.md +98 -12
  2. package/.sdd/skills/curated/devtrack-api/agents/claude-code.yaml +10 -0
  3. package/.sdd/skills/curated/devtrack-api/agents/codex.yaml +10 -0
  4. package/.sdd/skills/curated/devtrack-api/agents/cursor.yaml +10 -0
  5. package/.sdd/skills/curated/devtrack-api/agents/gemini.yaml +10 -0
  6. package/.sdd/skills/curated/devtrack-api/agents/kimi.yaml +10 -0
  7. package/.sdd/skills/curated/devtrack-api/agents/openai.yaml +5 -3
  8. package/.sdd/skills/curated/devtrack-api/agents/opencode.yaml +12 -0
  9. package/.sdd/skills/curated/devtrack-api/references/application-presentation.md +61 -5
  10. package/.sdd/skills/curated/devtrack-api/references/consumer-sync-policy.md +15 -3
  11. package/.sdd/skills/curated/devtrack-api/references/contract-pack.yaml +1951 -0
  12. package/.sdd/skills/curated/devtrack-api/references/domain-modeling.md +16 -14
  13. package/.sdd/skills/curated/devtrack-api/references/field-validation-protocol.md +40 -0
  14. package/.sdd/skills/curated/devtrack-api/references/foundation-layout.md +19 -2
  15. package/.sdd/skills/curated/devtrack-api/references/generated-artifact-invalidation.md +97 -0
  16. package/.sdd/skills/curated/devtrack-api/references/implementation-checklist.md +30 -1
  17. package/.sdd/skills/curated/devtrack-api/references/portable-agent-contract.md +42 -0
  18. package/.sdd/skills/curated/devtrack-api/references/testing-validation.md +22 -1
  19. package/.sdd/skills/curated/devtrack-api/references/typeorm-infrastructure.md +9 -7
  20. package/README.md +280 -29
  21. package/dist/applications/sdd/index.d.ts +16 -0
  22. package/dist/applications/sdd/index.js +16 -0
  23. package/dist/cli/program.js +180 -11
  24. package/dist/commands/config.js +197 -10
  25. package/dist/commands/sdd/execution.js +408 -16
  26. package/dist/commands/sdd/plugin.js +5 -0
  27. package/dist/commands/sdd/shared.d.ts +1 -0
  28. package/dist/commands/sdd/shared.js +10 -0
  29. package/dist/commands/sdd.js +157 -7
  30. package/dist/core/cli/command-matrix.d.ts +18 -0
  31. package/dist/core/cli/command-matrix.js +157 -0
  32. package/dist/core/cli-command-quality.js +11 -0
  33. package/dist/core/completions/command-registry.js +45 -0
  34. package/dist/core/config-schema.d.ts +31 -1
  35. package/dist/core/config-schema.js +79 -5
  36. package/dist/core/config.d.ts +1 -0
  37. package/dist/core/config.js +11 -0
  38. package/dist/core/global-config.d.ts +29 -0
  39. package/dist/core/init.d.ts +2 -2
  40. package/dist/core/init.js +13 -14
  41. package/dist/core/sdd/agent-binding.d.ts +19 -19
  42. package/dist/core/sdd/agent-runtime-contract.d.ts +204 -0
  43. package/dist/core/sdd/agent-runtime-contract.js +200 -0
  44. package/dist/core/sdd/allocator-recovery.d.ts +14 -0
  45. package/dist/core/sdd/allocator-recovery.js +30 -0
  46. package/dist/core/sdd/allocator-security.d.ts +18 -0
  47. package/dist/core/sdd/allocator-security.js +36 -0
  48. package/dist/core/sdd/api-foundation-baseline.d.ts +111 -0
  49. package/dist/core/sdd/api-foundation-baseline.js +151 -0
  50. package/dist/core/sdd/api-foundation-parity.d.ts +114 -0
  51. package/dist/core/sdd/api-foundation-parity.js +131 -0
  52. package/dist/core/sdd/api-profile-catalog.d.ts +36 -0
  53. package/dist/core/sdd/api-profile-catalog.js +132 -0
  54. package/dist/core/sdd/api-profile-dry-run-projection.d.ts +93 -0
  55. package/dist/core/sdd/api-profile-dry-run-projection.js +370 -0
  56. package/dist/core/sdd/api-profile-recipes.d.ts +82 -0
  57. package/dist/core/sdd/api-profile-recipes.js +484 -0
  58. package/dist/core/sdd/artifact-id-allocator.d.ts +368 -0
  59. package/dist/core/sdd/artifact-id-allocator.js +510 -0
  60. package/dist/core/sdd/check.d.ts +52 -1
  61. package/dist/core/sdd/check.js +326 -11
  62. package/dist/core/sdd/coordination/coordination-adapters.d.ts +15 -8
  63. package/dist/core/sdd/coordination/coordination-adapters.js +43 -15
  64. package/dist/core/sdd/coordination/index.d.ts +1 -0
  65. package/dist/core/sdd/coordination/index.js +1 -0
  66. package/dist/core/sdd/coordination/redis-runtime.d.ts +131 -0
  67. package/dist/core/sdd/coordination/redis-runtime.js +698 -0
  68. package/dist/core/sdd/deepagent-contracts.d.ts +99 -5
  69. package/dist/core/sdd/deepagent-contracts.js +62 -0
  70. package/dist/core/sdd/deepagents/reversa-subagents.d.ts +3 -3
  71. package/dist/core/sdd/default-bootstrap-files.d.ts +2 -2
  72. package/dist/core/sdd/default-bootstrap-files.js +14 -10
  73. package/dist/core/sdd/default-skills.js +115 -9
  74. package/dist/core/sdd/devtrack-api-appliance.d.ts +42 -1
  75. package/dist/core/sdd/devtrack-api-appliance.js +159 -32
  76. package/dist/core/sdd/devtrack-api-architecture.d.ts +16 -0
  77. package/dist/core/sdd/devtrack-api-architecture.js +86 -0
  78. package/dist/core/sdd/docs-sync.js +24 -18
  79. package/dist/core/sdd/domain/capability-diff.d.ts +63 -0
  80. package/dist/core/sdd/domain/capability-diff.js +200 -0
  81. package/dist/core/sdd/domain/change-safety-guardrails.d.ts +74 -0
  82. package/dist/core/sdd/domain/change-safety-guardrails.js +333 -0
  83. package/dist/core/sdd/domain/semantic-intent-classifier.d.ts +29 -0
  84. package/dist/core/sdd/domain/semantic-intent-classifier.js +117 -0
  85. package/dist/core/sdd/enterprise-mutating-command-gate.d.ts +27 -0
  86. package/dist/core/sdd/enterprise-mutating-command-gate.js +104 -0
  87. package/dist/core/sdd/enterprise-provenance-gates.d.ts +20 -0
  88. package/dist/core/sdd/enterprise-provenance-gates.js +63 -0
  89. package/dist/core/sdd/enterprise-provisioning-policy.d.ts +26 -0
  90. package/dist/core/sdd/enterprise-provisioning-policy.js +104 -0
  91. package/dist/core/sdd/foundation-artifact-map-validator.d.ts +16 -0
  92. package/dist/core/sdd/foundation-artifact-map-validator.js +71 -0
  93. package/dist/core/sdd/foundation-layer-manifest.d.ts +24 -0
  94. package/dist/core/sdd/foundation-layer-manifest.js +117 -0
  95. package/dist/core/sdd/governance-schemas.d.ts +2 -2
  96. package/dist/core/sdd/governance-schemas.js +11 -2
  97. package/dist/core/sdd/intent-guard.d.ts +22 -0
  98. package/dist/core/sdd/intent-guard.js +67 -0
  99. package/dist/core/sdd/json-schema.js +13 -1
  100. package/dist/core/sdd/legacy-operations.js +169 -5
  101. package/dist/core/sdd/migrate-workspace.js +39 -0
  102. package/dist/core/sdd/package-security-gates.d.ts +21 -0
  103. package/dist/core/sdd/package-security-gates.js +121 -0
  104. package/dist/core/sdd/package-structure-gate.d.ts +85 -3
  105. package/dist/core/sdd/package-structure-gate.js +384 -11
  106. package/dist/core/sdd/parallel-feat-automation.d.ts +185 -7
  107. package/dist/core/sdd/parallel-feat-automation.js +212 -0
  108. package/dist/core/sdd/plugin-broker.d.ts +223 -4
  109. package/dist/core/sdd/plugin-broker.js +10 -0
  110. package/dist/core/sdd/plugin-cli.d.ts +30 -0
  111. package/dist/core/sdd/plugin-cli.js +70 -3
  112. package/dist/core/sdd/plugin-evidence.d.ts +73 -0
  113. package/dist/core/sdd/plugin-manifest.d.ts +69 -1
  114. package/dist/core/sdd/plugin-manifest.js +10 -0
  115. package/dist/core/sdd/plugin-policy-pack.d.ts +1 -1
  116. package/dist/core/sdd/plugin-policy.js +6 -1
  117. package/dist/core/sdd/plugin-registry.d.ts +138 -2
  118. package/dist/core/sdd/plugin-sdk-contract.d.ts +363 -0
  119. package/dist/core/sdd/plugin-sdk-contract.js +268 -0
  120. package/dist/core/sdd/plugin-skill-binding.d.ts +1 -1
  121. package/dist/core/sdd/quality-validation.d.ts +89 -16
  122. package/dist/core/sdd/release-readiness.d.ts +68 -0
  123. package/dist/core/sdd/release-readiness.js +767 -0
  124. package/dist/core/sdd/reversa-architecture-extractor.d.ts +13 -0
  125. package/dist/core/sdd/reversa-architecture-extractor.js +89 -0
  126. package/dist/core/sdd/reversa-artifact-writer.d.ts +18 -0
  127. package/dist/core/sdd/reversa-artifact-writer.js +40 -0
  128. package/dist/core/sdd/reversa-command-policy.d.ts +136 -0
  129. package/dist/core/sdd/reversa-command-policy.js +361 -0
  130. package/dist/core/sdd/reversa-data-extractor.d.ts +11 -0
  131. package/dist/core/sdd/reversa-data-extractor.js +73 -0
  132. package/dist/core/sdd/reversa-equivalence.d.ts +20 -0
  133. package/dist/core/sdd/reversa-equivalence.js +34 -0
  134. package/dist/core/sdd/reversa-evidence.d.ts +298 -0
  135. package/dist/core/sdd/reversa-evidence.js +118 -0
  136. package/dist/core/sdd/reversa-reconstruction.d.ts +29 -0
  137. package/dist/core/sdd/reversa-reconstruction.js +32 -0
  138. package/dist/core/sdd/reversa-rules-extractor.d.ts +12 -0
  139. package/dist/core/sdd/reversa-rules-extractor.js +86 -0
  140. package/dist/core/sdd/reversa-source-safety.d.ts +19 -0
  141. package/dist/core/sdd/reversa-source-safety.js +105 -0
  142. package/dist/core/sdd/reversa-surface-scout.d.ts +13 -0
  143. package/dist/core/sdd/reversa-surface-scout.js +85 -0
  144. package/dist/core/sdd/reversa-ux-mapper.d.ts +11 -0
  145. package/dist/core/sdd/reversa-ux-mapper.js +73 -0
  146. package/dist/core/sdd/runtime-boundary-contract.d.ts +45 -0
  147. package/dist/core/sdd/runtime-boundary-contract.js +90 -0
  148. package/dist/core/sdd/sdk-agent-plugin-quality-gates.d.ts +150 -0
  149. package/dist/core/sdd/sdk-agent-plugin-quality-gates.js +258 -0
  150. package/dist/core/sdd/services/agent-run.service.d.ts +38 -6
  151. package/dist/core/sdd/services/agent-run.service.js +73 -1
  152. package/dist/core/sdd/services/archive-quality-coherence.service.d.ts +17 -0
  153. package/dist/core/sdd/services/archive-quality-coherence.service.js +141 -0
  154. package/dist/core/sdd/services/capability-diff.service.d.ts +18 -0
  155. package/dist/core/sdd/services/capability-diff.service.js +26 -0
  156. package/dist/core/sdd/services/change-safety-preflight.service.d.ts +17 -0
  157. package/dist/core/sdd/services/change-safety-preflight.service.js +17 -0
  158. package/dist/core/sdd/services/context.service.d.ts +43 -340
  159. package/dist/core/sdd/services/context.service.js +323 -9
  160. package/dist/core/sdd/services/decide.service.js +1 -1
  161. package/dist/core/sdd/services/finalize.service.d.ts +27 -0
  162. package/dist/core/sdd/services/finalize.service.js +226 -18
  163. package/dist/core/sdd/services/frontend-impact.service.d.ts +1 -1
  164. package/dist/core/sdd/services/historical-quality-regression.service.d.ts +35 -0
  165. package/dist/core/sdd/services/historical-quality-regression.service.js +228 -0
  166. package/dist/core/sdd/services/ingest-deposito.service.js +1 -1
  167. package/dist/core/sdd/services/planning-execution-coherence.service.d.ts +45 -0
  168. package/dist/core/sdd/services/planning-execution-coherence.service.js +225 -0
  169. package/dist/core/sdd/services/semantic-intent-classifier.service.d.ts +6 -0
  170. package/dist/core/sdd/services/semantic-intent-classifier.service.js +7 -0
  171. package/dist/core/sdd/state.d.ts +1 -0
  172. package/dist/core/sdd/state.js +266 -34
  173. package/dist/core/sdd/store/sdd-stores.js +2 -2
  174. package/dist/core/sdd/structural-health.d.ts +13 -13
  175. package/dist/core/sdd/types.d.ts +30 -15
  176. package/dist/core/sdd/types.js +4 -0
  177. package/dist/core/sdd/views.js +17 -0
  178. package/dist/core/sdd/workspace-schemas.d.ts +428 -7
  179. package/dist/core/sdd/workspace-schemas.js +223 -70
  180. package/dist/core/shared/skill-generation.d.ts +2 -0
  181. package/dist/core/shared/skill-generation.js +19 -2
  182. package/dist/core/shared/tool-detection.d.ts +19 -0
  183. package/dist/core/shared/tool-detection.js +89 -0
  184. package/dist/domains/sdd/index.d.ts +6 -0
  185. package/dist/domains/sdd/index.js +6 -0
  186. package/dist/infrastructures/sdd/index.d.ts +7 -0
  187. package/dist/infrastructures/sdd/index.js +6 -0
  188. package/dist/presentations/cli/sdd/index.d.ts +3 -0
  189. package/dist/presentations/cli/sdd/index.js +3 -0
  190. package/dist/shared/sdd/index.d.ts +3 -0
  191. package/dist/shared/sdd/index.js +2 -0
  192. package/package.json +14 -10
  193. package/schemas/sdd/2-plan.schema.json +207 -2
  194. package/schemas/sdd/5-quality.schema.json +324 -25
  195. package/schemas/sdd/agent-runtime-command-plan.schema.json +212 -0
  196. package/schemas/sdd/agent-runtime-opencode-run-evidence.schema.json +270 -0
  197. package/schemas/sdd/codesdd-plugin.schema.json +171 -0
  198. package/schemas/sdd/deepagent-run-request.schema.json +316 -0
  199. package/schemas/sdd/parallel-feat-automation-plan.schema.json +89 -0
  200. package/schemas/sdd/parallel-feat-scheduler-request.schema.json +116 -0
  201. package/schemas/sdd/parallel-feat-scheduler-result.schema.json +404 -0
  202. package/schemas/sdd/plugin-artifact-manifest.schema.json +109 -0
  203. package/schemas/sdd/plugin-artifact-map.schema.json +223 -0
  204. package/schemas/sdd/plugin-evidence-manifest.schema.json +109 -0
  205. package/schemas/sdd/plugin-language-runtime.schema.json +103 -0
  206. package/schemas/sdd/plugin-package-governance.schema.json +74 -0
  207. package/schemas/sdd/plugin-registry.schema.json +171 -0
  208. package/schemas/sdd/plugin-runtime-invocation-plan.schema.json +109 -0
  209. package/schemas/sdd/quality-evidence-bundle.schema.json +109 -0
  210. package/schemas/sdd/reversa-evidence-bundle.schema.json +466 -0
  211. package/schemas/sdd/sdk-agent-plugin-quality-gate-input.schema.json +168 -0
  212. package/schemas/sdd/sdk-agent-plugin-quality-gate-report.schema.json +160 -0
  213. package/schemas/sdd/workspace-catalog.schema.json +5298 -1409
@@ -4,13 +4,14 @@ import path from 'node:path';
4
4
  import { parse as parseYaml } from 'yaml';
5
5
  import { CLI_NAME } from '../branding.js';
6
6
  import { ID_PATTERNS, } from './types.js';
7
- import { loadProjectSddConfig, loadStateSnapshot, resolveSddPaths, } from './state.js';
7
+ import { buildDefaultSkillRoutingState, loadProjectSddConfig, loadStateSnapshot, resolveSddPaths, } from './state.js';
8
8
  import { DEFAULT_CURATED_SKILL_CATALOG } from './default-skills.js';
9
9
  import { renderViews } from './views.js';
10
10
  import { syncSddGuideDocs, validateSddGuideDocs } from './docs-sync.js';
11
11
  import { evaluateWorkspaceTraceability } from './domain/traceability.js';
12
12
  import { detectArchiveArchivedLifecycleCollisions } from './domain/post-active-validation.js';
13
13
  import { parseWorkspaceYamlDocument, workspaceTasksSchema, } from './workspace-schemas.js';
14
+ import { scanArchiveQualityCoherence } from './services/archive-quality-coherence.service.js';
14
15
  export function checkUniqueIds(items, scope, errors) {
15
16
  const seen = new Set();
16
17
  for (const item of items) {
@@ -21,6 +22,7 @@ export function checkUniqueIds(items, scope, errors) {
21
22
  }
22
23
  }
23
24
  const FORBIDDEN_TITLE_PATTERNS = ['debate:', 'insight:', '(preencher', '(placeholder'];
25
+ const CLOSED_BACKLOG_STATUSES = new Set(['DONE', 'ARCHIVED']);
24
26
  export function hasForbiddenTitleToken(title) {
25
27
  const lowered = title.toLowerCase();
26
28
  for (const token of FORBIDDEN_TITLE_PATTERNS) {
@@ -29,6 +31,258 @@ export function hasForbiddenTitleToken(title) {
29
31
  }
30
32
  return null;
31
33
  }
34
+ export function summarizeFrontendGapHealth(gaps) {
35
+ let open = 0;
36
+ let planned = 0;
37
+ let inProgress = 0;
38
+ let resolved = 0;
39
+ let superseded = 0;
40
+ for (const gap of gaps) {
41
+ if (gap.status === 'OPEN')
42
+ open++;
43
+ if (gap.status === 'PLANNED')
44
+ planned++;
45
+ if (gap.status === 'IN_PROGRESS')
46
+ inProgress++;
47
+ if (gap.status === 'DONE')
48
+ resolved++;
49
+ if (gap.status === 'SUPERSEDED')
50
+ superseded++;
51
+ }
52
+ return {
53
+ total: gaps.length,
54
+ open,
55
+ planned,
56
+ in_progress: inProgress,
57
+ resolved,
58
+ superseded,
59
+ unresolved: gaps.length - resolved - superseded,
60
+ };
61
+ }
62
+ export function summarizeLockDomainHealth(items) {
63
+ const lockOwners = new Map();
64
+ for (const item of items) {
65
+ for (const lock of item.lock_domains) {
66
+ const owners = lockOwners.get(lock) || [];
67
+ owners.push(item);
68
+ lockOwners.set(lock, owners);
69
+ }
70
+ }
71
+ const shared = [];
72
+ for (const [lock, owners] of lockOwners.entries()) {
73
+ if (owners.length <= 1)
74
+ continue;
75
+ const activeOwners = owners.filter((owner) => !CLOSED_BACKLOG_STATUSES.has(owner.status));
76
+ const historicalOwners = owners.filter((owner) => CLOSED_BACKLOG_STATUSES.has(owner.status));
77
+ const severity = activeOwners.length > 1 ? 'active' : activeOwners.length === 1 ? 'mixed' : 'historical';
78
+ shared.push({
79
+ lock,
80
+ severity,
81
+ owners: owners.map((owner) => owner.id),
82
+ active_owners: activeOwners.map((owner) => owner.id),
83
+ historical_owners: historicalOwners.map((owner) => owner.id),
84
+ });
85
+ }
86
+ shared.sort((left, right) => left.lock.localeCompare(right.lock));
87
+ return {
88
+ shared_total: shared.length,
89
+ active_conflicts: shared.filter((item) => item.severity === 'active').length,
90
+ mixed_history: shared.filter((item) => item.severity === 'mixed').length,
91
+ historical: shared.filter((item) => item.severity === 'historical').length,
92
+ shared,
93
+ };
94
+ }
95
+ function normalizeCatalogPath(input) {
96
+ return input.trim().replace(/\\/g, '/').replace(/^\.\//, '').replace(/\/+$/, '');
97
+ }
98
+ export function hasRepoMapReference(repoPath, repoMapPaths) {
99
+ const normalizedRepoPath = normalizeCatalogPath(repoPath);
100
+ if (!normalizedRepoPath)
101
+ return false;
102
+ return repoMapPaths.some((repoMapPath) => {
103
+ const normalizedRepoMapPath = normalizeCatalogPath(repoMapPath);
104
+ if (!normalizedRepoMapPath)
105
+ return false;
106
+ return (normalizedRepoMapPath === normalizedRepoPath ||
107
+ normalizedRepoMapPath.startsWith(`${normalizedRepoPath}/`) ||
108
+ normalizedRepoPath.startsWith(`${normalizedRepoMapPath}/`));
109
+ });
110
+ }
111
+ export function hasIntegrationContract(contractRef, knownContracts) {
112
+ const normalizedContractRef = contractRef.trim();
113
+ if (!normalizedContractRef)
114
+ return false;
115
+ if (knownContracts.has(normalizedContractRef))
116
+ return true;
117
+ for (const knownContract of knownContracts) {
118
+ if (knownContract.startsWith(`${normalizedContractRef}::`)) {
119
+ return true;
120
+ }
121
+ }
122
+ return false;
123
+ }
124
+ export function auditServiceCatalogMaturity(services, backlogItems, repoMapItems, integrationContracts) {
125
+ const backlogIds = new Set(backlogItems.map((item) => item.id));
126
+ const repoMapPaths = repoMapItems.map((item) => item.path);
127
+ const contracts = new Set(integrationContracts.map((item) => item.trim()).filter(Boolean));
128
+ const byLevel = { 1: 0, 2: 0, 3: 0, 4: 0, 5: 0 };
129
+ const below5 = [];
130
+ for (const service of services) {
131
+ const unmet = [];
132
+ const hasIdentity = Boolean(service.id.trim()) && Boolean(service.name.trim()) && Boolean((service.responsibility || '').trim());
133
+ if (!hasIdentity)
134
+ unmet.push('identity');
135
+ const hasOwners = service.owner_refs.length > 0 && service.owner_refs.every((ownerRef) => backlogIds.has(ownerRef));
136
+ if (!hasOwners)
137
+ unmet.push('owner_refs');
138
+ const hasRepoPaths = service.repo_paths.length > 0 && service.repo_paths.every((repoPath) => hasRepoMapReference(repoPath, repoMapPaths));
139
+ if (!hasRepoPaths)
140
+ unmet.push('repo_paths');
141
+ const hasContracts = service.contracts.length > 0 &&
142
+ service.contracts.every((contractRef) => hasIntegrationContract(contractRef, contracts));
143
+ if (!hasContracts)
144
+ unmet.push('contracts');
145
+ const hasExternalDependencies = Array.isArray(service.external_dependencies);
146
+ if (!hasExternalDependencies)
147
+ unmet.push('external_dependencies');
148
+ const level = !hasIdentity ? 1 : !hasOwners ? 2 : !hasRepoPaths ? 3 : !hasContracts || !hasExternalDependencies ? 4 : 5;
149
+ byLevel[level] += 1;
150
+ if (level < 5) {
151
+ below5.push({
152
+ id: service.id,
153
+ level,
154
+ unmet,
155
+ });
156
+ }
157
+ }
158
+ return {
159
+ serviceCount: services.length,
160
+ byLevel,
161
+ below5,
162
+ };
163
+ }
164
+ export async function validateAdrConsistency(adrsDir, backlogItems, errors, warnings, isStrict) {
165
+ const backlogStatus = new Map();
166
+ for (const item of backlogItems) {
167
+ backlogStatus.set(item.id, item.status);
168
+ }
169
+ let filenames;
170
+ try {
171
+ filenames = await fs.readdir(adrsDir);
172
+ }
173
+ catch {
174
+ return;
175
+ }
176
+ const adrs = filenames.filter((f) => f.startsWith('ADR-') && f.endsWith('.md'));
177
+ for (const filename of adrs) {
178
+ let content;
179
+ try {
180
+ content = await fs.readFile(path.join(adrsDir, filename), 'utf-8');
181
+ }
182
+ catch {
183
+ continue;
184
+ }
185
+ const frontmatterMatch = /^---\r?\n([\s\S]*?)\r?\n---/.exec(content);
186
+ if (!frontmatterMatch)
187
+ continue;
188
+ let frontmatter;
189
+ try {
190
+ frontmatter = parseYaml(frontmatterMatch[1]) ?? {};
191
+ }
192
+ catch {
193
+ continue;
194
+ }
195
+ const adrStatus = typeof frontmatter.status === 'string' ? frontmatter.status : '';
196
+ const featureId = typeof frontmatter.feature_id === 'string' ? frontmatter.feature_id : '';
197
+ const adrId = typeof frontmatter.id === 'string' ? frontmatter.id : filename;
198
+ if (!featureId || !adrStatus)
199
+ continue;
200
+ const featureStatus = backlogStatus.get(featureId);
201
+ if (!featureStatus)
202
+ continue;
203
+ const featureDone = featureStatus === 'DONE' || featureStatus === 'ARCHIVED';
204
+ const adrUnresolved = adrStatus === 'IN_PROGRESS' || adrStatus === 'DRAFT';
205
+ if (featureDone && adrUnresolved) {
206
+ const message = `ADR consistency gate failed: ${adrId} status is ${adrStatus} but feature ${featureId} is ${featureStatus} in backlog`;
207
+ if (isStrict) {
208
+ errors.push(message);
209
+ }
210
+ else {
211
+ warnings.push(message);
212
+ }
213
+ }
214
+ }
215
+ }
216
+ export function validateInsightMinimums(insightTitle, insightBody, errors) {
217
+ if ((insightTitle || '').trim().length < 50) {
218
+ errors.push(`INSIGHT quality gate: title must be >=50 characters (current: ${(insightTitle || '').trim().length}). Expand the title to describe the problem and expected impact.`);
219
+ }
220
+ const bodyLower = (insightBody || '').toLowerCase();
221
+ const missingKeywords = [];
222
+ if (!/\bproblem|\bproblema|\bissue\b/.test(bodyLower))
223
+ missingKeywords.push('problem description');
224
+ if (!/\bscope|\bescopo|\bboundary\b/.test(bodyLower))
225
+ missingKeywords.push('scope definition');
226
+ if (!/\bimpact|\bimpacto|\boutcome\b/.test(bodyLower))
227
+ missingKeywords.push('expected impact');
228
+ if (missingKeywords.length > 0) {
229
+ errors.push(`INSIGHT quality gate: body must describe problem, scope, and impact. Missing: ${missingKeywords.join(', ')}.`);
230
+ }
231
+ }
232
+ export function validateEpicMinimums(epicBody, epicTitle, errors) {
233
+ const bodyClean = (epicBody || '').replace(/^---[\s\S]*?---\r?\n?/, '').trim();
234
+ if (bodyClean.length < 500) {
235
+ errors.push(`EPIC quality gate: expanded plan must be >=500 characters (current: ${bodyClean.length}). Add feature breakdown, dependency map, risk matrix, and DoD.`);
236
+ }
237
+ const featMatches = bodyClean.match(/FEAT-\d{4}/g) || [];
238
+ const uniqueFeats = new Set(featMatches).size;
239
+ if (uniqueFeats < 3) {
240
+ errors.push(`EPIC quality gate: must reference >=3 estimated features (current: ${uniqueFeats}). Expand the breakdown plan with concrete feature estimates.`);
241
+ }
242
+ const hasDependencyMap = /\bupstream\b|\bdownstream\b|\bdependencies\b|\bdependency map\b/i.test(bodyClean);
243
+ if (!hasDependencyMap) {
244
+ errors.push(`EPIC quality gate: must include a dependency map with upstream and downstream references.`);
245
+ }
246
+ }
247
+ export function validateTransitionLogConsistency(transitionLogEvents, backlogItems, errors, warnings, isStrict) {
248
+ if (transitionLogEvents.length === 0)
249
+ return;
250
+ const oldestEventTs = Math.min(...transitionLogEvents.map((e) => e.timestamp ? Date.parse(String(e.timestamp)) : Infinity));
251
+ const backlogStatus = new Map(backlogItems.map((item) => [item.id, item.status]));
252
+ const finalizeEvents = new Set();
253
+ for (const event of transitionLogEvents) {
254
+ const action = event.action || event.to || '';
255
+ if (action === 'finalized' || action === 'archived' || action === 'DONE' || action === 'ARCHIVED') {
256
+ finalizeEvents.add(event.entity_id);
257
+ }
258
+ }
259
+ for (const item of backlogItems) {
260
+ const isDone = item.status === 'DONE' || item.status === 'ARCHIVED';
261
+ if (!isDone)
262
+ continue;
263
+ if (finalizeEvents.has(item.id))
264
+ continue;
265
+ const itemDoneAt = item.done_at || item.archived_at || '';
266
+ if (itemDoneAt && oldestEventTs !== Infinity && Date.parse(itemDoneAt) < oldestEventTs) {
267
+ continue;
268
+ }
269
+ const message = `Transition-log consistency: ${item.id} is ${item.status} in backlog but has no finalized event in transition-log`;
270
+ warnings.push(message);
271
+ }
272
+ for (const eventId of finalizeEvents) {
273
+ const status = backlogStatus.get(eventId);
274
+ if (!status) {
275
+ warnings.push(`Transition-log consistency: transition-log has finalized event for ${eventId} but no matching backlog item`);
276
+ }
277
+ else if (status !== 'DONE' && status !== 'ARCHIVED') {
278
+ const message = `Transition-log consistency: ${eventId} has finalized event in transition-log but backlog status is ${status}`;
279
+ if (isStrict)
280
+ errors.push(message);
281
+ else
282
+ warnings.push(message);
283
+ }
284
+ }
285
+ }
32
286
  export function validateDiscoveryRecords(records, errors) {
33
287
  for (const record of records) {
34
288
  if (record.type === 'INS' && !ID_PATTERNS.insight.test(record.id)) {
@@ -53,7 +307,6 @@ export function validateDiscoveryRecords(records, errors) {
53
307
  }
54
308
  export function validateBacklog(items, errors, warnings) {
55
309
  const ids = new Set(items.map((item) => item.id));
56
- const lockOwners = new Map();
57
310
  for (const item of items) {
58
311
  const forbidden = hasForbiddenTitleToken(item.title);
59
312
  if (forbidden) {
@@ -67,15 +320,10 @@ export function validateBacklog(items, errors, warnings) {
67
320
  warnings.push(`Item de backlog ${item.id} bloqueado por referencia inexistente: ${dep}`);
68
321
  }
69
322
  }
70
- for (const lock of item.lock_domains) {
71
- const owners = lockOwners.get(lock) || [];
72
- owners.push(item.id);
73
- lockOwners.set(lock, owners);
74
- }
75
323
  }
76
- for (const [lock, owners] of lockOwners.entries()) {
77
- if (owners.length > 1) {
78
- warnings.push(`Lock domain "${lock}" compartilhado por: ${owners.join(', ')}`);
324
+ for (const sharedLock of summarizeLockDomainHealth(items).shared) {
325
+ if (sharedLock.severity === 'active') {
326
+ warnings.push(`Lock domain "${sharedLock.lock}" compartilhado por FEATs ativas: ${sharedLock.active_owners.join(', ')}`);
79
327
  }
80
328
  }
81
329
  }
@@ -122,6 +370,13 @@ export function validateReferentialIntegrity(snapshot, errors, warnings, isStric
122
370
  }
123
371
  }
124
372
  }
373
+ if (snapshot.finalizeQueue?.history) {
374
+ for (const fq of snapshot.finalizeQueue.history) {
375
+ if (!backlogIds.has(fq.feature_id)) {
376
+ record(`Finalize history has entry for feature_id="${fq.feature_id}" blocked by an unknown feature.`);
377
+ }
378
+ }
379
+ }
125
380
  if (snapshot.unblockEvents?.events) {
126
381
  for (const ev of snapshot.unblockEvents.events) {
127
382
  if (!backlogIds.has(ev.feature_id)) {
@@ -172,6 +427,26 @@ export function validateSkillOperationalIntegrity(snapshot, errors) {
172
427
  }
173
428
  }
174
429
  }
430
+ const currentRoutes = new Map((snapshot.skillRouting?.routes || []).map((route) => [route.domain, route]));
431
+ for (const defaultRoute of buildDefaultSkillRoutingState().routes) {
432
+ const currentRoute = currentRoutes.get(defaultRoute.domain);
433
+ if (!currentRoute) {
434
+ errors.push(`Default skill route "${defaultRoute.domain}" is missing from skill-routing.yaml`);
435
+ continue;
436
+ }
437
+ const currentSkills = currentRoute.skills || [];
438
+ const currentBundles = currentRoute.bundles || [];
439
+ for (const skillId of defaultRoute.skills) {
440
+ if (!currentSkills.includes(skillId)) {
441
+ errors.push(`Default skill route "${defaultRoute.domain}" is missing skill "${skillId}"`);
442
+ }
443
+ }
444
+ for (const bundleId of defaultRoute.bundles) {
445
+ if (!currentBundles.includes(bundleId)) {
446
+ errors.push(`Default skill route "${defaultRoute.domain}" is missing bundle "${bundleId}"`);
447
+ }
448
+ }
449
+ }
175
450
  }
176
451
  export function computeGraphSummary(items) {
177
452
  const byId = new Map(items.map((item) => [item.id, item]));
@@ -541,6 +816,8 @@ export class SddCheckCommand {
541
816
  backlog: 0,
542
817
  techDebt: 0,
543
818
  finalizeQueue: 0,
819
+ finalizeQueuePending: 0,
820
+ finalizeQueueDone: 0,
544
821
  frontendEnabled: false,
545
822
  frontendGaps: 0,
546
823
  frontendRoutes: 0,
@@ -618,6 +895,8 @@ export class SddCheckCommand {
618
895
  backlog: 0,
619
896
  techDebt: 0,
620
897
  finalizeQueue: 0,
898
+ finalizeQueuePending: 0,
899
+ finalizeQueueDone: 0,
621
900
  frontendEnabled: config.frontend.enabled,
622
901
  frontendGaps: 0,
623
902
  frontendRoutes: 0,
@@ -658,6 +937,8 @@ export class SddCheckCommand {
658
937
  backlog: 0,
659
938
  techDebt: 0,
660
939
  finalizeQueue: 0,
940
+ finalizeQueuePending: 0,
941
+ finalizeQueueDone: 0,
661
942
  frontendEnabled: config.frontend.enabled,
662
943
  frontendGaps: 0,
663
944
  frontendRoutes: 0,
@@ -706,6 +987,15 @@ export class SddCheckCommand {
706
987
  validatePrivacySourceRegistry(snapshot, warnings);
707
988
  await validatePrivacyWorkspaceChecklist(paths.activeDir, snapshot.backlog.items, warnings);
708
989
  await validateTraceabilityWorkspaceChecklist(paths.projectRoot, paths.activeDir, snapshot.backlog.items, warnings);
990
+ await validateAdrConsistency(path.join(paths.coreDir, 'adrs'), snapshot.backlog.items, errors, warnings, isStrict);
991
+ validateTransitionLogConsistency(snapshot.transitionLog?.events || [], snapshot.backlog.items, errors, warnings, isStrict);
992
+ const archiveQualityCoherence = await scanArchiveQualityCoherence(paths.archivedDir);
993
+ for (const issue of archiveQualityCoherence.issues) {
994
+ errors.push(issue.message);
995
+ }
996
+ if (archiveQualityCoherence.grandfathered > 0) {
997
+ warnings.push(`Archive quality coherence grandfathered ${archiveQualityCoherence.grandfathered} archived FEAT workspaces (<= FEAT-0451).`);
998
+ }
709
999
  const featuresMissingFrontendDeclaration = config.frontend.enabled
710
1000
  ? snapshot.backlog.items
711
1001
  .filter((item) => item.status !== 'ARCHIVED' && item.status !== 'DONE')
@@ -747,6 +1037,22 @@ export class SddCheckCommand {
747
1037
  if (snapshot.repoMap.items.length === 0) {
748
1038
  missingArchitectureFields.push('repo-map.items vazio');
749
1039
  }
1040
+ const serviceCatalogMaturityAudit = auditServiceCatalogMaturity(snapshot.serviceCatalog.services, snapshot.backlog.items, snapshot.repoMap.items, snapshot.integrationContracts.contracts);
1041
+ if (serviceCatalogMaturityAudit.below5.length > 0) {
1042
+ const sample = serviceCatalogMaturityAudit.below5
1043
+ .slice(0, 10)
1044
+ .map((entry) => `${entry.id}(L${entry.level}: ${entry.unmet.join(', ')})`)
1045
+ .join('; ');
1046
+ const remainder = serviceCatalogMaturityAudit.below5.length - Math.min(10, serviceCatalogMaturityAudit.below5.length);
1047
+ const suffix = remainder > 0 ? `; +${remainder} service(s)` : '';
1048
+ const message = `Service catalog maturity gate failed: ${serviceCatalogMaturityAudit.below5.length}/${serviceCatalogMaturityAudit.serviceCount} services are below level 5. ${sample}${suffix}`;
1049
+ if (isStrict) {
1050
+ errors.push(message);
1051
+ }
1052
+ else {
1053
+ warnings.push(message);
1054
+ }
1055
+ }
750
1056
  const hasFrontendDecisionDemand = config.frontend.enabled &&
751
1057
  ((snapshot.frontendGaps?.items.length ?? 0) > 0 ||
752
1058
  snapshot.backlog.items.some((item) => featureHasMetadataFrontendEvidence(item)));
@@ -796,6 +1102,11 @@ export class SddCheckCommand {
796
1102
  featuresMissingFgapLink.length === 0;
797
1103
  const qualityContractSync = featuresMissingQualityContract.length === 0 &&
798
1104
  featuresMissingQualityArtifact.length === 0;
1105
+ const finalizeQueuePending = snapshot.finalizeQueue.items.filter((item) => item.status === 'PENDING').length;
1106
+ const finalizeQueueDone = snapshot.finalizeQueue.history.length +
1107
+ snapshot.finalizeQueue.items.filter((item) => item.status === 'DONE').length;
1108
+ const frontendGapsSummary = summarizeFrontendGapHealth(snapshot.frontendGaps?.items ?? []);
1109
+ const lockDomainHealth = summarizeLockDomainHealth(snapshot.backlog.items);
799
1110
  return {
800
1111
  valid: errors.length === 0,
801
1112
  errors,
@@ -804,15 +1115,19 @@ export class SddCheckCommand {
804
1115
  discovery: snapshot.discoveryIndex.records.length,
805
1116
  backlog: snapshot.backlog.items.length,
806
1117
  techDebt: snapshot.techDebt.items.length,
807
- finalizeQueue: snapshot.finalizeQueue.items.length,
1118
+ finalizeQueue: finalizeQueuePending,
1119
+ finalizeQueuePending,
1120
+ finalizeQueueDone,
808
1121
  frontendEnabled: config.frontend.enabled,
809
1122
  frontendGaps: snapshot.frontendGaps?.items.length ?? 0,
1123
+ frontendGapsSummary,
810
1124
  frontendRoutes: snapshot.frontendMap?.routes.length ?? 0,
811
1125
  progress_global: progress.progressGlobal,
812
1126
  progress_by_radar: progress.progressByRadar,
813
1127
  ready_for_parallel: graph.readyForParallel,
814
1128
  blocked: graph.blocked,
815
1129
  lock_conflicts: graph.lockConflicts,
1130
+ lock_domain_health: lockDomainHealth,
816
1131
  documentation_sync: docsValidation.documentationSync,
817
1132
  core_views_stale: coreViewsStale,
818
1133
  missing_architecture_fields: missingArchitectureFields,
@@ -1,4 +1,7 @@
1
1
  import { type AcquireOptions } from '../state-lock.js';
2
+ import { type GlobalConfig } from '../../global-config.js';
3
+ import { type RedisRuntimeConfig, type RedisRuntimeStatus } from './redis-runtime.js';
4
+ export { resolveRedisBoundaryConfig } from './redis-runtime.js';
2
5
  export interface CoordinationLockAdapter {
3
6
  withLock<T>(scope: string, fn: () => Promise<T>, options?: AcquireOptions): Promise<T>;
4
7
  }
@@ -24,20 +27,16 @@ export interface CoordinationEventBusAdapter {
24
27
  publish(event: CoordinationEvent): Promise<void>;
25
28
  drain(): Promise<CoordinationEvent[]>;
26
29
  }
27
- export interface RedisBoundaryConfig {
28
- requested: boolean;
29
- namespace: string;
30
- url?: string;
31
- }
30
+ export type RedisBoundaryConfig = RedisRuntimeConfig;
32
31
  export interface SddCoordinationAdapters {
33
- mode: 'filesystem';
32
+ mode: 'filesystem' | 'redis' | 'hybrid';
34
33
  cacheTier: {
35
34
  root: string;
36
35
  projectFingerprint: string;
37
36
  namespace: string;
38
37
  };
39
38
  redis: RedisBoundaryConfig & {
40
- status: 'disabled' | 'requested-unavailable';
39
+ status: RedisRuntimeStatus;
41
40
  reason: string;
42
41
  };
43
42
  locks: CoordinationLockAdapter;
@@ -100,12 +99,20 @@ export declare class InMemoryCoordinationEventBus implements CoordinationEventBu
100
99
  publish(event: CoordinationEvent): Promise<void>;
101
100
  drain(): Promise<CoordinationEvent[]>;
102
101
  }
103
- export declare function resolveRedisBoundaryConfig(env?: NodeJS.ProcessEnv): RedisBoundaryConfig;
104
102
  export declare function createFilesystemFirstCoordinationAdapters(options: {
105
103
  stateDir: string;
106
104
  projectRoot?: string;
107
105
  configFingerprint?: string;
108
106
  cacheRootDir?: string;
109
107
  env?: NodeJS.ProcessEnv;
108
+ globalConfig?: Partial<GlobalConfig>;
110
109
  }): SddCoordinationAdapters;
110
+ export declare function createSddCoordinationAdapters(options: {
111
+ stateDir: string;
112
+ projectRoot?: string;
113
+ configFingerprint?: string;
114
+ cacheRootDir?: string;
115
+ env?: NodeJS.ProcessEnv;
116
+ globalConfig?: Partial<GlobalConfig>;
117
+ }): Promise<SddCoordinationAdapters>;
111
118
  //# sourceMappingURL=coordination-adapters.d.ts.map
@@ -3,6 +3,8 @@ import { createHash } from 'node:crypto';
3
3
  import path from 'node:path';
4
4
  import { promises as fs } from 'node:fs';
5
5
  import { createProjectFingerprint, ensureGlobalCacheLayout, getGlobalCacheDir } from '../../global-config.js';
6
+ import { buildRedisOperationalReport, RedisClientFactory, RedisCoordinationCache, RedisEventBusAdapter, RedisLockAdapter, RedisQueueAdapter, resolveRedisBoundaryConfig, } from './redis-runtime.js';
7
+ export { resolveRedisBoundaryConfig } from './redis-runtime.js';
6
8
  export class FileSystemLockAdapter {
7
9
  stateDir;
8
10
  constructor(stateDir) {
@@ -186,21 +188,11 @@ export class InMemoryCoordinationEventBus {
186
188
  return this.events.splice(0, this.events.length);
187
189
  }
188
190
  }
189
- export function resolveRedisBoundaryConfig(env = process.env) {
190
- const url = env.CODESDD_REDIS_URL?.trim() || env.REDIS_URL?.trim() || undefined;
191
- const requestedByFlag = env.CODESDD_REDIS_ENABLED === 'true';
192
- const namespace = env.CODESDD_REDIS_NAMESPACE?.trim() || 'codesdd';
193
- return {
194
- requested: Boolean(url || requestedByFlag),
195
- namespace,
196
- url,
197
- };
198
- }
199
191
  export function createFilesystemFirstCoordinationAdapters(options) {
200
192
  if (!options.cacheRootDir) {
201
193
  ensureGlobalCacheLayout();
202
194
  }
203
- const redis = resolveRedisBoundaryConfig(options.env);
195
+ const redis = resolveRedisBoundaryConfig(options.env, options.globalConfig);
204
196
  const cache = new MultiTierCoordinationCache({
205
197
  projectRoot: options.projectRoot ?? process.cwd(),
206
198
  configFingerprint: options.configFingerprint,
@@ -211,10 +203,12 @@ export function createFilesystemFirstCoordinationAdapters(options) {
211
203
  cacheTier: cache.describeTierState(),
212
204
  redis: {
213
205
  ...redis,
214
- status: redis.requested ? 'requested-unavailable' : 'disabled',
215
- reason: redis.requested
216
- ? 'Redis adapter boundary is configured but no Redis client adapter is installed; filesystem-first defaults remain authoritative.'
217
- : 'Redis is optional and disabled; filesystem-first defaults are authoritative.',
206
+ status: redis.validationErrors.length > 0 ? 'blocked' : redis.requested ? 'requested-unavailable' : 'disabled',
207
+ reason: redis.validationErrors.length > 0
208
+ ? 'Redis configuration is invalid and was blocked fail-closed.'
209
+ : redis.requested
210
+ ? 'Redis is requested but unavailable; filesystem-first defaults remain authoritative.'
211
+ : 'Redis is optional and disabled; filesystem-first defaults are authoritative.',
218
212
  },
219
213
  locks: new FileSystemLockAdapter(options.stateDir),
220
214
  cache,
@@ -222,4 +216,38 @@ export function createFilesystemFirstCoordinationAdapters(options) {
222
216
  events: new InMemoryCoordinationEventBus(),
223
217
  };
224
218
  }
219
+ export async function createSddCoordinationAdapters(options) {
220
+ const filesystem = createFilesystemFirstCoordinationAdapters(options);
221
+ const projectRoot = options.projectRoot ?? process.cwd();
222
+ const redis = resolveRedisBoundaryConfig(options.env, options.globalConfig);
223
+ const report = await buildRedisOperationalReport({
224
+ env: options.env,
225
+ globalConfig: options.globalConfig,
226
+ });
227
+ if (report.status !== 'ready' || !redis.url) {
228
+ return {
229
+ ...filesystem,
230
+ redis: {
231
+ ...redis,
232
+ status: report.status,
233
+ reason: report.reason,
234
+ },
235
+ };
236
+ }
237
+ const projectFingerprint = createProjectFingerprint(projectRoot);
238
+ const factory = new RedisClientFactory(redis);
239
+ return {
240
+ ...filesystem,
241
+ mode: redis.fallback === 'filesystem' ? 'hybrid' : 'redis',
242
+ redis: {
243
+ ...redis,
244
+ status: 'ready',
245
+ reason: report.reason,
246
+ },
247
+ cache: new RedisCoordinationCache(factory, filesystem.cache, projectFingerprint),
248
+ locks: new RedisLockAdapter(factory, filesystem.locks, projectFingerprint),
249
+ queues: new RedisQueueAdapter(factory, filesystem.queues, projectFingerprint),
250
+ events: new RedisEventBusAdapter(factory, filesystem.events, projectFingerprint),
251
+ };
252
+ }
225
253
  //# sourceMappingURL=coordination-adapters.js.map
@@ -1,2 +1,3 @@
1
1
  export * from './coordination-adapters.js';
2
+ export * from './redis-runtime.js';
2
3
  //# sourceMappingURL=index.d.ts.map
@@ -1,2 +1,3 @@
1
1
  export * from './coordination-adapters.js';
2
+ export * from './redis-runtime.js';
2
3
  //# sourceMappingURL=index.js.map