@devtrack-solution/codesdd 1.2.4-rc3 → 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 (128) hide show
  1. package/.sdd/skills/curated/devtrack-api/SKILL.md +91 -12
  2. package/.sdd/skills/curated/devtrack-api/agents/claude-code.yaml +2 -0
  3. package/.sdd/skills/curated/devtrack-api/agents/codex.yaml +2 -0
  4. package/.sdd/skills/curated/devtrack-api/agents/cursor.yaml +2 -0
  5. package/.sdd/skills/curated/devtrack-api/agents/gemini.yaml +2 -0
  6. package/.sdd/skills/curated/devtrack-api/agents/kimi.yaml +2 -0
  7. package/.sdd/skills/curated/devtrack-api/agents/openai.yaml +3 -3
  8. package/.sdd/skills/curated/devtrack-api/agents/opencode.yaml +2 -0
  9. package/.sdd/skills/curated/devtrack-api/references/application-presentation.md +59 -3
  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 +1898 -2
  12. package/.sdd/skills/curated/devtrack-api/references/domain-modeling.md +3 -1
  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 +20 -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 +4 -3
  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 -5
  20. package/README.md +122 -25
  21. package/dist/cli/program.js +180 -11
  22. package/dist/commands/config.js +27 -1
  23. package/dist/commands/sdd/execution.js +64 -2
  24. package/dist/commands/sdd.js +119 -4
  25. package/dist/core/cli/command-matrix.d.ts +18 -0
  26. package/dist/core/cli/command-matrix.js +148 -0
  27. package/dist/core/cli-command-quality.js +2 -0
  28. package/dist/core/config-schema.d.ts +14 -1
  29. package/dist/core/config-schema.js +32 -1
  30. package/dist/core/config.d.ts +1 -0
  31. package/dist/core/config.js +11 -0
  32. package/dist/core/global-config.d.ts +13 -0
  33. package/dist/core/init.d.ts +2 -2
  34. package/dist/core/init.js +13 -14
  35. package/dist/core/sdd/agent-binding.d.ts +9 -9
  36. package/dist/core/sdd/agent-runtime-contract.d.ts +4 -4
  37. package/dist/core/sdd/allocator-recovery.d.ts +14 -0
  38. package/dist/core/sdd/allocator-recovery.js +30 -0
  39. package/dist/core/sdd/allocator-security.d.ts +18 -0
  40. package/dist/core/sdd/allocator-security.js +36 -0
  41. package/dist/core/sdd/api-foundation-baseline.d.ts +111 -0
  42. package/dist/core/sdd/api-foundation-baseline.js +151 -0
  43. package/dist/core/sdd/api-foundation-parity.d.ts +114 -0
  44. package/dist/core/sdd/api-foundation-parity.js +131 -0
  45. package/dist/core/sdd/api-profile-catalog.d.ts +36 -0
  46. package/dist/core/sdd/api-profile-catalog.js +132 -0
  47. package/dist/core/sdd/api-profile-dry-run-projection.d.ts +93 -0
  48. package/dist/core/sdd/api-profile-dry-run-projection.js +370 -0
  49. package/dist/core/sdd/api-profile-recipes.d.ts +82 -0
  50. package/dist/core/sdd/api-profile-recipes.js +484 -0
  51. package/dist/core/sdd/artifact-id-allocator.d.ts +368 -0
  52. package/dist/core/sdd/artifact-id-allocator.js +510 -0
  53. package/dist/core/sdd/check.d.ts +50 -1
  54. package/dist/core/sdd/check.js +286 -9
  55. package/dist/core/sdd/deepagent-contracts.d.ts +4 -4
  56. package/dist/core/sdd/deepagents/reversa-subagents.d.ts +3 -3
  57. package/dist/core/sdd/default-bootstrap-files.d.ts +1 -1
  58. package/dist/core/sdd/default-bootstrap-files.js +0 -2
  59. package/dist/core/sdd/default-skills.js +7 -5
  60. package/dist/core/sdd/devtrack-api-appliance.d.ts +34 -0
  61. package/dist/core/sdd/devtrack-api-appliance.js +138 -34
  62. package/dist/core/sdd/devtrack-api-architecture.d.ts +16 -0
  63. package/dist/core/sdd/devtrack-api-architecture.js +86 -0
  64. package/dist/core/sdd/docs-sync.js +3 -3
  65. package/dist/core/sdd/enterprise-mutating-command-gate.d.ts +27 -0
  66. package/dist/core/sdd/enterprise-mutating-command-gate.js +104 -0
  67. package/dist/core/sdd/enterprise-provenance-gates.d.ts +20 -0
  68. package/dist/core/sdd/enterprise-provenance-gates.js +63 -0
  69. package/dist/core/sdd/enterprise-provisioning-policy.d.ts +26 -0
  70. package/dist/core/sdd/enterprise-provisioning-policy.js +104 -0
  71. package/dist/core/sdd/governance-schemas.d.ts +2 -2
  72. package/dist/core/sdd/governance-schemas.js +11 -2
  73. package/dist/core/sdd/json-schema.js +4 -0
  74. package/dist/core/sdd/legacy-operations.js +93 -4
  75. package/dist/core/sdd/package-security-gates.js +2 -0
  76. package/dist/core/sdd/package-structure-gate.d.ts +85 -3
  77. package/dist/core/sdd/package-structure-gate.js +386 -8
  78. package/dist/core/sdd/parallel-feat-automation.d.ts +6 -6
  79. package/dist/core/sdd/plugin-policy.js +6 -1
  80. package/dist/core/sdd/plugin-registry.d.ts +3 -3
  81. package/dist/core/sdd/quality-validation.d.ts +5 -5
  82. package/dist/core/sdd/release-readiness.d.ts +49 -0
  83. package/dist/core/sdd/release-readiness.js +303 -8
  84. package/dist/core/sdd/reversa-architecture-extractor.d.ts +13 -0
  85. package/dist/core/sdd/reversa-architecture-extractor.js +89 -0
  86. package/dist/core/sdd/reversa-artifact-writer.d.ts +18 -0
  87. package/dist/core/sdd/reversa-artifact-writer.js +40 -0
  88. package/dist/core/sdd/reversa-command-policy.d.ts +136 -0
  89. package/dist/core/sdd/reversa-command-policy.js +361 -0
  90. package/dist/core/sdd/reversa-data-extractor.d.ts +11 -0
  91. package/dist/core/sdd/reversa-data-extractor.js +73 -0
  92. package/dist/core/sdd/reversa-equivalence.d.ts +20 -0
  93. package/dist/core/sdd/reversa-equivalence.js +34 -0
  94. package/dist/core/sdd/reversa-evidence.d.ts +298 -0
  95. package/dist/core/sdd/reversa-evidence.js +118 -0
  96. package/dist/core/sdd/reversa-reconstruction.d.ts +29 -0
  97. package/dist/core/sdd/reversa-reconstruction.js +32 -0
  98. package/dist/core/sdd/reversa-rules-extractor.d.ts +12 -0
  99. package/dist/core/sdd/reversa-rules-extractor.js +86 -0
  100. package/dist/core/sdd/reversa-source-safety.d.ts +19 -0
  101. package/dist/core/sdd/reversa-source-safety.js +105 -0
  102. package/dist/core/sdd/reversa-surface-scout.d.ts +13 -0
  103. package/dist/core/sdd/reversa-surface-scout.js +85 -0
  104. package/dist/core/sdd/reversa-ux-mapper.d.ts +11 -0
  105. package/dist/core/sdd/reversa-ux-mapper.js +73 -0
  106. package/dist/core/sdd/sdk-agent-plugin-quality-gates.d.ts +1 -1
  107. package/dist/core/sdd/services/archive-quality-coherence.service.d.ts +17 -0
  108. package/dist/core/sdd/services/archive-quality-coherence.service.js +141 -0
  109. package/dist/core/sdd/services/decide.service.js +1 -1
  110. package/dist/core/sdd/services/finalize.service.d.ts +2 -0
  111. package/dist/core/sdd/services/finalize.service.js +48 -2
  112. package/dist/core/sdd/services/historical-quality-regression.service.d.ts +35 -0
  113. package/dist/core/sdd/services/historical-quality-regression.service.js +228 -0
  114. package/dist/core/sdd/services/ingest-deposito.service.js +1 -1
  115. package/dist/core/sdd/services/planning-execution-coherence.service.d.ts +45 -0
  116. package/dist/core/sdd/services/planning-execution-coherence.service.js +225 -0
  117. package/dist/core/sdd/state.js +15 -5
  118. package/dist/core/sdd/types.d.ts +3 -3
  119. package/dist/core/sdd/workspace-schemas.d.ts +45 -4
  120. package/dist/core/sdd/workspace-schemas.js +27 -6
  121. package/dist/core/shared/skill-generation.d.ts +2 -0
  122. package/dist/core/shared/skill-generation.js +19 -2
  123. package/dist/core/shared/tool-detection.d.ts +19 -0
  124. package/dist/core/shared/tool-detection.js +89 -0
  125. package/package.json +6 -5
  126. package/schemas/sdd/5-quality.schema.json +43 -0
  127. package/schemas/sdd/reversa-evidence-bundle.schema.json +466 -0
  128. package/schemas/sdd/workspace-catalog.schema.json +511 -0
