oxe-cc 1.5.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cursor/commands/oxe-dashboard.md +2 -2
- package/.cursor/commands/oxe-execute.md +2 -2
- package/.cursor/commands/oxe-plan.md +2 -2
- package/.github/prompts/oxe-dashboard.prompt.md +2 -2
- package/.github/prompts/oxe-execute.prompt.md +2 -2
- package/.github/prompts/oxe-plan.prompt.md +2 -2
- package/AGENTS.md +1 -1
- package/CHANGELOG.md +52 -0
- package/README.md +17 -15
- package/bin/lib/oxe-context-engine.cjs +9 -4
- package/bin/lib/oxe-dashboard.cjs +140 -58
- package/bin/lib/oxe-project-health.cjs +486 -151
- package/bin/lib/oxe-rationality.cjs +385 -0
- package/bin/lib/oxe-release.cjs +76 -4
- package/bin/oxe-cc.js +113 -58
- package/commands/oxe/dashboard.md +2 -2
- package/commands/oxe/execute.md +2 -2
- package/commands/oxe/plan.md +2 -2
- package/docs/RELEASE-READINESS.md +8 -0
- package/docs/RUNTIME-SMOKE-MATRIX.md +9 -2
- package/lib/sdk/index.cjs +20 -11
- package/lib/sdk/index.d.ts +99 -34
- package/oxe/templates/CONFIG.md +3 -3
- package/oxe/templates/EXECUTION-RUNTIME.template.md +1 -1
- package/oxe/templates/FIXTURE-PACK.template.json +34 -0
- package/oxe/templates/FIXTURE-PACK.template.md +21 -0
- package/oxe/templates/IMPLEMENTATION-PACK.template.json +52 -0
- package/oxe/templates/IMPLEMENTATION-PACK.template.md +36 -0
- package/oxe/templates/INVESTIGATION.template.md +38 -38
- package/oxe/templates/PLAN.template.md +23 -14
- package/oxe/templates/REFERENCE-ANCHORS.template.md +24 -0
- package/oxe/templates/RESEARCH.template.md +11 -11
- package/oxe/templates/SPEC.template.md +6 -6
- package/oxe/templates/SUMMARY.template.md +20 -20
- package/oxe/templates/config.template.json +1 -1
- package/oxe/workflows/execute.md +18 -2
- package/oxe/workflows/milestone.md +12 -12
- package/oxe/workflows/next.md +1 -1
- package/oxe/workflows/plan.md +115 -57
- package/oxe/workflows/references/adaptive-discovery.md +27 -27
- package/oxe/workflows/references/flow-robustness-contract.md +80 -80
- package/oxe/workflows/references/session-path-resolution.md +71 -71
- package/oxe/workflows/references/workflow-runtime-contracts.json +36 -4
- package/oxe/workflows/verify.md +4 -4
- package/oxe/workflows/workstream.md +16 -16
- package/package.json +1 -1
- package/packages/runtime/package.json +1 -1
- package/vscode-extension/oxe-agents-1.5.1.vsix +0 -0
- package/vscode-extension/oxe-agents-1.6.0.vsix +0 -0
- package/vscode-extension/package.json +1 -1
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
|
|
5
|
+
function readTextIfExists(filePath) {
|
|
6
|
+
try {
|
|
7
|
+
return filePath && fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf8') : null;
|
|
8
|
+
} catch {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function readJsonIfExists(filePath) {
|
|
14
|
+
const text = readTextIfExists(filePath);
|
|
15
|
+
if (!text) return { ok: false, data: null, error: null };
|
|
16
|
+
try {
|
|
17
|
+
return { ok: true, data: JSON.parse(text), error: null };
|
|
18
|
+
} catch (error) {
|
|
19
|
+
return {
|
|
20
|
+
ok: false,
|
|
21
|
+
data: null,
|
|
22
|
+
error: error instanceof Error ? error.message : String(error),
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function parseAttrs(fragment) {
|
|
28
|
+
/** @type {Record<string, string>} */
|
|
29
|
+
const attrs = {};
|
|
30
|
+
const raw = String(fragment || '');
|
|
31
|
+
for (const match of raw.matchAll(/([A-Za-z0-9_:-]+)="([^"]*)"/g)) {
|
|
32
|
+
attrs[match[1]] = match[2];
|
|
33
|
+
}
|
|
34
|
+
return attrs;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function normalizeTaskMode(value) {
|
|
38
|
+
const mode = String(value || '').trim().toLowerCase();
|
|
39
|
+
if (!mode) return 'mutating';
|
|
40
|
+
return mode;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function isMutatingMode(mode) {
|
|
44
|
+
return !new Set(['docs_only', 'not_applicable', 'external', 'docs-only']).has(normalizeTaskMode(mode));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function isTaskRiskKeyword(task) {
|
|
48
|
+
const text = `${task.title || ''} ${task.body || ''}`.toLowerCase();
|
|
49
|
+
return /(parser|parse|layout|integra|integration|contrato|contract|migra|migration|fila|queue|service bus|event grid|builder|payload|transform|cnab|listener|schema|ddl)/.test(text);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* @param {string | null} planPath
|
|
54
|
+
* @returns {Array<{
|
|
55
|
+
* id: string,
|
|
56
|
+
* title: string,
|
|
57
|
+
* files: string[],
|
|
58
|
+
* complexity: string | null,
|
|
59
|
+
* body: string,
|
|
60
|
+
* }>}
|
|
61
|
+
*/
|
|
62
|
+
function parsePlanTasks(planPath) {
|
|
63
|
+
const raw = readTextIfExists(planPath);
|
|
64
|
+
if (!raw) return [];
|
|
65
|
+
const parts = raw.split(/^###\s+(T\d+)\s*[—-]?\s*/m);
|
|
66
|
+
if (parts.length < 3) return [];
|
|
67
|
+
/** @type {ReturnType<typeof parsePlanTasks>} */
|
|
68
|
+
const tasks = [];
|
|
69
|
+
for (let i = 1; i < parts.length; i += 2) {
|
|
70
|
+
const id = String(parts[i] || '').trim();
|
|
71
|
+
const rest = String(parts[i + 1] || '').split(/^###\s+T\d+/m)[0];
|
|
72
|
+
const title = (((rest.match(/^([^\n]+)/) || [null, ''])[1]) || '').trim();
|
|
73
|
+
const filesLine = (((rest.match(/\*\*Arquivos\s+prov[aá]veis:\*\*\s*([^\n]+)/i) || [null, ''])[1]) || '').trim();
|
|
74
|
+
const backtickPaths = Array.from(filesLine.matchAll(/`([^`]+)`/g)).map((match) => match[1].trim()).filter(Boolean);
|
|
75
|
+
const files = backtickPaths.length
|
|
76
|
+
? backtickPaths
|
|
77
|
+
: filesLine.split(',').map((entry) => entry.trim()).filter(Boolean);
|
|
78
|
+
const complexity = (((rest.match(/\*\*Complexidade:\*\*\s*([A-Z]+)/i) || [null, ''])[1]) || '').trim() || null;
|
|
79
|
+
tasks.push({ id, title, files, complexity, body: rest });
|
|
80
|
+
}
|
|
81
|
+
return tasks;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* @param {string | null} planAgentsPath
|
|
86
|
+
* @returns {string[]}
|
|
87
|
+
*/
|
|
88
|
+
function readExternalRefs(planAgentsPath) {
|
|
89
|
+
const parsed = readJsonIfExists(planAgentsPath);
|
|
90
|
+
if (!parsed.ok || !parsed.data || typeof parsed.data !== 'object') return [];
|
|
91
|
+
const agents = Array.isArray(parsed.data.agents) ? parsed.data.agents : [];
|
|
92
|
+
return agents
|
|
93
|
+
.flatMap((agent) => Array.isArray(agent && agent.inputs) ? agent.inputs : [])
|
|
94
|
+
.map((input) => String(input || '').trim())
|
|
95
|
+
.filter((input) => /^external-ref:/i.test(input));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function summarizeImplementationPack(filePath, planTasks) {
|
|
99
|
+
const parsed = readJsonIfExists(filePath);
|
|
100
|
+
/** @type {string[]} */
|
|
101
|
+
const criticalGaps = [];
|
|
102
|
+
if (!parsed.ok) {
|
|
103
|
+
return {
|
|
104
|
+
path: filePath,
|
|
105
|
+
exists: Boolean(readTextIfExists(filePath)),
|
|
106
|
+
parseError: parsed.error,
|
|
107
|
+
ready: false,
|
|
108
|
+
tasks: [],
|
|
109
|
+
taskCount: 0,
|
|
110
|
+
mutatingTasks: 0,
|
|
111
|
+
criticalGaps: [`IMPLEMENTATION-PACK.json inválido ou ausente${parsed.error ? `: ${parsed.error}` : ''}`],
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
const data = parsed.data && typeof parsed.data === 'object' ? parsed.data : {};
|
|
115
|
+
const tasks = Array.isArray(data.tasks) ? data.tasks : [];
|
|
116
|
+
const byId = new Map(tasks.map((task) => [String(task && task.id || task && task.task_id || '').trim(), task]).filter((entry) => entry[0]));
|
|
117
|
+
const allowedModes = new Set(['mutating', 'docs_only', 'not_applicable', 'external', 'docs-only']);
|
|
118
|
+
for (const planTask of planTasks) {
|
|
119
|
+
if (!byId.has(planTask.id)) {
|
|
120
|
+
criticalGaps.push(`IMPLEMENTATION-PACK.json sem contrato para ${planTask.id}`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
let mutatingTasks = 0;
|
|
124
|
+
for (const task of tasks) {
|
|
125
|
+
const id = String(task && task.id || task && task.task_id || '').trim();
|
|
126
|
+
const mode = normalizeTaskMode(task && task.mode);
|
|
127
|
+
const exactPaths = Array.isArray(task && task.exact_paths) ? task.exact_paths.map((entry) => String(entry || '').trim()).filter(Boolean) : [];
|
|
128
|
+
const symbols = Array.isArray(task && task.symbols) ? task.symbols : [];
|
|
129
|
+
const contracts = Array.isArray(task && task.contracts) ? task.contracts : [];
|
|
130
|
+
const expectedChecks = Array.isArray(task && task.expected_checks) ? task.expected_checks : [];
|
|
131
|
+
const gaps = Array.isArray(task && task.critical_gaps) ? task.critical_gaps : [];
|
|
132
|
+
if (!id) {
|
|
133
|
+
criticalGaps.push('IMPLEMENTATION-PACK.json com task sem id');
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
if (!allowedModes.has(mode)) {
|
|
137
|
+
criticalGaps.push(`IMPLEMENTATION-PACK.json tarefa ${id} com mode inválido: ${mode}`);
|
|
138
|
+
}
|
|
139
|
+
if (task && task.ready === false) {
|
|
140
|
+
criticalGaps.push(`IMPLEMENTATION-PACK.json tarefa ${id} marcada como not ready`);
|
|
141
|
+
}
|
|
142
|
+
if (gaps.length) {
|
|
143
|
+
criticalGaps.push(`IMPLEMENTATION-PACK.json tarefa ${id} com critical_gaps abertos`);
|
|
144
|
+
}
|
|
145
|
+
if (isMutatingMode(mode)) {
|
|
146
|
+
mutatingTasks += 1;
|
|
147
|
+
if (!exactPaths.length) {
|
|
148
|
+
criticalGaps.push(`IMPLEMENTATION-PACK.json tarefa ${id} sem exact_paths`);
|
|
149
|
+
}
|
|
150
|
+
if (exactPaths.some((entry) => entry.includes('...'))) {
|
|
151
|
+
criticalGaps.push(`IMPLEMENTATION-PACK.json tarefa ${id} com exact_paths ambíguos (...)`);
|
|
152
|
+
}
|
|
153
|
+
if (String(task && task.write_set || '').trim().toLowerCase() !== 'closed') {
|
|
154
|
+
criticalGaps.push(`IMPLEMENTATION-PACK.json tarefa ${id} com write_set aberto`);
|
|
155
|
+
}
|
|
156
|
+
if (!symbols.length) {
|
|
157
|
+
criticalGaps.push(`IMPLEMENTATION-PACK.json tarefa ${id} sem symbols`);
|
|
158
|
+
}
|
|
159
|
+
if (!contracts.length) {
|
|
160
|
+
criticalGaps.push(`IMPLEMENTATION-PACK.json tarefa ${id} sem contracts`);
|
|
161
|
+
}
|
|
162
|
+
if (!expectedChecks.length) {
|
|
163
|
+
criticalGaps.push(`IMPLEMENTATION-PACK.json tarefa ${id} sem expected_checks`);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
if (data.ready === false) {
|
|
168
|
+
criticalGaps.push('IMPLEMENTATION-PACK.json com ready=false');
|
|
169
|
+
}
|
|
170
|
+
if (Array.isArray(data.critical_gaps) && data.critical_gaps.length) {
|
|
171
|
+
criticalGaps.push(...data.critical_gaps.map((gap) => `IMPLEMENTATION-PACK.json: ${String(gap)}`));
|
|
172
|
+
}
|
|
173
|
+
return {
|
|
174
|
+
path: filePath,
|
|
175
|
+
exists: true,
|
|
176
|
+
parseError: null,
|
|
177
|
+
ready: Boolean(data.ready) && criticalGaps.length === 0,
|
|
178
|
+
tasks,
|
|
179
|
+
taskCount: tasks.length,
|
|
180
|
+
mutatingTasks,
|
|
181
|
+
criticalGaps: Array.from(new Set(criticalGaps)),
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function summarizeReferenceAnchors(filePath, externalRefs) {
|
|
186
|
+
const raw = readTextIfExists(filePath);
|
|
187
|
+
/** @type {string[]} */
|
|
188
|
+
const criticalGaps = [];
|
|
189
|
+
if (!raw) {
|
|
190
|
+
return {
|
|
191
|
+
path: filePath,
|
|
192
|
+
exists: false,
|
|
193
|
+
ready: false,
|
|
194
|
+
anchors: [],
|
|
195
|
+
missingCriticalCount: externalRefs.length,
|
|
196
|
+
staleCount: 0,
|
|
197
|
+
criticalGaps: ['REFERENCE-ANCHORS.md ausente'],
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
const rootMatch = raw.match(/<reference_anchors\b([^>]*)>([\s\S]*?)<\/reference_anchors>/i);
|
|
201
|
+
if (!rootMatch) {
|
|
202
|
+
return {
|
|
203
|
+
path: filePath,
|
|
204
|
+
exists: true,
|
|
205
|
+
ready: false,
|
|
206
|
+
anchors: [],
|
|
207
|
+
missingCriticalCount: externalRefs.length,
|
|
208
|
+
staleCount: 0,
|
|
209
|
+
criticalGaps: ['REFERENCE-ANCHORS.md sem bloco <reference_anchors>'],
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
const rootAttrs = parseAttrs(rootMatch[1]);
|
|
213
|
+
const status = String(rootAttrs.status || '').toLowerCase();
|
|
214
|
+
/** @type {Array<Record<string, unknown>>} */
|
|
215
|
+
const anchors = [];
|
|
216
|
+
for (const match of rootMatch[2].matchAll(/<anchor\b([^>]*)>([\s\S]*?)<\/anchor>/gi)) {
|
|
217
|
+
const attrs = parseAttrs(match[1]);
|
|
218
|
+
anchors.push({
|
|
219
|
+
id: attrs.id || null,
|
|
220
|
+
task: attrs.task || null,
|
|
221
|
+
critical: String(attrs.critical || '').toLowerCase() === 'true',
|
|
222
|
+
status: String(attrs.status || 'missing').toLowerCase(),
|
|
223
|
+
sourceType: attrs.source_type || null,
|
|
224
|
+
path: attrs.path || null,
|
|
225
|
+
sourceRef: attrs.source_ref || null,
|
|
226
|
+
relevance: (((match[2].match(/<relevance>([\s\S]*?)<\/relevance>/i) || [null, ''])[1]) || '').trim(),
|
|
227
|
+
action: (((match[2].match(/<action>([\s\S]*?)<\/action>/i) || [null, ''])[1]) || '').trim(),
|
|
228
|
+
summary: (((match[2].match(/<summary>([\s\S]*?)<\/summary>/i) || [null, ''])[1]) || '').trim(),
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
if (status === 'not_applicable' && externalRefs.length) {
|
|
232
|
+
criticalGaps.push('REFERENCE-ANCHORS.md marcado como not_applicable, mas há external-ref no blueprint');
|
|
233
|
+
}
|
|
234
|
+
let missingCriticalCount = 0;
|
|
235
|
+
let staleCount = 0;
|
|
236
|
+
for (const anchor of anchors) {
|
|
237
|
+
const anchorStatus = String(anchor.status || 'missing').toLowerCase();
|
|
238
|
+
if (anchorStatus === 'stale') staleCount += 1;
|
|
239
|
+
if (anchor.critical && anchorStatus !== 'resolved') {
|
|
240
|
+
missingCriticalCount += 1;
|
|
241
|
+
criticalGaps.push(`REFERENCE-ANCHORS.md âncora crítica ${anchor.id || anchor.sourceRef || '?'} em estado ${anchorStatus}`);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
for (const ref of externalRefs) {
|
|
245
|
+
const matched = anchors.find((anchor) => String(anchor.sourceRef || '').trim() === ref);
|
|
246
|
+
if (!matched) {
|
|
247
|
+
missingCriticalCount += 1;
|
|
248
|
+
criticalGaps.push(`REFERENCE-ANCHORS.md sem âncora materializada para ${ref}`);
|
|
249
|
+
} else if (String(matched.status || '').toLowerCase() !== 'resolved') {
|
|
250
|
+
missingCriticalCount += 1;
|
|
251
|
+
criticalGaps.push(`REFERENCE-ANCHORS.md com ${ref} em estado ${matched.status}`);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
if (String(rootAttrs.ready || '').toLowerCase() === 'false') {
|
|
255
|
+
criticalGaps.push('REFERENCE-ANCHORS.md com ready=false');
|
|
256
|
+
}
|
|
257
|
+
return {
|
|
258
|
+
path: filePath,
|
|
259
|
+
exists: true,
|
|
260
|
+
ready: status === 'not_applicable'
|
|
261
|
+
? criticalGaps.length === 0
|
|
262
|
+
: Boolean(String(rootAttrs.ready || '').toLowerCase() !== 'false') && criticalGaps.length === 0,
|
|
263
|
+
anchors,
|
|
264
|
+
missingCriticalCount,
|
|
265
|
+
staleCount,
|
|
266
|
+
criticalGaps: Array.from(new Set(criticalGaps)),
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function summarizeFixturePack(filePath, planTasks, implementationPack) {
|
|
271
|
+
const parsed = readJsonIfExists(filePath);
|
|
272
|
+
/** @type {string[]} */
|
|
273
|
+
const criticalGaps = [];
|
|
274
|
+
if (!parsed.ok) {
|
|
275
|
+
return {
|
|
276
|
+
path: filePath,
|
|
277
|
+
exists: Boolean(readTextIfExists(filePath)),
|
|
278
|
+
parseError: parsed.error,
|
|
279
|
+
ready: false,
|
|
280
|
+
fixtures: [],
|
|
281
|
+
fixtureCount: 0,
|
|
282
|
+
criticalGaps: [`FIXTURE-PACK.json inválido ou ausente${parsed.error ? `: ${parsed.error}` : ''}`],
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
const data = parsed.data && typeof parsed.data === 'object' ? parsed.data : {};
|
|
286
|
+
const fixtures = Array.isArray(data.fixtures) ? data.fixtures : [];
|
|
287
|
+
const byTask = new Map();
|
|
288
|
+
for (const fixture of fixtures) {
|
|
289
|
+
const taskId = String(fixture && fixture.task_id || fixture && fixture.taskId || '').trim();
|
|
290
|
+
if (!taskId) continue;
|
|
291
|
+
if (!byTask.has(taskId)) byTask.set(taskId, []);
|
|
292
|
+
byTask.get(taskId).push(fixture);
|
|
293
|
+
}
|
|
294
|
+
if (data.ready === false) {
|
|
295
|
+
criticalGaps.push('FIXTURE-PACK.json com ready=false');
|
|
296
|
+
}
|
|
297
|
+
if (Array.isArray(data.critical_gaps) && data.critical_gaps.length) {
|
|
298
|
+
criticalGaps.push(...data.critical_gaps.map((gap) => `FIXTURE-PACK.json: ${String(gap)}`));
|
|
299
|
+
}
|
|
300
|
+
const implementationTasks = Array.isArray(implementationPack.tasks) ? implementationPack.tasks : [];
|
|
301
|
+
for (const task of implementationTasks) {
|
|
302
|
+
const taskId = String(task && task.id || task && task.task_id || '').trim();
|
|
303
|
+
if (!taskId) continue;
|
|
304
|
+
const planTask = planTasks.find((candidate) => candidate.id === taskId);
|
|
305
|
+
const requiresFixture = task && task.requires_fixture === true
|
|
306
|
+
|| (planTask ? isTaskRiskKeyword(planTask) : false);
|
|
307
|
+
if (!requiresFixture) continue;
|
|
308
|
+
const entries = byTask.get(taskId) || [];
|
|
309
|
+
if (!entries.length) {
|
|
310
|
+
criticalGaps.push(`FIXTURE-PACK.json sem fixture para ${taskId}`);
|
|
311
|
+
continue;
|
|
312
|
+
}
|
|
313
|
+
const readyFixture = entries.find((entry) => String(entry && entry.status || '').toLowerCase() === 'ready');
|
|
314
|
+
if (!readyFixture) {
|
|
315
|
+
criticalGaps.push(`FIXTURE-PACK.json sem fixture ready para ${taskId}`);
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
if (!Array.isArray(readyFixture.expected_checks) || readyFixture.expected_checks.length === 0) {
|
|
319
|
+
criticalGaps.push(`FIXTURE-PACK.json fixture de ${taskId} sem expected_checks`);
|
|
320
|
+
}
|
|
321
|
+
if (Array.isArray(readyFixture.critical_gaps) && readyFixture.critical_gaps.length) {
|
|
322
|
+
criticalGaps.push(`FIXTURE-PACK.json fixture de ${taskId} com critical_gaps abertos`);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
return {
|
|
326
|
+
path: filePath,
|
|
327
|
+
exists: true,
|
|
328
|
+
parseError: null,
|
|
329
|
+
ready: Boolean(data.ready) && criticalGaps.length === 0,
|
|
330
|
+
fixtures,
|
|
331
|
+
fixtureCount: fixtures.length,
|
|
332
|
+
criticalGaps: Array.from(new Set(criticalGaps)),
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* @param {{
|
|
338
|
+
* plan?: string | null,
|
|
339
|
+
* planAgents?: string | null,
|
|
340
|
+
* implementationPackJson?: string | null,
|
|
341
|
+
* implementationPackMd?: string | null,
|
|
342
|
+
* referenceAnchors?: string | null,
|
|
343
|
+
* fixturePackJson?: string | null,
|
|
344
|
+
* fixturePackMd?: string | null,
|
|
345
|
+
* }} paths
|
|
346
|
+
*/
|
|
347
|
+
function buildExecutionRationality(paths = {}) {
|
|
348
|
+
const planTasks = parsePlanTasks(paths.plan || null);
|
|
349
|
+
const externalRefs = readExternalRefs(paths.planAgents || null);
|
|
350
|
+
const implementationPack = summarizeImplementationPack(paths.implementationPackJson || null, planTasks);
|
|
351
|
+
const referenceAnchors = summarizeReferenceAnchors(paths.referenceAnchors || null, externalRefs);
|
|
352
|
+
const fixturePack = summarizeFixturePack(paths.fixturePackJson || null, planTasks, implementationPack);
|
|
353
|
+
const criticalExecutionGaps = Array.from(
|
|
354
|
+
new Set([
|
|
355
|
+
...implementationPack.criticalGaps,
|
|
356
|
+
...referenceAnchors.criticalGaps,
|
|
357
|
+
...fixturePack.criticalGaps,
|
|
358
|
+
])
|
|
359
|
+
);
|
|
360
|
+
const applicable = Boolean(paths.plan && fs.existsSync(paths.plan));
|
|
361
|
+
return {
|
|
362
|
+
applicable,
|
|
363
|
+
planTaskCount: planTasks.length,
|
|
364
|
+
externalReferenceCount: externalRefs.length,
|
|
365
|
+
implementationPackReady: applicable ? implementationPack.ready : false,
|
|
366
|
+
referenceAnchorsReady: applicable ? referenceAnchors.ready : false,
|
|
367
|
+
fixturePackReady: applicable ? fixturePack.ready : false,
|
|
368
|
+
executionRationalityReady: applicable
|
|
369
|
+
? implementationPack.ready && referenceAnchors.ready && fixturePack.ready && criticalExecutionGaps.length === 0
|
|
370
|
+
: false,
|
|
371
|
+
criticalExecutionGaps,
|
|
372
|
+
implementationPack,
|
|
373
|
+
referenceAnchors,
|
|
374
|
+
fixturePack,
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
module.exports = {
|
|
379
|
+
buildExecutionRationality,
|
|
380
|
+
parsePlanTasks,
|
|
381
|
+
readExternalRefs,
|
|
382
|
+
summarizeImplementationPack,
|
|
383
|
+
summarizeReferenceAnchors,
|
|
384
|
+
summarizeFixturePack,
|
|
385
|
+
};
|
package/bin/lib/oxe-release.cjs
CHANGED
|
@@ -5,6 +5,7 @@ const path = require('path');
|
|
|
5
5
|
const { spawnSync } = require('child_process');
|
|
6
6
|
|
|
7
7
|
const oxeManifest = require('./oxe-manifest.cjs');
|
|
8
|
+
const runtimeSemantics = require('./oxe-runtime-semantics.cjs');
|
|
8
9
|
|
|
9
10
|
const REQUIRED_RUNTIMES = [
|
|
10
11
|
'cursor',
|
|
@@ -269,6 +270,7 @@ function loadRuntimeSmokeReport(projectRoot) {
|
|
|
269
270
|
&& item.oxe_present
|
|
270
271
|
&& item.workflow_resolution_ok
|
|
271
272
|
&& item.wrapper_drift_ok !== false
|
|
273
|
+
&& item.extra_checks_ok !== false
|
|
272
274
|
&& item.uninstall_ok
|
|
273
275
|
);
|
|
274
276
|
});
|
|
@@ -301,11 +303,42 @@ function readVersionSnapshot(projectRoot) {
|
|
|
301
303
|
};
|
|
302
304
|
}
|
|
303
305
|
|
|
306
|
+
function inspectCanonicalSource(projectRoot) {
|
|
307
|
+
const workflowsDir = path.join(projectRoot, 'oxe', 'workflows');
|
|
308
|
+
const referencesDir = path.join(workflowsDir, 'references');
|
|
309
|
+
const contractsPath = path.join(referencesDir, 'workflow-runtime-contracts.json');
|
|
310
|
+
const commandsDir = path.join(projectRoot, 'commands', 'oxe');
|
|
311
|
+
const workflowFiles = fs.existsSync(workflowsDir)
|
|
312
|
+
? fs.readdirSync(workflowsDir).filter((name) => name.endsWith('.md'))
|
|
313
|
+
: [];
|
|
314
|
+
const registryIssues = runtimeSemantics.validateWorkflowContractsRegistry();
|
|
315
|
+
return {
|
|
316
|
+
workflowsDir,
|
|
317
|
+
referencesDir,
|
|
318
|
+
contractsPath,
|
|
319
|
+
commandsDir,
|
|
320
|
+
workflowsPresent: fs.existsSync(workflowsDir),
|
|
321
|
+
referencesPresent: fs.existsSync(referencesDir),
|
|
322
|
+
commandsPresent: fs.existsSync(commandsDir),
|
|
323
|
+
contractsPresent: fs.existsSync(contractsPath),
|
|
324
|
+
workflowCount: workflowFiles.length,
|
|
325
|
+
contractVersion: runtimeSemantics.CONTRACT_VERSION,
|
|
326
|
+
registryIssues,
|
|
327
|
+
ok: fs.existsSync(workflowsDir)
|
|
328
|
+
&& fs.existsSync(referencesDir)
|
|
329
|
+
&& fs.existsSync(commandsDir)
|
|
330
|
+
&& fs.existsSync(contractsPath)
|
|
331
|
+
&& workflowFiles.length > 0
|
|
332
|
+
&& registryIssues.length === 0,
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
|
|
304
336
|
function buildReleaseManifest(projectRoot, options = {}) {
|
|
305
337
|
const packageRoot = path.resolve(options.packageRoot || projectRoot);
|
|
306
338
|
const paths = releasePaths(projectRoot);
|
|
307
339
|
const versions = readVersionSnapshot(projectRoot);
|
|
308
340
|
const runtimeEntry = path.join(packageRoot, 'lib', 'runtime', 'index.js');
|
|
341
|
+
const canonicalSource = inspectCanonicalSource(projectRoot);
|
|
309
342
|
const wrapperSync = options.skipWrapperSync ? {
|
|
310
343
|
before: collectWrapperHashes(projectRoot),
|
|
311
344
|
after: collectWrapperHashes(projectRoot),
|
|
@@ -313,6 +346,7 @@ function buildReleaseManifest(projectRoot, options = {}) {
|
|
|
313
346
|
mismatches: [],
|
|
314
347
|
ok: true,
|
|
315
348
|
} : syncWrappers(projectRoot, packageRoot);
|
|
349
|
+
const semanticsAudit = runtimeSemantics.auditRuntimeTargets(projectRoot);
|
|
316
350
|
const smoke = loadRuntimeSmokeReport(projectRoot);
|
|
317
351
|
const recovery = loadRecoveryFixtureReport(projectRoot);
|
|
318
352
|
const multiAgent = loadMultiAgentSoakReport(projectRoot);
|
|
@@ -332,6 +366,12 @@ function buildReleaseManifest(projectRoot, options = {}) {
|
|
|
332
366
|
path: runtimeEntry,
|
|
333
367
|
ok: fs.existsSync(runtimeEntry),
|
|
334
368
|
},
|
|
369
|
+
canonical_source: canonicalSource,
|
|
370
|
+
semantics: {
|
|
371
|
+
contractVersion: runtimeSemantics.CONTRACT_VERSION,
|
|
372
|
+
registryPath: runtimeSemantics.CONTRACTS_PATH,
|
|
373
|
+
audit: semanticsAudit,
|
|
374
|
+
},
|
|
335
375
|
wrappers: {
|
|
336
376
|
hash_before_sync: wrapperSync.before,
|
|
337
377
|
hash_after_sync: wrapperSync.after,
|
|
@@ -354,8 +394,8 @@ function buildReleaseManifest(projectRoot, options = {}) {
|
|
|
354
394
|
return manifest;
|
|
355
395
|
}
|
|
356
396
|
|
|
357
|
-
function
|
|
358
|
-
const
|
|
397
|
+
function evaluateReleaseManifest(manifest, options = {}) {
|
|
398
|
+
const enforceSync = options.enforceSync !== false;
|
|
359
399
|
const blockers = [];
|
|
360
400
|
const warnings = [];
|
|
361
401
|
const versions = manifest.versions;
|
|
@@ -378,10 +418,22 @@ function checkReleaseConsistency(projectRoot, options = {}) {
|
|
|
378
418
|
if (!versions.changelog.date) blockers.push('CHANGELOG.md topo sem data');
|
|
379
419
|
if (!versions.changelog.hasHighlights) blockers.push('CHANGELOG.md topo sem highlights');
|
|
380
420
|
}
|
|
421
|
+
const canonical = manifest.canonical_source || {};
|
|
422
|
+
if (!canonical.ok) {
|
|
423
|
+
blockers.push('árvore canónica ausente ou inválida');
|
|
424
|
+
}
|
|
425
|
+
if (!canonical.workflowsPresent) blockers.push('oxe/workflows ausente');
|
|
426
|
+
if (!canonical.referencesPresent) blockers.push('oxe/workflows/references ausente');
|
|
427
|
+
if (!canonical.commandsPresent) blockers.push('commands/oxe ausente');
|
|
428
|
+
if (!canonical.contractsPresent) blockers.push('workflow-runtime-contracts.json ausente');
|
|
429
|
+
if (canonical.workflowCount === 0) blockers.push('oxe/workflows sem workflows canónicos');
|
|
430
|
+
if (Array.isArray(canonical.registryIssues) && canonical.registryIssues.length) {
|
|
431
|
+
blockers.push('workflow-runtime-contracts.json inválido');
|
|
432
|
+
}
|
|
381
433
|
if (!manifest.runtime_compiled.ok) {
|
|
382
434
|
blockers.push('runtime não compilado (lib/runtime/index.js ausente)');
|
|
383
435
|
}
|
|
384
|
-
if (!manifest.wrappers.sync.ok) {
|
|
436
|
+
if (enforceSync && !manifest.wrappers.sync.ok) {
|
|
385
437
|
if (manifest.wrappers.sync.scripts.some((entry) => !entry.ok)) {
|
|
386
438
|
blockers.push('sync de wrappers falhou');
|
|
387
439
|
}
|
|
@@ -389,6 +441,10 @@ function checkReleaseConsistency(projectRoot, options = {}) {
|
|
|
389
441
|
blockers.push('wrappers ficam dirty após sync-runtime-metadata/sync:cursor');
|
|
390
442
|
}
|
|
391
443
|
}
|
|
444
|
+
const semanticsAudit = manifest.semantics && manifest.semantics.audit;
|
|
445
|
+
if (!semanticsAudit || semanticsAudit.ok !== true) {
|
|
446
|
+
blockers.push('drift semântico entre workflows e wrappers');
|
|
447
|
+
}
|
|
392
448
|
if (!manifest.reports.runtime_smoke.present || !manifest.reports.runtime_smoke.ok) {
|
|
393
449
|
blockers.push('runtime smoke matrix incompleta ou com falhas');
|
|
394
450
|
}
|
|
@@ -406,10 +462,23 @@ function checkReleaseConsistency(projectRoot, options = {}) {
|
|
|
406
462
|
blockers,
|
|
407
463
|
warnings,
|
|
408
464
|
manifest,
|
|
409
|
-
manifestPath: releasePaths(
|
|
465
|
+
manifestPath: releasePaths(manifest.project_root).manifest,
|
|
410
466
|
};
|
|
411
467
|
}
|
|
412
468
|
|
|
469
|
+
function checkReleaseConsistency(projectRoot, options = {}) {
|
|
470
|
+
const manifest = buildReleaseManifest(projectRoot, options);
|
|
471
|
+
return evaluateReleaseManifest(manifest, { enforceSync: true });
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
function inspectReleaseReadiness(projectRoot, options = {}) {
|
|
475
|
+
const manifest = buildReleaseManifest(projectRoot, {
|
|
476
|
+
...options,
|
|
477
|
+
skipWrapperSync: true,
|
|
478
|
+
});
|
|
479
|
+
return evaluateReleaseManifest(manifest, { enforceSync: false });
|
|
480
|
+
}
|
|
481
|
+
|
|
413
482
|
module.exports = {
|
|
414
483
|
REQUIRED_RUNTIMES,
|
|
415
484
|
WRAPPER_TARGETS,
|
|
@@ -419,5 +488,8 @@ module.exports = {
|
|
|
419
488
|
loadRecoveryFixtureReport,
|
|
420
489
|
loadMultiAgentSoakReport,
|
|
421
490
|
buildReleaseManifest,
|
|
491
|
+
inspectCanonicalSource,
|
|
492
|
+
evaluateReleaseManifest,
|
|
493
|
+
inspectReleaseReadiness,
|
|
422
494
|
checkReleaseConsistency,
|
|
423
495
|
};
|