oxe-cc 1.4.1 → 1.5.1
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/.cursor/commands/oxe-verify-audit.md +46 -0
- package/.cursor/commands/oxe-workflow-authoring.md +47 -0
- package/.github/prompts/oxe-compact.prompt.md +1 -1
- package/.github/prompts/oxe-dashboard.prompt.md +2 -2
- package/.github/prompts/oxe-execute.prompt.md +2 -2
- package/.github/prompts/oxe-plan-agent.prompt.md +1 -0
- package/.github/prompts/oxe-plan.prompt.md +2 -2
- package/.github/prompts/oxe-verify-audit.prompt.md +46 -0
- package/.github/prompts/oxe-workflow-authoring.prompt.md +47 -0
- package/.github/workflows/ci.yml +1 -0
- package/.github/workflows/release.yml +1 -0
- package/AGENTS.md +3 -1
- package/CHANGELOG.md +50 -0
- package/QUICKSTART.md +99 -0
- package/README.md +20 -11
- package/bin/lib/oxe-context-engine.cjs +9 -4
- package/bin/lib/oxe-dashboard.cjs +119 -53
- package/bin/lib/oxe-install-resolve.cjs +10 -0
- package/bin/lib/oxe-operational.cjs +34 -28
- package/bin/lib/oxe-project-health.cjs +407 -118
- package/bin/lib/oxe-rationality.cjs +385 -0
- package/bin/lib/oxe-release.cjs +423 -0
- package/bin/oxe-cc.js +446 -325
- package/commands/oxe/dashboard.md +2 -2
- package/commands/oxe/execute.md +2 -2
- package/commands/oxe/plan.md +2 -2
- package/commands/oxe/verify-audit.md +50 -0
- package/commands/oxe/workflow-authoring.md +50 -0
- package/docs/INCIDENT-PLAYBOOK.md +181 -0
- package/docs/RELEASE-READINESS.md +46 -0
- package/docs/ROLES.md +129 -0
- package/docs/RUNTIME-SMOKE-MATRIX.md +128 -0
- package/docs/TEAM-ADOPTION.md +153 -0
- package/docs/WALKTHROUGH.md +241 -0
- package/lib/runtime/scheduler/multi-agent-coordinator.d.ts +28 -0
- package/lib/runtime/scheduler/multi-agent-coordinator.js +152 -26
- package/lib/sdk/README.md +2 -0
- package/lib/sdk/index.cjs +32 -14
- package/lib/sdk/index.d.ts +138 -40
- package/oxe/templates/CONFIG.md +1 -1
- 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/PLAN.template.md +46 -37
- package/oxe/templates/REFERENCE-ANCHORS.template.md +24 -0
- package/oxe/templates/config.template.json +2 -1
- package/oxe/workflows/execute.md +36 -20
- package/oxe/workflows/next.md +1 -1
- package/oxe/workflows/plan.md +80 -22
- package/oxe/workflows/references/flow-robustness-contract.md +3 -3
- package/oxe/workflows/references/workflow-runtime-contracts.json +127 -95
- package/oxe/workflows/verify.md +4 -4
- package/package.json +28 -20
- package/packages/runtime/package.json +1 -1
- package/packages/runtime/src/scheduler/multi-agent-coordinator.ts +357 -193
- package/vscode-extension/oxe-agents-1.5.0.vsix +0 -0
- package/vscode-extension/oxe-agents-1.5.1.vsix +0 -0
- package/vscode-extension/package.json +1 -1
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const { spawnSync } = require('child_process');
|
|
6
|
+
|
|
7
|
+
const oxeManifest = require('./oxe-manifest.cjs');
|
|
8
|
+
|
|
9
|
+
const REQUIRED_RUNTIMES = [
|
|
10
|
+
'cursor',
|
|
11
|
+
'copilot_vscode',
|
|
12
|
+
'claude_code',
|
|
13
|
+
'codex',
|
|
14
|
+
'opencode',
|
|
15
|
+
'gemini',
|
|
16
|
+
'windsurf',
|
|
17
|
+
'antigravity',
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
const WRAPPER_TARGETS = [
|
|
21
|
+
{
|
|
22
|
+
key: '.github/prompts',
|
|
23
|
+
dir: '.github/prompts',
|
|
24
|
+
filter: (name) => (name === 'oxe.prompt.md' || name.startsWith('oxe-')) && name.endsWith('.prompt.md'),
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
key: 'commands/oxe',
|
|
28
|
+
dir: 'commands/oxe',
|
|
29
|
+
filter: (name) => name.endsWith('.md'),
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
key: '.cursor/commands',
|
|
33
|
+
dir: '.cursor/commands',
|
|
34
|
+
filter: (name) => (name === 'oxe.md' || name.startsWith('oxe-')) && name.endsWith('.md'),
|
|
35
|
+
},
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
function releasePaths(projectRoot) {
|
|
39
|
+
const releaseDir = path.join(projectRoot, '.oxe', 'release');
|
|
40
|
+
return {
|
|
41
|
+
releaseDir,
|
|
42
|
+
manifest: path.join(releaseDir, 'release-manifest.json'),
|
|
43
|
+
smokeReport: path.join(releaseDir, 'runtime-smoke-report.json'),
|
|
44
|
+
recoveryFixtureReport: path.join(releaseDir, 'recovery-fixture-report.json'),
|
|
45
|
+
multiAgentSoakReport: path.join(releaseDir, 'multi-agent-soak-report.json'),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function ensureDirForFile(filePath) {
|
|
50
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function readJsonIfExists(filePath) {
|
|
54
|
+
try {
|
|
55
|
+
if (!fs.existsSync(filePath)) return null;
|
|
56
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
57
|
+
} catch {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function readTextIfExists(filePath) {
|
|
63
|
+
try {
|
|
64
|
+
if (!fs.existsSync(filePath)) return null;
|
|
65
|
+
return fs.readFileSync(filePath, 'utf8');
|
|
66
|
+
} catch {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function readPackageVersion(filePath) {
|
|
72
|
+
const json = readJsonIfExists(filePath);
|
|
73
|
+
if (!json || typeof json.version !== 'string') return null;
|
|
74
|
+
return json.version.trim();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function readReadmeVersion(filePath) {
|
|
78
|
+
const text = readTextIfExists(filePath);
|
|
79
|
+
if (!text) return null;
|
|
80
|
+
const match = text.match(/\*\*Versão:\*\*\s*`([^`]+)`/i);
|
|
81
|
+
return match ? match[1].trim() : null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function parseChangelogTop(filePath) {
|
|
85
|
+
const text = readTextIfExists(filePath);
|
|
86
|
+
if (!text) {
|
|
87
|
+
return {
|
|
88
|
+
path: filePath,
|
|
89
|
+
version: null,
|
|
90
|
+
date: null,
|
|
91
|
+
headerLine: null,
|
|
92
|
+
hasHighlights: false,
|
|
93
|
+
highlights: [],
|
|
94
|
+
ok: false,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
const match = text.match(/^## \[([^\]]+)\]\s+—\s+(\d{4}-\d{2}-\d{2})\s*$/m);
|
|
98
|
+
if (!match) {
|
|
99
|
+
return {
|
|
100
|
+
path: filePath,
|
|
101
|
+
version: null,
|
|
102
|
+
date: null,
|
|
103
|
+
headerLine: null,
|
|
104
|
+
hasHighlights: false,
|
|
105
|
+
highlights: [],
|
|
106
|
+
ok: false,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
const headerLine = match[0];
|
|
110
|
+
const start = match.index + headerLine.length;
|
|
111
|
+
const nextHeader = text.slice(start).match(/\n## \[/);
|
|
112
|
+
const section = nextHeader ? text.slice(start, start + nextHeader.index) : text.slice(start);
|
|
113
|
+
const highlights = section
|
|
114
|
+
.split(/\r?\n/)
|
|
115
|
+
.map((line) => line.trim())
|
|
116
|
+
.filter((line) => /^-\s+\S+/.test(line));
|
|
117
|
+
return {
|
|
118
|
+
path: filePath,
|
|
119
|
+
version: match[1].trim(),
|
|
120
|
+
date: match[2].trim(),
|
|
121
|
+
headerLine,
|
|
122
|
+
hasHighlights: highlights.length > 0,
|
|
123
|
+
highlights,
|
|
124
|
+
ok: true,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function parseBannerVersion(filePath, fallbackVersion) {
|
|
129
|
+
const text = readTextIfExists(filePath);
|
|
130
|
+
if (!text) return { version: null, mode: 'missing' };
|
|
131
|
+
if (text.includes('v{version}')) return { version: fallbackVersion || null, mode: 'placeholder' };
|
|
132
|
+
const match = text.match(/v(\d+\.\d+\.\d+)/);
|
|
133
|
+
return { version: match ? match[1] : null, mode: match ? 'fixed' : 'unknown' };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function hashObject(value) {
|
|
137
|
+
const json = JSON.stringify(value, null, 2);
|
|
138
|
+
const tempDir = fs.mkdtempSync(path.join(require('os').tmpdir(), 'oxe-release-hash-'));
|
|
139
|
+
const tempFile = path.join(tempDir, 'payload.json');
|
|
140
|
+
fs.writeFileSync(tempFile, json, 'utf8');
|
|
141
|
+
const hash = oxeManifest.sha256File(tempFile);
|
|
142
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
143
|
+
return hash;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function collectWrapperHashes(projectRoot) {
|
|
147
|
+
const wrappers = {};
|
|
148
|
+
for (const target of WRAPPER_TARGETS) {
|
|
149
|
+
const dir = path.join(projectRoot, target.dir);
|
|
150
|
+
if (!fs.existsSync(dir)) {
|
|
151
|
+
wrappers[target.key] = {
|
|
152
|
+
dir: target.dir,
|
|
153
|
+
missing: true,
|
|
154
|
+
fileCount: 0,
|
|
155
|
+
aggregateHash: null,
|
|
156
|
+
files: [],
|
|
157
|
+
};
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
const files = fs.readdirSync(dir)
|
|
161
|
+
.filter((name) => target.filter(name))
|
|
162
|
+
.sort()
|
|
163
|
+
.map((name) => {
|
|
164
|
+
const filePath = path.join(dir, name);
|
|
165
|
+
return {
|
|
166
|
+
path: path.relative(projectRoot, filePath).replace(/\\/g, '/'),
|
|
167
|
+
hash: oxeManifest.sha256File(filePath),
|
|
168
|
+
};
|
|
169
|
+
});
|
|
170
|
+
wrappers[target.key] = {
|
|
171
|
+
dir: target.dir,
|
|
172
|
+
missing: false,
|
|
173
|
+
fileCount: files.length,
|
|
174
|
+
aggregateHash: hashObject(files),
|
|
175
|
+
files,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
return wrappers;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function diffWrapperHashes(before, after) {
|
|
182
|
+
const mismatches = [];
|
|
183
|
+
const keys = new Set([...Object.keys(before || {}), ...Object.keys(after || {})]);
|
|
184
|
+
for (const key of keys) {
|
|
185
|
+
const a = before[key] || null;
|
|
186
|
+
const b = after[key] || null;
|
|
187
|
+
if (!a || !b || a.aggregateHash !== b.aggregateHash || a.fileCount !== b.fileCount) {
|
|
188
|
+
mismatches.push({
|
|
189
|
+
target: key,
|
|
190
|
+
before: a,
|
|
191
|
+
after: b,
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return mismatches;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function runSyncScript(packageRoot, scriptName, projectRoot) {
|
|
199
|
+
const scriptPath = path.join(packageRoot, 'scripts', scriptName);
|
|
200
|
+
const result = spawnSync(process.execPath, [scriptPath], {
|
|
201
|
+
cwd: packageRoot,
|
|
202
|
+
encoding: 'utf8',
|
|
203
|
+
env: {
|
|
204
|
+
...process.env,
|
|
205
|
+
OXE_SYNC_REPO_ROOT: projectRoot,
|
|
206
|
+
OXE_NO_BANNER: '1',
|
|
207
|
+
},
|
|
208
|
+
});
|
|
209
|
+
return {
|
|
210
|
+
script: scriptName,
|
|
211
|
+
status: result.status,
|
|
212
|
+
ok: result.status === 0,
|
|
213
|
+
stdout: String(result.stdout || ''),
|
|
214
|
+
stderr: String(result.stderr || ''),
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function syncWrappers(projectRoot, packageRoot) {
|
|
219
|
+
const before = collectWrapperHashes(projectRoot);
|
|
220
|
+
const syncRuntime = runSyncScript(packageRoot, 'sync-runtime-metadata.cjs', projectRoot);
|
|
221
|
+
const syncCursor = runSyncScript(packageRoot, 'sync-cursor-from-prompts.cjs', projectRoot);
|
|
222
|
+
const after = collectWrapperHashes(projectRoot);
|
|
223
|
+
const mismatches = diffWrapperHashes(before, after);
|
|
224
|
+
return {
|
|
225
|
+
before,
|
|
226
|
+
after,
|
|
227
|
+
scripts: [syncRuntime, syncCursor],
|
|
228
|
+
mismatches,
|
|
229
|
+
ok: syncRuntime.ok && syncCursor.ok && mismatches.length === 0,
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function readReportSummary(reportPath, requiredItems, extractOk) {
|
|
234
|
+
const data = readJsonIfExists(reportPath);
|
|
235
|
+
if (!data || !Array.isArray(data.results)) {
|
|
236
|
+
return {
|
|
237
|
+
path: reportPath,
|
|
238
|
+
present: false,
|
|
239
|
+
ok: false,
|
|
240
|
+
total: 0,
|
|
241
|
+
failures: ['report_missing'],
|
|
242
|
+
missingRequired: Array.isArray(requiredItems) ? requiredItems.slice() : [],
|
|
243
|
+
results: [],
|
|
244
|
+
raw: data,
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
const results = data.results;
|
|
248
|
+
const failures = results.filter((item) => !extractOk(item)).map((item) => item.runtime || item.fixture || item.scenario || item.name || 'unknown');
|
|
249
|
+
const missingRequired = Array.isArray(requiredItems)
|
|
250
|
+
? requiredItems.filter((name) => !results.some((item) => (item.runtime || item.fixture || item.scenario || item.name) === name))
|
|
251
|
+
: [];
|
|
252
|
+
return {
|
|
253
|
+
path: reportPath,
|
|
254
|
+
present: true,
|
|
255
|
+
ok: failures.length === 0 && missingRequired.length === 0,
|
|
256
|
+
total: results.length,
|
|
257
|
+
failures,
|
|
258
|
+
missingRequired,
|
|
259
|
+
results,
|
|
260
|
+
raw: data,
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function loadRuntimeSmokeReport(projectRoot) {
|
|
265
|
+
return readReportSummary(releasePaths(projectRoot).smokeReport, REQUIRED_RUNTIMES, (item) => {
|
|
266
|
+
return Boolean(
|
|
267
|
+
item
|
|
268
|
+
&& item.install_ok
|
|
269
|
+
&& item.oxe_present
|
|
270
|
+
&& item.workflow_resolution_ok
|
|
271
|
+
&& item.wrapper_drift_ok !== false
|
|
272
|
+
&& item.uninstall_ok
|
|
273
|
+
);
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function loadRecoveryFixtureReport(projectRoot) {
|
|
278
|
+
return readReportSummary(releasePaths(projectRoot).recoveryFixtureReport, null, (item) => Boolean(item && item.ok));
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function loadMultiAgentSoakReport(projectRoot) {
|
|
282
|
+
return readReportSummary(releasePaths(projectRoot).multiAgentSoakReport, null, (item) => Boolean(item && item.ok));
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function readVersionSnapshot(projectRoot) {
|
|
286
|
+
const packageJsonPath = path.join(projectRoot, 'package.json');
|
|
287
|
+
const runtimePackagePath = path.join(projectRoot, 'packages', 'runtime', 'package.json');
|
|
288
|
+
const vscodePackagePath = path.join(projectRoot, 'vscode-extension', 'package.json');
|
|
289
|
+
const readmePath = path.join(projectRoot, 'README.md');
|
|
290
|
+
const changelogPath = path.join(projectRoot, 'CHANGELOG.md');
|
|
291
|
+
const bannerPath = path.join(projectRoot, 'bin', 'banner.txt');
|
|
292
|
+
const rootVersion = readPackageVersion(packageJsonPath);
|
|
293
|
+
const changelog = parseChangelogTop(changelogPath);
|
|
294
|
+
return {
|
|
295
|
+
rootPackage: { path: packageJsonPath, version: rootVersion },
|
|
296
|
+
runtimePackage: { path: runtimePackagePath, version: readPackageVersion(runtimePackagePath) },
|
|
297
|
+
vscodeExtension: { path: vscodePackagePath, version: readPackageVersion(vscodePackagePath) },
|
|
298
|
+
readme: { path: readmePath, version: readReadmeVersion(readmePath) },
|
|
299
|
+
changelog,
|
|
300
|
+
banner: { path: bannerPath, ...parseBannerVersion(bannerPath, rootVersion) },
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function buildReleaseManifest(projectRoot, options = {}) {
|
|
305
|
+
const packageRoot = path.resolve(options.packageRoot || projectRoot);
|
|
306
|
+
const paths = releasePaths(projectRoot);
|
|
307
|
+
const versions = readVersionSnapshot(projectRoot);
|
|
308
|
+
const runtimeEntry = path.join(packageRoot, 'lib', 'runtime', 'index.js');
|
|
309
|
+
const wrapperSync = options.skipWrapperSync ? {
|
|
310
|
+
before: collectWrapperHashes(projectRoot),
|
|
311
|
+
after: collectWrapperHashes(projectRoot),
|
|
312
|
+
scripts: [],
|
|
313
|
+
mismatches: [],
|
|
314
|
+
ok: true,
|
|
315
|
+
} : syncWrappers(projectRoot, packageRoot);
|
|
316
|
+
const smoke = loadRuntimeSmokeReport(projectRoot);
|
|
317
|
+
const recovery = loadRecoveryFixtureReport(projectRoot);
|
|
318
|
+
const multiAgent = loadMultiAgentSoakReport(projectRoot);
|
|
319
|
+
const manifest = {
|
|
320
|
+
schema_version: 1,
|
|
321
|
+
generated_at: new Date().toISOString(),
|
|
322
|
+
project_root: projectRoot,
|
|
323
|
+
package_root: packageRoot,
|
|
324
|
+
release_contract: {
|
|
325
|
+
execute_verify: 'runtime-first',
|
|
326
|
+
promotion_target: 'pr_draft',
|
|
327
|
+
multi_agent_backend: 'git_worktree',
|
|
328
|
+
branch_push: 'advanced_only',
|
|
329
|
+
},
|
|
330
|
+
versions,
|
|
331
|
+
runtime_compiled: {
|
|
332
|
+
path: runtimeEntry,
|
|
333
|
+
ok: fs.existsSync(runtimeEntry),
|
|
334
|
+
},
|
|
335
|
+
wrappers: {
|
|
336
|
+
hash_before_sync: wrapperSync.before,
|
|
337
|
+
hash_after_sync: wrapperSync.after,
|
|
338
|
+
sync: {
|
|
339
|
+
ok: wrapperSync.ok,
|
|
340
|
+
mismatches: wrapperSync.mismatches,
|
|
341
|
+
scripts: wrapperSync.scripts,
|
|
342
|
+
},
|
|
343
|
+
},
|
|
344
|
+
reports: {
|
|
345
|
+
runtime_smoke: smoke,
|
|
346
|
+
recovery_fixtures: recovery,
|
|
347
|
+
multi_agent_soak: multiAgent,
|
|
348
|
+
},
|
|
349
|
+
};
|
|
350
|
+
if (options.writeManifest) {
|
|
351
|
+
ensureDirForFile(paths.manifest);
|
|
352
|
+
fs.writeFileSync(paths.manifest, JSON.stringify(manifest, null, 2) + '\n', 'utf8');
|
|
353
|
+
}
|
|
354
|
+
return manifest;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function checkReleaseConsistency(projectRoot, options = {}) {
|
|
358
|
+
const manifest = buildReleaseManifest(projectRoot, options);
|
|
359
|
+
const blockers = [];
|
|
360
|
+
const warnings = [];
|
|
361
|
+
const versions = manifest.versions;
|
|
362
|
+
const canonicalVersion = versions.rootPackage.version;
|
|
363
|
+
const compareTargets = [
|
|
364
|
+
['packages/runtime/package.json', versions.runtimePackage.version],
|
|
365
|
+
['vscode-extension/package.json', versions.vscodeExtension.version],
|
|
366
|
+
['README.md', versions.readme.version],
|
|
367
|
+
['CHANGELOG.md', versions.changelog.version],
|
|
368
|
+
['bin/banner.txt', versions.banner.version],
|
|
369
|
+
];
|
|
370
|
+
for (const [label, version] of compareTargets) {
|
|
371
|
+
if (!version || version !== canonicalVersion) {
|
|
372
|
+
blockers.push(`${label} diverge da versão raiz (${canonicalVersion || 'ausente'})`);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
if (!versions.changelog.ok) {
|
|
376
|
+
blockers.push('CHANGELOG.md sem cabeçalho de versão no topo');
|
|
377
|
+
} else {
|
|
378
|
+
if (!versions.changelog.date) blockers.push('CHANGELOG.md topo sem data');
|
|
379
|
+
if (!versions.changelog.hasHighlights) blockers.push('CHANGELOG.md topo sem highlights');
|
|
380
|
+
}
|
|
381
|
+
if (!manifest.runtime_compiled.ok) {
|
|
382
|
+
blockers.push('runtime não compilado (lib/runtime/index.js ausente)');
|
|
383
|
+
}
|
|
384
|
+
if (!manifest.wrappers.sync.ok) {
|
|
385
|
+
if (manifest.wrappers.sync.scripts.some((entry) => !entry.ok)) {
|
|
386
|
+
blockers.push('sync de wrappers falhou');
|
|
387
|
+
}
|
|
388
|
+
if (manifest.wrappers.sync.mismatches.length > 0) {
|
|
389
|
+
blockers.push('wrappers ficam dirty após sync-runtime-metadata/sync:cursor');
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
if (!manifest.reports.runtime_smoke.present || !manifest.reports.runtime_smoke.ok) {
|
|
393
|
+
blockers.push('runtime smoke matrix incompleta ou com falhas');
|
|
394
|
+
}
|
|
395
|
+
if (!manifest.reports.recovery_fixtures.present || !manifest.reports.recovery_fixtures.ok) {
|
|
396
|
+
blockers.push('recovery fixture report incompleto ou com falhas');
|
|
397
|
+
}
|
|
398
|
+
if (!manifest.reports.multi_agent_soak.present || !manifest.reports.multi_agent_soak.ok) {
|
|
399
|
+
blockers.push('multi-agent soak report incompleto ou com falhas');
|
|
400
|
+
}
|
|
401
|
+
if (versions.banner.mode === 'unknown') {
|
|
402
|
+
warnings.push('banner.txt sem placeholder v{version} nem versão fixa detectável');
|
|
403
|
+
}
|
|
404
|
+
return {
|
|
405
|
+
ok: blockers.length === 0,
|
|
406
|
+
blockers,
|
|
407
|
+
warnings,
|
|
408
|
+
manifest,
|
|
409
|
+
manifestPath: releasePaths(projectRoot).manifest,
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
module.exports = {
|
|
414
|
+
REQUIRED_RUNTIMES,
|
|
415
|
+
WRAPPER_TARGETS,
|
|
416
|
+
releasePaths,
|
|
417
|
+
collectWrapperHashes,
|
|
418
|
+
loadRuntimeSmokeReport,
|
|
419
|
+
loadRecoveryFixtureReport,
|
|
420
|
+
loadMultiAgentSoakReport,
|
|
421
|
+
buildReleaseManifest,
|
|
422
|
+
checkReleaseConsistency,
|
|
423
|
+
};
|