@@ -0,0 +1,228 @@
1
+ import { existsSync, promises as fs } from 'node:fs';
2
+ import path from 'node:path';
3
+ import { parse as parseYaml } from 'yaml';
4
+ import { parseWorkspaceYamlDocument } from '../workspace-schemas.js';
5
+ const PLACEHOLDER_PATTERNS = [
6
+ /\(fill in/i,
7
+ /\(placeholder/i,
8
+ /\btodo\b/i,
9
+ /\btbd\b/i,
10
+ /\bpending\b/i,
11
+ /\bmust implement\b/i,
12
+ /\bto be executed\b/i,
13
+ /\bplanned\b/i,
14
+ /\bwill be\b/i,
15
+ /\bto do\b/i,
16
+ ];
17
+ const DEFAULT_RECENT_WINDOW_SIZE = 25;
18
+ const DEFAULT_GRANDFATHER_MAX_FEAT = 451;
19
+ function featNumericId(featureId) {
20
+ const match = featureId.match(/^FEAT-(\d{4,})$/);
21
+ if (!match)
22
+ return null;
23
+ return Number.parseInt(match[1] || '', 10);
24
+ }
25
+ function hasPlaceholderSignal(text) {
26
+ return PLACEHOLDER_PATTERNS.some((pattern) => pattern.test(text));
27
+ }
28
+ function hasPlaceholderEvidence(document) {
29
+ const evidenceTexts = [
30
+ ...document.evidence_log.flatMap((entry) => [entry.kind, entry.result, entry.artifact ?? '']),
31
+ ...document.acceptance_matrix.flatMap((entry) => [entry.criterion, entry.evidence]),
32
+ ];
33
+ return evidenceTexts.some((text) => text && hasPlaceholderSignal(text));
34
+ }
35
+ function sortFeaturesByRecency(featureIds) {
36
+ return [...featureIds].sort((left, right) => {
37
+ const leftNum = featNumericId(left);
38
+ const rightNum = featNumericId(right);
39
+ if (leftNum !== null && rightNum !== null && leftNum !== rightNum) {
40
+ return rightNum - leftNum;
41
+ }
42
+ if (leftNum !== null && rightNum === null)
43
+ return -1;
44
+ if (leftNum === null && rightNum !== null)
45
+ return 1;
46
+ return right.localeCompare(left);
47
+ });
48
+ }
49
+ async function loadBacklogEvidence(backlogPath) {
50
+ const map = new Map();
51
+ if (!existsSync(backlogPath))
52
+ return map;
53
+ const raw = await fs.readFile(backlogPath, 'utf-8').catch(() => '');
54
+ if (!raw.trim())
55
+ return map;
56
+ const parsed = parseYaml(raw);
57
+ for (const item of parsed.items ?? []) {
58
+ const id = typeof item.id === 'string' ? item.id : '';
59
+ if (!id.startsWith('FEAT-'))
60
+ continue;
61
+ map.set(id, {
62
+ id,
63
+ status: typeof item.status === 'string' ? item.status : undefined,
64
+ frontend_impact_status: typeof item.frontend_impact_status === 'string' ? item.frontend_impact_status : undefined,
65
+ done_at: typeof item.done_at === 'string' ? item.done_at : undefined,
66
+ archived_at: typeof item.archived_at === 'string' ? item.archived_at : undefined,
67
+ });
68
+ }
69
+ return map;
70
+ }
71
+ export class HistoricalQualityRegressionService {
72
+ async execute(projectRoot, options = {}) {
73
+ const mode = options.mode ?? 'recent-window';
74
+ const recentWindowSize = options.recentWindowSize ?? DEFAULT_RECENT_WINDOW_SIZE;
75
+ const grandfatherMaxFeat = options.grandfatherMaxFeat ?? DEFAULT_GRANDFATHER_MAX_FEAT;
76
+ const archivedDir = path.join(projectRoot, '.sdd', 'archived');
77
+ const backlogPath = path.join(projectRoot, '.sdd', 'state', 'backlog.yaml');
78
+ const adrsDir = path.join(projectRoot, '.sdd', 'core', 'adrs');
79
+ const backlogByFeature = await loadBacklogEvidence(backlogPath);
80
+ if (!existsSync(archivedDir)) {
81
+ return {
82
+ generated_at: new Date().toISOString(),
83
+ mode,
84
+ recent_window_size: mode === 'recent-window' ? recentWindowSize : null,
85
+ scanned: 0,
86
+ considered: 0,
87
+ analyzed: 0,
88
+ grandfathered: 0,
89
+ issue_count: 0,
90
+ grandfathering: {
91
+ enabled: true,
92
+ max_feat_id: grandfatherMaxFeat,
93
+ policy: `Skip blocking regression checks for archived FEAT ids <= FEAT-${String(grandfatherMaxFeat).padStart(4, '0')}.`,
94
+ },
95
+ issues: [],
96
+ };
97
+ }
98
+ const entries = await fs.readdir(archivedDir, { withFileTypes: true });
99
+ const scannedFeatures = entries
100
+ .filter((entry) => entry.isDirectory() && entry.name.startsWith('FEAT-'))
101
+ .map((entry) => entry.name);
102
+ const orderedFeatures = sortFeaturesByRecency(scannedFeatures);
103
+ const consideredFeatures = mode === 'recent-window' ? orderedFeatures.slice(0, Math.max(1, recentWindowSize)) : orderedFeatures;
104
+ let analyzed = 0;
105
+ let grandfathered = 0;
106
+ const issues = [];
107
+ for (const featureId of consideredFeatures) {
108
+ const numericId = featNumericId(featureId);
109
+ if (numericId !== null && numericId <= grandfatherMaxFeat) {
110
+ grandfathered += 1;
111
+ continue;
112
+ }
113
+ const backlog = backlogByFeature.get(featureId);
114
+ const qualityPath = path.join(archivedDir, featureId, '5-quality.yaml');
115
+ const adrPath = path.join(adrsDir, `ADR-${featureId}.md`);
116
+ if (!backlog) {
117
+ issues.push({
118
+ feature_id: featureId,
119
+ code: 'STATE_CORE_DIVERGENCE',
120
+ message: `${featureId} is archived but missing from .sdd/state/backlog.yaml.`,
121
+ backlog_path: backlogPath,
122
+ });
123
+ }
124
+ if (backlog && backlog.status && !['DONE', 'ARCHIVED'].includes(backlog.status)) {
125
+ issues.push({
126
+ feature_id: featureId,
127
+ code: 'STATE_CORE_DIVERGENCE',
128
+ message: `${featureId} is archived on disk but backlog status is ${backlog.status}.`,
129
+ backlog_path: backlogPath,
130
+ });
131
+ }
132
+ if (backlog && (backlog.frontend_impact_status ?? 'unknown') === 'unknown') {
133
+ issues.push({
134
+ feature_id: featureId,
135
+ code: 'FRONTEND_IMPACT_UNKNOWN',
136
+ message: `${featureId} has frontend_impact_status=unknown in backlog state.`,
137
+ backlog_path: backlogPath,
138
+ });
139
+ }
140
+ if (backlog && !backlog.done_at) {
141
+ issues.push({
142
+ feature_id: featureId,
143
+ code: 'MISSING_DONE_AT',
144
+ message: `${featureId} is archived without done_at in backlog state.`,
145
+ backlog_path: backlogPath,
146
+ });
147
+ }
148
+ if (backlog && !backlog.archived_at) {
149
+ issues.push({
150
+ feature_id: featureId,
151
+ code: 'MISSING_ARCHIVED_AT',
152
+ message: `${featureId} is archived without archived_at in backlog state.`,
153
+ backlog_path: backlogPath,
154
+ });
155
+ }
156
+ if (!existsSync(adrPath)) {
157
+ issues.push({
158
+ feature_id: featureId,
159
+ code: 'MISSING_ADR',
160
+ message: `${featureId} is missing required ADR file at .sdd/core/adrs/ADR-${featureId}.md.`,
161
+ adr_path: adrPath,
162
+ });
163
+ issues.push({
164
+ feature_id: featureId,
165
+ code: 'STATE_CORE_DIVERGENCE',
166
+ message: `${featureId} has no ADR in .sdd/core/adrs while archived state exists.`,
167
+ adr_path: adrPath,
168
+ });
169
+ }
170
+ if (!existsSync(qualityPath)) {
171
+ continue;
172
+ }
173
+ const rawQuality = await fs.readFile(qualityPath, 'utf-8').catch(() => '');
174
+ if (!rawQuality.trim()) {
175
+ continue;
176
+ }
177
+ let quality;
178
+ try {
179
+ quality = parseWorkspaceYamlDocument('5-quality.yaml', rawQuality);
180
+ }
181
+ catch {
182
+ continue;
183
+ }
184
+ analyzed += 1;
185
+ if (quality.acceptance_matrix.some((entry) => entry.status === 'not_met')) {
186
+ issues.push({
187
+ feature_id: featureId,
188
+ code: 'ACCEPTANCE_NOT_MET',
189
+ message: `${featureId} contains acceptance_matrix.status=not_met in archived quality evidence.`,
190
+ quality_path: qualityPath,
191
+ });
192
+ }
193
+ if (quality.requirement_validation_evidence_risk_matrix.some((entry) => entry.risk_status === 'open')) {
194
+ issues.push({
195
+ feature_id: featureId,
196
+ code: 'OPEN_RISK',
197
+ message: `${featureId} contains requirement risk_status=open in archived quality evidence.`,
198
+ quality_path: qualityPath,
199
+ });
200
+ }
201
+ if (hasPlaceholderEvidence(quality)) {
202
+ issues.push({
203
+ feature_id: featureId,
204
+ code: 'PLACEHOLDER_EVIDENCE',
205
+ message: `${featureId} still includes placeholder or future-tense evidence markers.`,
206
+ quality_path: qualityPath,
207
+ });
208
+ }
209
+ }
210
+ return {
211
+ generated_at: new Date().toISOString(),
212
+ mode,
213
+ recent_window_size: mode === 'recent-window' ? recentWindowSize : null,
214
+ scanned: orderedFeatures.length,
215
+ considered: consideredFeatures.length,
216
+ analyzed,
217
+ grandfathered,
218
+ issue_count: issues.length,
219
+ grandfathering: {
220
+ enabled: true,
221
+ max_feat_id: grandfatherMaxFeat,
222
+ policy: `Skip blocking regression checks for archived FEAT ids <= FEAT-${String(grandfatherMaxFeat).padStart(4, '0')}.`,
223
+ },
224
+ issues,
225
+ };
226
+ }
227
+ }
228
+ //# sourceMappingURL=historical-quality-regression.service.js.map
@@ -224,7 +224,7 @@ export class IngestDepositoService {
224
224
  'frontend-extractor-sdd',
225
225
  'planning-normalizer-sdd',
226
226
  ],
227
- recommended_prompt: relProjectPath(paths, path.join(paths.promptsDir, '01-ingestao-deposito.md')),
227
+ recommended_prompt: relProjectPath(paths, path.join(paths.promptsDir, config.layout === 'en-US' ? '01-source-intake.md' : '01-ingestao-deposito.md')),
228
228
  };
229
229
  }
230
230
  }
@@ -0,0 +1,45 @@
1
+ import { type WorkspaceChangelogDocument, type WorkspacePlanDocument, type WorkspaceQualityDocument, type WorkspaceSpecDocument, type WorkspaceTasksDocument } from '../workspace-schemas.js';
2
+ export type PlanningExecutionCoherenceIssueCode = 'MISSING_FILE_EVIDENCE' | 'MISSING_COMMAND_EVIDENCE' | 'ACCEPTANCE_NOT_MET' | 'RISK_STATUS_OPEN' | 'ADR_MISSING';
3
+ export interface PlanningExecutionCoherenceIssue {
4
+ code: PlanningExecutionCoherenceIssueCode;
5
+ severity: 'error' | 'warning';
6
+ message: string;
7
+ evidence: Record<string, unknown>;
8
+ }
9
+ export interface PlanningExecutionCoherenceReport {
10
+ schema_version: 1;
11
+ feature_id: string;
12
+ generated_at: string;
13
+ workspace_path: string;
14
+ adr_path: string;
15
+ healthy: boolean;
16
+ planned: {
17
+ files: string[];
18
+ commands: string[];
19
+ };
20
+ execution: {
21
+ files: string[];
22
+ commands: string[];
23
+ };
24
+ issues: PlanningExecutionCoherenceIssue[];
25
+ }
26
+ export interface EvaluatePlanningExecutionCoherenceInput {
27
+ featureId: string;
28
+ workspacePath: string;
29
+ adrPath: string;
30
+ adrExists: boolean;
31
+ spec: WorkspaceSpecDocument;
32
+ plan: WorkspacePlanDocument;
33
+ tasks: WorkspaceTasksDocument;
34
+ changelog: WorkspaceChangelogDocument;
35
+ quality: WorkspaceQualityDocument;
36
+ changedFiles: string[];
37
+ }
38
+ export interface PlanningExecutionCoherenceOptions {
39
+ changedFiles?: string[];
40
+ }
41
+ export declare function evaluatePlanningExecutionCoherence(input: EvaluatePlanningExecutionCoherenceInput): PlanningExecutionCoherenceReport;
42
+ export declare class PlanningExecutionCoherenceService {
43
+ execute(projectRoot: string, featureId: string, options?: PlanningExecutionCoherenceOptions): Promise<PlanningExecutionCoherenceReport>;
44
+ }
45
+ //# sourceMappingURL=planning-execution-coherence.service.d.ts.map
@@ -0,0 +1,225 @@
1
+ import path from 'node:path';
2
+ import { existsSync, promises as fs } from 'node:fs';
3
+ import { execFile } from 'node:child_process';
4
+ import { promisify } from 'node:util';
5
+ import { activeDocNamesForLayout, ensureMemoryInitialized, relProjectPath } from '../legacy-operations.js';
6
+ import { loadProjectSddConfig, resolveSddPaths } from '../state.js';
7
+ import { parseWorkspaceYamlDocument, } from '../workspace-schemas.js';
8
+ const execFileAsync = promisify(execFile);
9
+ function uniqueSorted(values) {
10
+ return Array.from(new Set(values.filter(Boolean))).sort((a, b) => a.localeCompare(b));
11
+ }
12
+ function normalizeCommand(value) {
13
+ return value.replace(/\s+/g, ' ').trim().toLowerCase();
14
+ }
15
+ function normalizePathForCoherence(value) {
16
+ const normalized = value.replace(/\\/g, '/').trim();
17
+ return normalized.replace(/\.sdd\/(planned|active|archived)\/(FEAT-\d{4})\//, '.sdd/*/$2/');
18
+ }
19
+ function pathCovered(plannedPath, evidencePaths) {
20
+ const planned = normalizePathForCoherence(plannedPath);
21
+ return evidencePaths.some((candidate) => {
22
+ const normalizedCandidate = normalizePathForCoherence(candidate);
23
+ return (normalizedCandidate === planned ||
24
+ normalizedCandidate.endsWith(`/${planned}`) ||
25
+ planned.endsWith(`/${normalizedCandidate}`));
26
+ });
27
+ }
28
+ function collectPlannedFiles(plan, tasks, quality) {
29
+ const includePath = (value) => {
30
+ const normalized = value.replace(/\\/g, '/');
31
+ if (normalized.startsWith('.sdd/state/'))
32
+ return false;
33
+ if (normalized.startsWith('.sdd/archived/'))
34
+ return false;
35
+ return true;
36
+ };
37
+ return uniqueSorted([
38
+ ...plan.execution_plan.command_sequence.flatMap((step) => step.expected_state_writes.filter(includePath)),
39
+ ...tasks.tasks
40
+ .filter((task) => task.phase !== 'finalization')
41
+ .flatMap((task) => task.files_touched.filter(includePath)),
42
+ ...quality.traceability.requirements.flatMap((req) => req.code_refs.map((ref) => ref.path)),
43
+ ]);
44
+ }
45
+ function collectPlannedCommands(plan, tasks, quality) {
46
+ return uniqueSorted([
47
+ ...plan.governance.validation_gates.map((gate) => gate.command),
48
+ ...tasks.tasks.flatMap((task) => task.test_scripts.map((script) => script.command)),
49
+ ...quality.validation_strategies.map((strategy) => strategy.command),
50
+ ]).map(normalizeCommand);
51
+ }
52
+ function collectExecutionFiles(quality, changelog, changedFiles) {
53
+ const changelogRefs = changelog.entries.flatMap((entry) => entry.refs);
54
+ return uniqueSorted([
55
+ ...changedFiles,
56
+ ...quality.evidence_log.map((entry) => entry.artifact ?? '').filter(Boolean),
57
+ ...quality.traceability.requirements.flatMap((req) => req.evidence_refs),
58
+ ...changelogRefs,
59
+ ]);
60
+ }
61
+ function collectExecutionCommands(quality) {
62
+ return uniqueSorted(quality.evidence_log
63
+ .map((entry) => entry.provenance?.command ?? '')
64
+ .filter(Boolean)
65
+ .map(normalizeCommand));
66
+ }
67
+ function expectsAdr(spec, tasks) {
68
+ const corpus = [spec.objective, spec.expected_outcome, ...tasks.tasks.map((task) => `${task.title} ${task.description}`)]
69
+ .join(' ')
70
+ .toLowerCase();
71
+ return corpus.includes('adr');
72
+ }
73
+ export function evaluatePlanningExecutionCoherence(input) {
74
+ const plannedFiles = collectPlannedFiles(input.plan, input.tasks, input.quality);
75
+ const plannedCommands = collectPlannedCommands(input.plan, input.tasks, input.quality);
76
+ const executionFiles = collectExecutionFiles(input.quality, input.changelog, input.changedFiles);
77
+ const executionCommands = collectExecutionCommands(input.quality);
78
+ const issues = [];
79
+ const missingFiles = plannedFiles.filter((plannedFile) => !pathCovered(plannedFile, executionFiles));
80
+ if (missingFiles.length > 0) {
81
+ issues.push({
82
+ code: 'MISSING_FILE_EVIDENCE',
83
+ severity: 'error',
84
+ message: 'Planned files are not reflected in diff/evidence artifacts.',
85
+ evidence: {
86
+ missing_files: missingFiles,
87
+ },
88
+ });
89
+ }
90
+ const missingCommands = plannedCommands.filter((command) => !executionCommands.includes(command));
91
+ if (missingCommands.length > 0) {
92
+ issues.push({
93
+ code: 'MISSING_COMMAND_EVIDENCE',
94
+ severity: 'warning',
95
+ message: 'Planned validation commands are missing from evidence provenance.',
96
+ evidence: {
97
+ missing_commands: missingCommands,
98
+ },
99
+ });
100
+ }
101
+ const notMetAcceptance = input.quality.acceptance_matrix.filter((entry) => entry.status === 'not_met');
102
+ if (notMetAcceptance.length > 0) {
103
+ issues.push({
104
+ code: 'ACCEPTANCE_NOT_MET',
105
+ severity: 'error',
106
+ message: 'Quality acceptance matrix still has not_met criteria.',
107
+ evidence: {
108
+ criteria: notMetAcceptance.map((entry) => entry.criterion),
109
+ },
110
+ });
111
+ }
112
+ const openRisks = input.quality.requirement_validation_evidence_risk_matrix.filter((entry) => entry.risk_status === 'open');
113
+ if (openRisks.length > 0) {
114
+ issues.push({
115
+ code: 'RISK_STATUS_OPEN',
116
+ severity: 'warning',
117
+ message: 'Requirement risk matrix still has open risk entries.',
118
+ evidence: {
119
+ requirements: openRisks.map((entry) => entry.requirement_ref),
120
+ },
121
+ });
122
+ }
123
+ if (expectsAdr(input.spec, input.tasks) && !input.adrExists) {
124
+ issues.push({
125
+ code: 'ADR_MISSING',
126
+ severity: 'error',
127
+ message: 'Planning requires ADR coherence but ADR file is missing.',
128
+ evidence: {
129
+ expected_adr: input.adrPath,
130
+ },
131
+ });
132
+ }
133
+ const healthy = !issues.some((issue) => issue.severity === 'error');
134
+ return {
135
+ schema_version: 1,
136
+ feature_id: input.featureId,
137
+ generated_at: new Date().toISOString(),
138
+ workspace_path: input.workspacePath,
139
+ adr_path: input.adrPath,
140
+ healthy,
141
+ planned: {
142
+ files: plannedFiles,
143
+ commands: plannedCommands,
144
+ },
145
+ execution: {
146
+ files: executionFiles,
147
+ commands: executionCommands,
148
+ },
149
+ issues,
150
+ };
151
+ }
152
+ async function listChangedFiles(projectRoot) {
153
+ try {
154
+ const { stdout } = await execFileAsync('git', ['status', '--porcelain'], {
155
+ cwd: projectRoot,
156
+ maxBuffer: 16 * 1024 * 1024,
157
+ });
158
+ return uniqueSorted(stdout
159
+ .split('\n')
160
+ .map((line) => line.trim())
161
+ .filter(Boolean)
162
+ .map((line) => line.slice(3).trim())
163
+ .map((line) => (line.includes('->') ? line.split('->').pop() ?? line : line))
164
+ .map((line) => line.trim())
165
+ .filter(Boolean));
166
+ }
167
+ catch {
168
+ return [];
169
+ }
170
+ }
171
+ async function resolveWorkspacePath(projectRoot, featureId) {
172
+ const config = await loadProjectSddConfig(projectRoot);
173
+ const paths = resolveSddPaths(projectRoot, config);
174
+ await ensureMemoryInitialized(paths);
175
+ const roots = [paths.activeDir, paths.plannedDir, paths.archivedDir];
176
+ for (const root of roots) {
177
+ const workspacePath = path.join(root, featureId);
178
+ if (existsSync(workspacePath)) {
179
+ return {
180
+ workspacePath,
181
+ adrPath: path.join(paths.coreDir, 'adrs', `ADR-${featureId}.md`),
182
+ };
183
+ }
184
+ }
185
+ throw new Error(`Workspace not found for ${featureId}.`);
186
+ }
187
+ export class PlanningExecutionCoherenceService {
188
+ async execute(projectRoot, featureId, options = {}) {
189
+ if (!/^FEAT-\d{4}$/.test(featureId)) {
190
+ throw new Error('Invalid feature id. Use FEAT-####.');
191
+ }
192
+ const config = await loadProjectSddConfig(projectRoot);
193
+ const paths = resolveSddPaths(projectRoot, config);
194
+ const names = activeDocNamesForLayout(config);
195
+ const { workspacePath, adrPath } = await resolveWorkspacePath(projectRoot, featureId);
196
+ const readDoc = async (name) => {
197
+ const filePath = path.join(workspacePath, name);
198
+ if (!existsSync(filePath)) {
199
+ throw new Error(`${name} not found for ${featureId}.`);
200
+ }
201
+ return fs.readFile(filePath, 'utf-8');
202
+ };
203
+ const [specRaw, planRaw, tasksRaw, changelogRaw, qualityRaw] = await Promise.all([
204
+ readDoc(names.spec),
205
+ readDoc(names.plan),
206
+ readDoc(names.tasks),
207
+ readDoc(names.changelog),
208
+ readDoc(names.quality),
209
+ ]);
210
+ const report = evaluatePlanningExecutionCoherence({
211
+ featureId,
212
+ workspacePath: relProjectPath(paths, workspacePath),
213
+ adrPath: relProjectPath(paths, adrPath),
214
+ adrExists: existsSync(adrPath),
215
+ spec: parseWorkspaceYamlDocument('1-spec.yaml', specRaw),
216
+ plan: parseWorkspaceYamlDocument('2-plan.yaml', planRaw),
217
+ tasks: parseWorkspaceYamlDocument('3-tasks.yaml', tasksRaw),
218
+ changelog: parseWorkspaceYamlDocument('4-changelog.yaml', changelogRaw),
219
+ quality: parseWorkspaceYamlDocument('5-quality.yaml', qualityRaw),
220
+ changedFiles: options.changedFiles ?? (await listChangedFiles(projectRoot)),
221
+ });
222
+ return report;
223
+ }
224
+ }
225
+ //# sourceMappingURL=planning-execution-coherence.service.js.map
@@ -305,9 +305,9 @@ export function defaultFoldersForLayout(layout) {
305
305
  const DEFAULT_SDD_CONFIG = {
306
306
  enabled: true,
307
307
  memoryDir: '.sdd',
308
- language: 'pt-BR',
309
- layout: 'legacy',
310
- folders: defaultFoldersForLayout('legacy'),
308
+ language: 'en-US',
309
+ layout: 'en-US',
310
+ folders: defaultFoldersForLayout('en-US'),
311
311
  frontend: { enabled: false },
312
312
  views: { autoRender: true },
313
313
  };
@@ -358,8 +358,18 @@ export function mergeRuntimeConfig(raw) {
358
358
  }
359
359
  const frontend = isRecord(raw.frontend) ? raw.frontend : {};
360
360
  const views = isRecord(raw.views) ? raw.views : {};
361
- const language = raw.language === 'en-US' ? 'en-US' : 'pt-BR';
362
- const layout = raw.layout === 'pt-BR' ? 'pt-BR' : raw.layout === 'en-US' ? 'en-US' : 'legacy';
361
+ const language = raw.language === 'pt-BR'
362
+ ? 'pt-BR'
363
+ : raw.language === 'en-US'
364
+ ? 'en-US'
365
+ : DEFAULT_SDD_CONFIG.language;
366
+ const layout = raw.layout === 'pt-BR'
367
+ ? 'pt-BR'
368
+ : raw.layout === 'en-US'
369
+ ? 'en-US'
370
+ : raw.layout === 'legacy'
371
+ ? 'legacy'
372
+ : DEFAULT_SDD_CONFIG.layout;
363
373
  const folderDefaults = defaultFoldersForLayout(layout);
364
374
  const rawFolders = isRecord(raw.folders) ? raw.folders : {};
365
375
  // compatibilidade retroativa para nome legado "pendencias"
@@ -92,8 +92,8 @@ export declare const SourceClassificationSchema: z.ZodEnum<{
92
92
  other: "other";
93
93
  }>;
94
94
  export declare const SourceVerificationStatusSchema: z.ZodEnum<{
95
- verified: "verified";
96
95
  stale: "stale";
96
+ verified: "verified";
97
97
  unverified: "unverified";
98
98
  needs_review: "needs_review";
99
99
  }>;
@@ -616,8 +616,8 @@ export declare const SourceDocumentRecordSchema: z.ZodObject<{
616
616
  jurisdiction_tags: z.ZodDefault<z.ZodArray<z.ZodString>>;
617
617
  last_verified_at: z.ZodOptional<z.ZodString>;
618
618
  verification_status: z.ZodDefault<z.ZodEnum<{
619
- verified: "verified";
620
619
  stale: "stale";
620
+ verified: "verified";
621
621
  unverified: "unverified";
622
622
  needs_review: "needs_review";
623
623
  }>>;
@@ -1235,8 +1235,8 @@ export declare const SourceIndexStateSchema: z.ZodObject<{
1235
1235
  jurisdiction_tags: z.ZodDefault<z.ZodArray<z.ZodString>>;
1236
1236
  last_verified_at: z.ZodOptional<z.ZodString>;
1237
1237
  verification_status: z.ZodDefault<z.ZodEnum<{
1238
- verified: "verified";
1239
1238
  stale: "stale";
1239
+ verified: "verified";
1240
1240
  unverified: "unverified";
1241
1241
  needs_review: "needs_review";
1242
1242
  }>>;
@@ -449,8 +449,8 @@ export declare const workspaceQualityLedgerSchema: z.ZodObject<{
449
449
  export declare const workspaceTokenTelemetrySchema: z.ZodObject<{
450
450
  mode: z.ZodOptional<z.ZodEnum<{
451
451
  standard: "standard";
452
- compact: "compact";
453
452
  full: "full";
453
+ compact: "compact";
454
454
  }>>;
455
455
  budget_chars: z.ZodOptional<z.ZodNumber>;
456
456
  actual_chars: z.ZodOptional<z.ZodNumber>;
@@ -470,8 +470,8 @@ export declare const workspaceTokenBudgetGatesSchema: z.ZodObject<{
470
470
  telemetry: z.ZodDefault<z.ZodArray<z.ZodObject<{
471
471
  mode: z.ZodOptional<z.ZodEnum<{
472
472
  standard: "standard";
473
- compact: "compact";
474
473
  full: "full";
474
+ compact: "compact";
475
475
  }>>;
476
476
  budget_chars: z.ZodOptional<z.ZodNumber>;
477
477
  actual_chars: z.ZodOptional<z.ZodNumber>;
@@ -553,6 +553,29 @@ export declare const workspaceRuntimeQualityGatesSchema: z.ZodObject<{
553
553
  notes: z.ZodOptional<z.ZodString>;
554
554
  }, z.core.$strip>>>;
555
555
  }, z.core.$strip>;
556
+ export declare const workspaceCriticalEvidenceProvenanceSchema: z.ZodObject<{
557
+ command: z.ZodString;
558
+ exit_code: z.ZodNumber;
559
+ timestamp_iso: z.ZodString;
560
+ commit_sha: z.ZodString;
561
+ artifact_path: z.ZodString;
562
+ artifact_hash: z.ZodString;
563
+ }, z.core.$strip>;
564
+ export declare const workspaceEvidenceLogEntrySchema: z.ZodObject<{
565
+ timestamp: z.ZodString;
566
+ kind: z.ZodString;
567
+ result: z.ZodString;
568
+ artifact: z.ZodOptional<z.ZodString>;
569
+ critical: z.ZodOptional<z.ZodBoolean>;
570
+ provenance: z.ZodOptional<z.ZodObject<{
571
+ command: z.ZodString;
572
+ exit_code: z.ZodNumber;
573
+ timestamp_iso: z.ZodString;
574
+ commit_sha: z.ZodString;
575
+ artifact_path: z.ZodString;
576
+ artifact_hash: z.ZodString;
577
+ }, z.core.$strip>>;
578
+ }, z.core.$strip>;
556
579
  export declare const workspaceQualitySchema: z.ZodObject<{
557
580
  schema_version: z.ZodLiteral<1>;
558
581
  feature_id: z.ZodString;
@@ -571,6 +594,15 @@ export declare const workspaceQualitySchema: z.ZodObject<{
571
594
  kind: z.ZodString;
572
595
  result: z.ZodString;
573
596
  artifact: z.ZodOptional<z.ZodString>;
597
+ critical: z.ZodOptional<z.ZodBoolean>;
598
+ provenance: z.ZodOptional<z.ZodObject<{
599
+ command: z.ZodString;
600
+ exit_code: z.ZodNumber;
601
+ timestamp_iso: z.ZodString;
602
+ commit_sha: z.ZodString;
603
+ artifact_path: z.ZodString;
604
+ artifact_hash: z.ZodString;
605
+ }, z.core.$strip>>;
574
606
  }, z.core.$strip>>;
575
607
  skill_evidence: z.ZodDefault<z.ZodObject<{
576
608
  required_skill_ids: z.ZodArray<z.ZodString>;
@@ -702,8 +734,8 @@ export declare const workspaceQualitySchema: z.ZodObject<{
702
734
  telemetry: z.ZodDefault<z.ZodArray<z.ZodObject<{
703
735
  mode: z.ZodOptional<z.ZodEnum<{
704
736
  standard: "standard";
705
- compact: "compact";
706
737
  full: "full";
738
+ compact: "compact";
707
739
  }>>;
708
740
  budget_chars: z.ZodOptional<z.ZodNumber>;
709
741
  actual_chars: z.ZodOptional<z.ZodNumber>;
@@ -1024,6 +1056,15 @@ export declare const workspaceDocumentSchemas: {
1024
1056
  kind: z.ZodString;
1025
1057
  result: z.ZodString;
1026
1058
  artifact: z.ZodOptional<z.ZodString>;
1059
+ critical: z.ZodOptional<z.ZodBoolean>;
1060
+ provenance: z.ZodOptional<z.ZodObject<{
1061
+ command: z.ZodString;
1062
+ exit_code: z.ZodNumber;
1063
+ timestamp_iso: z.ZodString;
1064
+ commit_sha: z.ZodString;
1065
+ artifact_path: z.ZodString;
1066
+ artifact_hash: z.ZodString;
1067
+ }, z.core.$strip>>;
1027
1068
  }, z.core.$strip>>;
1028
1069
  skill_evidence: z.ZodDefault<z.ZodObject<{
1029
1070
  required_skill_ids: z.ZodArray<z.ZodString>;
@@ -1155,8 +1196,8 @@ export declare const workspaceDocumentSchemas: {
1155
1196
  telemetry: z.ZodDefault<z.ZodArray<z.ZodObject<{
1156
1197
  mode: z.ZodOptional<z.ZodEnum<{
1157
1198
  standard: "standard";
1158
- compact: "compact";
1159
1199
  full: "full";
1200
+ compact: "compact";
1160
1201
  }>>;
1161
1202
  budget_chars: z.ZodOptional<z.ZodNumber>;
1162
1203
  actual_chars: z.ZodOptional<z.ZodNumber>;