oxe-cc 1.8.3 → 1.9.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/AGENTS.md +1 -1
- package/CHANGELOG.md +31 -0
- package/README.md +15 -12
- package/bin/lib/oxe-operational.cjs +209 -69
- package/bin/lib/oxe-project-health.cjs +2 -0
- package/bin/lib/oxe-release.cjs +26 -0
- package/bin/oxe-cc.js +22 -13
- package/docs/RELEASE-READINESS.md +6 -1
- package/lib/runtime/scheduler/multi-agent-coordinator.d.ts +39 -0
- package/lib/runtime/scheduler/multi-agent-coordinator.js +222 -13
- package/lib/runtime/workspace/strategies/git-worktree.js +18 -9
- package/package.json +6 -3
- package/packages/runtime/package.json +1 -1
- package/packages/runtime/src/scheduler/multi-agent-coordinator.ts +320 -46
- package/packages/runtime/src/workspace/strategies/git-worktree.ts +24 -16
- package/vscode-extension/package.json +1 -1
package/AGENTS.md
CHANGED
|
@@ -99,4 +99,4 @@ Os wrappers por runtime podem carregar metadata cognitiva (`oxe_reasoning_mode`,
|
|
|
99
99
|
|
|
100
100
|
Para `execute` e `verify`, o comportamento atual esperado é **runtime-first**: se `oxe-cc runtime` estiver disponível, preferir `runtime compile/project/verify/gates` e tratar markdown como projeção derivada; se o runtime não puder ser executado, declarar `fallback legado` explicitamente.
|
|
101
101
|
|
|
102
|
-
Para publicação, o gate local esperado passa por `oxe-cc doctor --release --write-manifest`. Essa verificação deve falhar se houver drift de versão, topo inválido no `CHANGELOG`, ausência da árvore canónica `oxe/`, `workflow-runtime-contracts.json` inválido, wrapper dirty após sync, runtime não compilado ou ausência dos relatórios `.oxe/release/*.json` exigidos pela release.
|
|
102
|
+
Para publicação, o gate local esperado passa por `npm test`, `npm run release:pack-check` e `oxe-cc doctor --release --write-manifest`. Essa verificação deve falhar se houver drift de versão, topo inválido no `CHANGELOG`, ausência da árvore canónica `oxe/`, `workflow-runtime-contracts.json` inválido, wrapper dirty após sync, runtime não compilado, pacote npm com artefatos indevidos ou ausência dos relatórios `.oxe/release/*.json` exigidos pela release, incluindo `runtime-real-report.json` e, a partir da `1.9.1`, `multi-agent-real-report.json`.
|
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,37 @@ Todas as versões seguem [Semantic Versioning](https://semver.org/). As mudança
|
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
+
## [1.9.1] — 2026-05-04
|
|
8
|
+
|
|
9
|
+
### Multi-Agent Real
|
|
10
|
+
|
|
11
|
+
Esta release endurece a coordenação multi-agent sobre workspaces reais, sem abrir novos comandos públicos.
|
|
12
|
+
|
|
13
|
+
- multi-agent real passa a exigir workspaces isolados `git_worktree` para modos `parallel`, `competitive` e `cooperative`
|
|
14
|
+
- `runtime execute` valida `plan-agents.json` antes de iniciar agentes e usa `GitWorktreeManager` por padrão no caminho multi-agent
|
|
15
|
+
- novo artefato por run `.oxe/runs/<run_id>/workspace-merge-report.json` com worktrees, ownership, merge blockers e readiness
|
|
16
|
+
- `runtime agents status --json`, `status --json` e recovery passam a expor merge readiness, blockers, arbitration e worktrees órfãos
|
|
17
|
+
- novo `npm run test:multi-agent-real` gera `.oxe/release/multi-agent-real-report.json` usando repositório git temporário com worktrees reais
|
|
18
|
+
- `doctor --release` passa a exigir `multi-agent-real-report.json` em versões `>=1.9.1`
|
|
19
|
+
|
|
20
|
+
## [1.9.0] — 2026-05-04
|
|
21
|
+
|
|
22
|
+
### Runtime Real Maturity
|
|
23
|
+
|
|
24
|
+
Esta release eleva a maturidade prática do OXE com foco em prova determinística do ciclo real `spec -> plan -> execute -> verify -> status/doctor`, sem abrir novos comandos públicos.
|
|
25
|
+
|
|
26
|
+
#### Runtime real
|
|
27
|
+
|
|
28
|
+
- nova suíte `test:runtime-real` valida projetos representativos com executor mockado e sem chamada a LLM/API externa
|
|
29
|
+
- `runtime execute` passa a bloquear antes da mutação quando confiança, rationality packs ou gates não estão prontos
|
|
30
|
+
- `runtime verify` continua evidence-first e passa a ser validado dentro do fluxo real de execução
|
|
31
|
+
|
|
32
|
+
#### Release proof
|
|
33
|
+
|
|
34
|
+
- novo relatório obrigatório `.oxe/release/runtime-real-report.json`
|
|
35
|
+
- `doctor --release` passa a bloquear publicação quando a prova de runtime real estiver ausente ou falha
|
|
36
|
+
- novo `release:pack-check` valida o tarball npm em dry-run contra artefatos indevidos
|
|
37
|
+
|
|
7
38
|
## [1.8.3] — 2026-05-04
|
|
8
39
|
|
|
9
40
|
### Release readiness e higiene de publicação
|
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
[](https://www.npmjs.com/package/oxe-cc)
|
|
8
8
|
[](LICENSE)
|
|
9
9
|
|
|
10
|
-
**Versão:** `1.
|
|
10
|
+
**Versão:** `1.9.1` · [package.json](package.json)
|
|
11
11
|
|
|
12
12
|
**Framework OXE — Orchestrated eXperience Engineering**
|
|
13
13
|
|
|
@@ -459,20 +459,23 @@ O `status --full` mostra em ANSI: readiness do ciclo, autoavaliação do plano,
|
|
|
459
459
|
|
|
460
460
|
O pacote está pronto para uma publicação robusta quando estes sinais estiverem verdes no repositório da release:
|
|
461
461
|
|
|
462
|
-
- `npm test`
|
|
463
|
-
- `npm run scan:assets`
|
|
464
|
-
- `npm run build:vscode-ext`
|
|
465
|
-
- `node bin/oxe-cc.js doctor --release --write-manifest`
|
|
466
|
-
- `
|
|
462
|
+
- `npm test`
|
|
463
|
+
- `npm run scan:assets`
|
|
464
|
+
- `npm run build:vscode-ext`
|
|
465
|
+
- `node bin/oxe-cc.js doctor --release --write-manifest`
|
|
466
|
+
- `npm run release:pack-check`
|
|
467
|
+
- `node bin/oxe-cc.js status --full`
|
|
467
468
|
|
|
468
469
|
Artefatos obrigatórios desta fase:
|
|
469
470
|
|
|
470
|
-
- `.oxe/release/release-manifest.json`
|
|
471
|
-
- `.oxe/release/runtime-smoke-report.json`
|
|
472
|
-
- `.oxe/release/
|
|
473
|
-
- `.oxe/release/
|
|
474
|
-
|
|
475
|
-
|
|
471
|
+
- `.oxe/release/release-manifest.json`
|
|
472
|
+
- `.oxe/release/runtime-smoke-report.json`
|
|
473
|
+
- `.oxe/release/runtime-real-report.json`
|
|
474
|
+
- `.oxe/release/recovery-fixture-report.json`
|
|
475
|
+
- `.oxe/release/multi-agent-soak-report.json`
|
|
476
|
+
- `.oxe/release/multi-agent-real-report.json`
|
|
477
|
+
|
|
478
|
+
Na linha `1.9.1`, `runtime-real-report.json` prova o ciclo real `compile -> execute mockado -> verify -> project -> status --json`, e `multi-agent-real-report.json` prova coordenação com `git_worktree`, ownership, arbitragem e merge readiness antes da publicação.
|
|
476
479
|
|
|
477
480
|
### `/oxe-retro` — loop de aprendizado
|
|
478
481
|
|
|
@@ -722,10 +722,52 @@ async function resolveRuntimeGate(projectRoot, activeSession, options = {}) {
|
|
|
722
722
|
};
|
|
723
723
|
}
|
|
724
724
|
|
|
725
|
-
// Gap 5: route execution to MultiAgentCoordinator when plan-agents.json exists
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
725
|
+
// Gap 5: route execution to MultiAgentCoordinator when plan-agents.json exists
|
|
726
|
+
function buildRuntimeExecutePreflight(projectRoot, activeSession, runState) {
|
|
727
|
+
const health = loadProjectHealth();
|
|
728
|
+
const blockers = [];
|
|
729
|
+
const warnings = [];
|
|
730
|
+
let report = null;
|
|
731
|
+
if (health && typeof health.buildHealthReport === 'function') {
|
|
732
|
+
report = health.buildHealthReport(projectRoot);
|
|
733
|
+
if (report.planSelfEvaluation && report.planSelfEvaluation.executable === false) {
|
|
734
|
+
const confidence = report.planSelfEvaluation.confidence;
|
|
735
|
+
const threshold = report.planConfidenceThreshold || 90;
|
|
736
|
+
blockers.push(`plan_confidence:${confidence == null ? 'missing' : `${confidence}%`}<=${threshold}%`);
|
|
737
|
+
}
|
|
738
|
+
if (report.executionRationality && report.executionRationality.applicable && !report.executionRationalityReady) {
|
|
739
|
+
const gaps = Array.isArray(report.criticalExecutionGaps) ? report.criticalExecutionGaps : [];
|
|
740
|
+
blockers.push(`execution_rationality:${gaps[0] || 'not_ready'}`);
|
|
741
|
+
}
|
|
742
|
+
if (report.fallbackMode && report.fallbackMode !== 'none') {
|
|
743
|
+
warnings.push(`fallback_mode:${report.fallbackMode}`);
|
|
744
|
+
}
|
|
745
|
+
} else {
|
|
746
|
+
warnings.push('health_report_unavailable');
|
|
747
|
+
}
|
|
748
|
+
const runId = runState && runState.run_id ? runState.run_id : null;
|
|
749
|
+
const queue = readRuntimeGates(projectRoot, activeSession, { runId });
|
|
750
|
+
if (queue.pending.length > 0) {
|
|
751
|
+
blockers.push(`pending_gates:${queue.pending.length}`);
|
|
752
|
+
}
|
|
753
|
+
return {
|
|
754
|
+
ok: blockers.length === 0,
|
|
755
|
+
blockers,
|
|
756
|
+
warnings,
|
|
757
|
+
runId,
|
|
758
|
+
gateQueue: {
|
|
759
|
+
pending: queue.pending.length,
|
|
760
|
+
stale: queue.staleCount || 0,
|
|
761
|
+
},
|
|
762
|
+
confidence: report && report.planSelfEvaluation ? report.planSelfEvaluation.confidence : null,
|
|
763
|
+
confidenceThreshold: report ? report.planConfidenceThreshold || 90 : 90,
|
|
764
|
+
executionRationalityReady: report ? Boolean(report.executionRationalityReady) : false,
|
|
765
|
+
};
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
async function runRuntimeExecute(projectRoot, activeSession, options = {}) {
|
|
769
|
+
const runtime = loadRuntimeModule();
|
|
770
|
+
if (!runtime) throw new Error('Runtime package não está disponível. Rode npm run build:runtime.');
|
|
729
771
|
const parsers = loadSdkParsers();
|
|
730
772
|
if (!parsers) throw new Error('SDK parsers não disponíveis.');
|
|
731
773
|
|
|
@@ -745,15 +787,41 @@ async function runRuntimeExecute(projectRoot, activeSession, options = {}) {
|
|
|
745
787
|
|
|
746
788
|
// Resolve compiled graph from run state or compile on demand
|
|
747
789
|
let current = options.runState || readRunState(projectRoot, activeSession);
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
790
|
+
if (!current || !current.compiled_graph) {
|
|
791
|
+
current = compileExecutionGraphFromArtifacts(projectRoot, activeSession, { runState: current }).run;
|
|
792
|
+
}
|
|
793
|
+
if (!current || !current.compiled_graph) {
|
|
794
|
+
throw new Error('Nenhum grafo compilado encontrado. Execute oxe-cc runtime compile primeiro.');
|
|
795
|
+
}
|
|
796
|
+
const preflight = buildRuntimeExecutePreflight(projectRoot, activeSession, current);
|
|
797
|
+
if (!preflight.ok && !options.skipPreflight) {
|
|
798
|
+
const reason = preflight.blockers[0] || 'runtime_execute_preflight_failed';
|
|
799
|
+
appendEvent(projectRoot, activeSession, {
|
|
800
|
+
type: 'WorkItemBlocked',
|
|
801
|
+
run_id: current.run_id,
|
|
802
|
+
payload: {
|
|
803
|
+
reason: 'runtime_execute_preflight_failed',
|
|
804
|
+
blockers: preflight.blockers,
|
|
805
|
+
},
|
|
806
|
+
});
|
|
807
|
+
return {
|
|
808
|
+
mode: 'preflight',
|
|
809
|
+
agentPlan: null,
|
|
810
|
+
result: {
|
|
811
|
+
run_id: current.run_id,
|
|
812
|
+
status: 'blocked',
|
|
813
|
+
completed: [],
|
|
814
|
+
failed: [],
|
|
815
|
+
blocked: ['runtime_execute_preflight'],
|
|
816
|
+
reason,
|
|
817
|
+
},
|
|
818
|
+
run: current,
|
|
819
|
+
preflight,
|
|
820
|
+
};
|
|
821
|
+
}
|
|
822
|
+
const graph = runtime.fromSerializable
|
|
823
|
+
? runtime.fromSerializable(current.compiled_graph)
|
|
824
|
+
: current.compiled_graph;
|
|
757
825
|
|
|
758
826
|
// Detect plan-agents.json (session path takes priority over root)
|
|
759
827
|
const rootAgentPlan = path.join(projectRoot, '.oxe', 'plan-agents.json');
|
|
@@ -780,35 +848,40 @@ async function runRuntimeExecute(projectRoot, activeSession, options = {}) {
|
|
|
780
848
|
// Gap 5: multi-agent path if plan-agents.json exists
|
|
781
849
|
if (agentPlanPath) {
|
|
782
850
|
let agentPlan;
|
|
783
|
-
try {
|
|
784
|
-
agentPlan = JSON.parse(fs.readFileSync(agentPlanPath, 'utf8'));
|
|
785
|
-
} catch (err) {
|
|
786
|
-
throw new Error(`plan-agents.json inválido: ${err.message}`);
|
|
787
|
-
}
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
851
|
+
try {
|
|
852
|
+
agentPlan = JSON.parse(fs.readFileSync(agentPlanPath, 'utf8'));
|
|
853
|
+
} catch (err) {
|
|
854
|
+
throw new Error(`plan-agents.json inválido: ${err.message}`);
|
|
855
|
+
}
|
|
856
|
+
const planErrors = validateMultiAgentPlan(agentPlan);
|
|
857
|
+
if (planErrors.length > 0) {
|
|
858
|
+
throw new Error(`plan-agents.json inválido: ${planErrors.join('; ')}`);
|
|
859
|
+
}
|
|
860
|
+
if (typeof runtime.MultiAgentCoordinator !== 'function') {
|
|
861
|
+
throw new Error('Runtime não exporta MultiAgentCoordinator. Verifique a versão do runtime.');
|
|
862
|
+
}
|
|
863
|
+
if (typeof runtime.GitWorktreeManager !== 'function' && !options.workspaceManager) {
|
|
864
|
+
throw new Error('Runtime não exporta GitWorktreeManager. Multi-agent real exige backend git_worktree.');
|
|
865
|
+
}
|
|
866
|
+
const agents = agentPlan.agents.map((spec) => ({
|
|
867
|
+
id: spec.id,
|
|
868
|
+
executor: options.executorFactory ? options.executorFactory(spec) : (options.executor || executor),
|
|
869
|
+
workspaceManager: options.workspaceManager || new runtime.GitWorktreeManager(projectRoot),
|
|
870
|
+
assignedTaskIds: Array.isArray(spec.tasks) ? spec.tasks : [],
|
|
871
|
+
}));
|
|
800
872
|
const coordinator = new runtime.MultiAgentCoordinator();
|
|
801
873
|
const result = await coordinator.run(graph, {
|
|
802
874
|
mode: agentPlan.mode || 'parallel',
|
|
803
875
|
agents,
|
|
804
876
|
projectRoot,
|
|
805
877
|
sessionId: activeSession || null,
|
|
806
|
-
runId: current.run_id,
|
|
807
|
-
heartbeatTimeoutMs: options.heartbeatTimeoutMs ?? 120000,
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
878
|
+
runId: current.run_id,
|
|
879
|
+
heartbeatTimeoutMs: options.heartbeatTimeoutMs ?? 120000,
|
|
880
|
+
applyWorkspaceMerges: true,
|
|
881
|
+
onEvent: ctx.onEvent,
|
|
882
|
+
});
|
|
883
|
+
return { mode: agentPlan.mode || 'parallel', agentPlan, result, run: current, preflight };
|
|
884
|
+
}
|
|
812
885
|
|
|
813
886
|
// Single-agent fallback
|
|
814
887
|
if (typeof runtime.Scheduler !== 'function') {
|
|
@@ -816,8 +889,8 @@ async function runRuntimeExecute(projectRoot, activeSession, options = {}) {
|
|
|
816
889
|
}
|
|
817
890
|
const scheduler = new runtime.Scheduler();
|
|
818
891
|
const result = await scheduler.run(graph, ctx);
|
|
819
|
-
|
|
820
|
-
}
|
|
892
|
+
return { mode: 'single', agentPlan: null, result, run: current, preflight };
|
|
893
|
+
}
|
|
821
894
|
|
|
822
895
|
function readRuntimeMultiAgentStatus(projectRoot, activeSession, options = {}) {
|
|
823
896
|
const runtime = loadRuntimeModule();
|
|
@@ -832,23 +905,32 @@ function readRuntimeMultiAgentStatus(projectRoot, activeSession, options = {}) {
|
|
|
832
905
|
workspaceIsolationEnforced: false,
|
|
833
906
|
agents: [],
|
|
834
907
|
ownership: [],
|
|
835
|
-
orphanReassignments: [],
|
|
836
|
-
handoffs: [],
|
|
837
|
-
arbitrationResults: [],
|
|
838
|
-
|
|
839
|
-
|
|
908
|
+
orphanReassignments: [],
|
|
909
|
+
handoffs: [],
|
|
910
|
+
arbitrationResults: [],
|
|
911
|
+
workspaceMergeReport: null,
|
|
912
|
+
worktrees: [],
|
|
913
|
+
mergeBlockers: [],
|
|
914
|
+
mergeReadiness: null,
|
|
915
|
+
arbitrationRequired: false,
|
|
916
|
+
summary: null,
|
|
917
|
+
};
|
|
840
918
|
}
|
|
841
919
|
const runDir = path.join(projectRoot, '.oxe', 'runs', runId);
|
|
842
920
|
const statePath = path.join(runDir, 'multi-agent-state.json');
|
|
843
|
-
const summaryPath = path.join(runDir, 'multi-agent-summary.json');
|
|
844
|
-
const handoffsPath = path.join(runDir, 'handoffs.json');
|
|
845
|
-
const arbitrationPath = path.join(runDir, 'arbitration-results.json');
|
|
921
|
+
const summaryPath = path.join(runDir, 'multi-agent-summary.json');
|
|
922
|
+
const handoffsPath = path.join(runDir, 'handoffs.json');
|
|
923
|
+
const arbitrationPath = path.join(runDir, 'arbitration-results.json');
|
|
924
|
+
const workspaceMergePath = path.join(runDir, 'workspace-merge-report.json');
|
|
846
925
|
const state = runtime && typeof runtime.loadMultiAgentState === 'function'
|
|
847
926
|
? runtime.loadMultiAgentState(projectRoot, runId)
|
|
848
927
|
: readJsonIfExists(statePath);
|
|
849
|
-
const summary = runtime && typeof runtime.loadMultiAgentSummary === 'function'
|
|
850
|
-
? runtime.loadMultiAgentSummary(projectRoot, runId)
|
|
851
|
-
: readJsonIfExists(summaryPath);
|
|
928
|
+
const summary = runtime && typeof runtime.loadMultiAgentSummary === 'function'
|
|
929
|
+
? runtime.loadMultiAgentSummary(projectRoot, runId)
|
|
930
|
+
: readJsonIfExists(summaryPath);
|
|
931
|
+
const workspaceMergeReport = runtime && typeof runtime.loadWorkspaceMergeReport === 'function'
|
|
932
|
+
? runtime.loadWorkspaceMergeReport(projectRoot, runId)
|
|
933
|
+
: readJsonIfExists(workspaceMergePath);
|
|
852
934
|
const handoffs = readJsonIfExists(handoffsPath);
|
|
853
935
|
const arbitrationResults = readJsonIfExists(arbitrationPath);
|
|
854
936
|
return {
|
|
@@ -861,10 +943,41 @@ function readRuntimeMultiAgentStatus(projectRoot, activeSession, options = {}) {
|
|
|
861
943
|
ownership: state && Array.isArray(state.ownership) ? state.ownership : [],
|
|
862
944
|
orphanReassignments: state && Array.isArray(state.orphan_reassignments) ? state.orphan_reassignments : [],
|
|
863
945
|
handoffs: Array.isArray(handoffs) ? handoffs : [],
|
|
864
|
-
arbitrationResults: Array.isArray(arbitrationResults) ? arbitrationResults : [],
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
946
|
+
arbitrationResults: Array.isArray(arbitrationResults) ? arbitrationResults : [],
|
|
947
|
+
workspaceMergeReport: workspaceMergeReport || null,
|
|
948
|
+
worktrees: workspaceMergeReport && Array.isArray(workspaceMergeReport.records) ? workspaceMergeReport.records : [],
|
|
949
|
+
mergeBlockers: workspaceMergeReport && Array.isArray(workspaceMergeReport.blockers) ? workspaceMergeReport.blockers : [],
|
|
950
|
+
mergeReadiness: workspaceMergeReport && workspaceMergeReport.merge_readiness ? workspaceMergeReport.merge_readiness : null,
|
|
951
|
+
arbitrationRequired: Boolean(workspaceMergeReport && workspaceMergeReport.arbitration_required),
|
|
952
|
+
summary: summary || null,
|
|
953
|
+
};
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
function validateMultiAgentPlan(agentPlan) {
|
|
957
|
+
const errors = [];
|
|
958
|
+
const allowedModes = new Set(['parallel', 'competitive', 'cooperative']);
|
|
959
|
+
const mode = agentPlan && agentPlan.mode ? String(agentPlan.mode) : 'parallel';
|
|
960
|
+
if (!allowedModes.has(mode)) errors.push(`mode inválido: ${mode}`);
|
|
961
|
+
if (!Array.isArray(agentPlan && agentPlan.agents) || agentPlan.agents.length === 0) {
|
|
962
|
+
errors.push('campo "agents" vazio ou ausente');
|
|
963
|
+
return errors;
|
|
964
|
+
}
|
|
965
|
+
const schemaVersion = Number(agentPlan.schema_version || agentPlan.schema || 0);
|
|
966
|
+
const seen = new Set();
|
|
967
|
+
for (const [idx, spec] of agentPlan.agents.entries()) {
|
|
968
|
+
const id = spec && spec.id ? String(spec.id) : '';
|
|
969
|
+
if (!id) errors.push(`agents[${idx}].id ausente`);
|
|
970
|
+
if (id && seen.has(id)) errors.push(`agent duplicado: ${id}`);
|
|
971
|
+
if (id) seen.add(id);
|
|
972
|
+
if (schemaVersion >= 3 && !spec.persona) errors.push(`${id || `agents[${idx}]`}.persona ausente`);
|
|
973
|
+
if (schemaVersion >= 3 && !spec.model_hint) errors.push(`${id || `agents[${idx}]`}.model_hint ausente`);
|
|
974
|
+
if (spec.tasks != null && !Array.isArray(spec.tasks)) errors.push(`${id || `agents[${idx}]`}.tasks deve ser array`);
|
|
975
|
+
}
|
|
976
|
+
if ((mode === 'competitive' || mode === 'cooperative') && agentPlan.agents.length < 2) {
|
|
977
|
+
errors.push(`${mode} exige pelo menos 2 agentes`);
|
|
978
|
+
}
|
|
979
|
+
return errors;
|
|
980
|
+
}
|
|
868
981
|
|
|
869
982
|
function loadRuntimeVerificationArtifacts(projectRoot, runState) {
|
|
870
983
|
const runtime = loadRuntimeModule();
|
|
@@ -890,7 +1003,7 @@ function loadRuntimeVerificationArtifacts(projectRoot, runState) {
|
|
|
890
1003
|
return { manifest, residualRisks, evidenceCoverage };
|
|
891
1004
|
}
|
|
892
1005
|
|
|
893
|
-
function countVerificationEvidenceRefs(runState, verificationArtifacts) {
|
|
1006
|
+
function countVerificationEvidenceRefs(runState, verificationArtifacts) {
|
|
894
1007
|
if (verificationArtifacts && verificationArtifacts.manifest && Array.isArray(verificationArtifacts.manifest.checks)) {
|
|
895
1008
|
return verificationArtifacts.manifest.checks.reduce((sum, check) => {
|
|
896
1009
|
return sum + (Array.isArray(check.evidence_refs) ? check.evidence_refs.length : 0);
|
|
@@ -902,7 +1015,26 @@ function countVerificationEvidenceRefs(runState, verificationArtifacts) {
|
|
|
902
1015
|
}, 0);
|
|
903
1016
|
}
|
|
904
1017
|
return 0;
|
|
905
|
-
}
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
function detectOrphanWorktrees(projectRoot, runId) {
|
|
1021
|
+
const workspacesDir = path.join(projectRoot, '.oxe', 'workspaces');
|
|
1022
|
+
if (!fs.existsSync(workspacesDir)) return [];
|
|
1023
|
+
const active = new Set();
|
|
1024
|
+
const mergeReport = readJsonIfExists(path.join(projectRoot, '.oxe', 'runs', runId, 'workspace-merge-report.json'));
|
|
1025
|
+
for (const record of (mergeReport && Array.isArray(mergeReport.records) ? mergeReport.records : [])) {
|
|
1026
|
+
if (record && record.workspace_id) active.add(String(record.workspace_id));
|
|
1027
|
+
}
|
|
1028
|
+
return fs.readdirSync(workspacesDir, { withFileTypes: true })
|
|
1029
|
+
.filter((entry) => entry.isDirectory())
|
|
1030
|
+
.map((entry) => entry.name)
|
|
1031
|
+
.filter((name) => !active.has(name))
|
|
1032
|
+
.map((name) => ({
|
|
1033
|
+
workspace_id: name,
|
|
1034
|
+
path: path.join(workspacesDir, name),
|
|
1035
|
+
next_action: 'inspecione o worktree órfão; depois remova com git worktree remove --force se não houver diffs úteis',
|
|
1036
|
+
}));
|
|
1037
|
+
}
|
|
906
1038
|
|
|
907
1039
|
function buildRuntimeModeStatus(runState) {
|
|
908
1040
|
if (!runState) {
|
|
@@ -1074,11 +1206,17 @@ function writeRecoverySummaryMarkdown(projectRoot, activeSession, runState, reco
|
|
|
1074
1206
|
'',
|
|
1075
1207
|
'## Work items órfãos',
|
|
1076
1208
|
'',
|
|
1077
|
-
...(Array.isArray(recoverySummary.orphan_work_items) && recoverySummary.orphan_work_items.length
|
|
1078
|
-
? recoverySummary.orphan_work_items.map((item) => `- ${item}`)
|
|
1079
|
-
: ['- Nenhum']),
|
|
1080
|
-
'',
|
|
1081
|
-
'##
|
|
1209
|
+
...(Array.isArray(recoverySummary.orphan_work_items) && recoverySummary.orphan_work_items.length
|
|
1210
|
+
? recoverySummary.orphan_work_items.map((item) => `- ${item}`)
|
|
1211
|
+
: ['- Nenhum']),
|
|
1212
|
+
'',
|
|
1213
|
+
'## Worktrees órfãos',
|
|
1214
|
+
'',
|
|
1215
|
+
...(Array.isArray(recoverySummary.orphan_worktrees) && recoverySummary.orphan_worktrees.length
|
|
1216
|
+
? recoverySummary.orphan_worktrees.map((item) => `- ${item.workspace_id} · ${item.next_action}`)
|
|
1217
|
+
: ['- Nenhum']),
|
|
1218
|
+
'',
|
|
1219
|
+
'## Tentativas incompletas',
|
|
1082
1220
|
'',
|
|
1083
1221
|
...(recoverySummary.consistency && Array.isArray(recoverySummary.consistency.incomplete_attempts) && recoverySummary.consistency.incomplete_attempts.length
|
|
1084
1222
|
? recoverySummary.consistency.incomplete_attempts.map((item) => `- ${item.work_item_id} · ${item.attempt_id || 'attempt'} · ${item.outcome || 'unknown'}`)
|
|
@@ -1441,13 +1579,14 @@ function recoverRuntimeState(projectRoot, activeSession, options = {}) {
|
|
|
1441
1579
|
journal,
|
|
1442
1580
|
verificationArtifacts
|
|
1443
1581
|
);
|
|
1444
|
-
const recoverySummary = {
|
|
1445
|
-
recovered_at: new Date().toISOString(),
|
|
1446
|
-
journal_state: journal.scheduler_state,
|
|
1447
|
-
orphan_work_items: orphanWorkItems,
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1582
|
+
const recoverySummary = {
|
|
1583
|
+
recovered_at: new Date().toISOString(),
|
|
1584
|
+
journal_state: journal.scheduler_state,
|
|
1585
|
+
orphan_work_items: orphanWorkItems,
|
|
1586
|
+
orphan_worktrees: detectOrphanWorktrees(projectRoot, current.run_id),
|
|
1587
|
+
pending_gates: readRuntimeGates(projectRoot, activeSession, { runId: current.run_id }).pending.map((gate) => gate.gate_id),
|
|
1588
|
+
consistency,
|
|
1589
|
+
};
|
|
1451
1590
|
const runDir = path.join(projectRoot, '.oxe', 'runs', current.run_id);
|
|
1452
1591
|
ensureDir(runDir);
|
|
1453
1592
|
const recoverySummaryPath = path.join(runDir, 'recovery-summary.json');
|
|
@@ -2329,8 +2468,9 @@ module.exports = {
|
|
|
2329
2468
|
buildRecoveryConsistency,
|
|
2330
2469
|
readRuntimeGates,
|
|
2331
2470
|
resolveRuntimeGate,
|
|
2332
|
-
createExecutionContext,
|
|
2333
|
-
|
|
2471
|
+
createExecutionContext,
|
|
2472
|
+
buildRuntimeExecutePreflight,
|
|
2473
|
+
runRuntimeExecute,
|
|
2334
2474
|
runRuntimeVerify,
|
|
2335
2475
|
projectRuntimeArtifacts,
|
|
2336
2476
|
runRuntimeCiChecks,
|
|
@@ -2070,8 +2070,10 @@ function suggestNextStep(target, cfg = {}) {
|
|
|
2070
2070
|
artifacts: [
|
|
2071
2071
|
'.oxe/release/release-manifest.json',
|
|
2072
2072
|
'.oxe/release/runtime-smoke-report.json',
|
|
2073
|
+
'.oxe/release/runtime-real-report.json',
|
|
2073
2074
|
'.oxe/release/recovery-fixture-report.json',
|
|
2074
2075
|
'.oxe/release/multi-agent-soak-report.json',
|
|
2076
|
+
'.oxe/release/multi-agent-real-report.json',
|
|
2075
2077
|
],
|
|
2076
2078
|
};
|
|
2077
2079
|
}
|
package/bin/lib/oxe-release.cjs
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const { spawnSync } = require('child_process');
|
|
6
|
+
const semver = require('semver');
|
|
6
7
|
|
|
7
8
|
const oxeManifest = require('./oxe-manifest.cjs');
|
|
8
9
|
const runtimeSemantics = require('./oxe-runtime-semantics.cjs');
|
|
@@ -42,8 +43,10 @@ function releasePaths(projectRoot) {
|
|
|
42
43
|
releaseDir,
|
|
43
44
|
manifest: path.join(releaseDir, 'release-manifest.json'),
|
|
44
45
|
smokeReport: path.join(releaseDir, 'runtime-smoke-report.json'),
|
|
46
|
+
runtimeRealReport: path.join(releaseDir, 'runtime-real-report.json'),
|
|
45
47
|
recoveryFixtureReport: path.join(releaseDir, 'recovery-fixture-report.json'),
|
|
46
48
|
multiAgentSoakReport: path.join(releaseDir, 'multi-agent-soak-report.json'),
|
|
49
|
+
multiAgentRealReport: path.join(releaseDir, 'multi-agent-real-report.json'),
|
|
47
50
|
};
|
|
48
51
|
}
|
|
49
52
|
|
|
@@ -277,6 +280,10 @@ function loadRuntimeSmokeReport(projectRoot) {
|
|
|
277
280
|
});
|
|
278
281
|
}
|
|
279
282
|
|
|
283
|
+
function loadRuntimeRealReport(projectRoot) {
|
|
284
|
+
return readReportSummary(releasePaths(projectRoot).runtimeRealReport, null, (item) => Boolean(item && item.ok));
|
|
285
|
+
}
|
|
286
|
+
|
|
280
287
|
function loadRecoveryFixtureReport(projectRoot) {
|
|
281
288
|
return readReportSummary(releasePaths(projectRoot).recoveryFixtureReport, null, (item) => Boolean(item && item.ok));
|
|
282
289
|
}
|
|
@@ -285,6 +292,10 @@ function loadMultiAgentSoakReport(projectRoot) {
|
|
|
285
292
|
return readReportSummary(releasePaths(projectRoot).multiAgentSoakReport, null, (item) => Boolean(item && item.ok));
|
|
286
293
|
}
|
|
287
294
|
|
|
295
|
+
function loadMultiAgentRealReport(projectRoot) {
|
|
296
|
+
return readReportSummary(releasePaths(projectRoot).multiAgentRealReport, null, (item) => Boolean(item && item.ok));
|
|
297
|
+
}
|
|
298
|
+
|
|
288
299
|
function readVersionSnapshot(projectRoot) {
|
|
289
300
|
const packageJsonPath = path.join(projectRoot, 'package.json');
|
|
290
301
|
const runtimePackagePath = path.join(projectRoot, 'packages', 'runtime', 'package.json');
|
|
@@ -349,8 +360,10 @@ function buildReleaseManifest(projectRoot, options = {}) {
|
|
|
349
360
|
} : syncWrappers(projectRoot, packageRoot);
|
|
350
361
|
const semanticsAudit = runtimeSemantics.auditRuntimeTargets(projectRoot);
|
|
351
362
|
const smoke = loadRuntimeSmokeReport(projectRoot);
|
|
363
|
+
const runtimeReal = loadRuntimeRealReport(projectRoot);
|
|
352
364
|
const recovery = loadRecoveryFixtureReport(projectRoot);
|
|
353
365
|
const multiAgent = loadMultiAgentSoakReport(projectRoot);
|
|
366
|
+
const multiAgentReal = loadMultiAgentRealReport(projectRoot);
|
|
354
367
|
const manifest = {
|
|
355
368
|
schema_version: 1,
|
|
356
369
|
generated_at: new Date().toISOString(),
|
|
@@ -384,8 +397,10 @@ function buildReleaseManifest(projectRoot, options = {}) {
|
|
|
384
397
|
},
|
|
385
398
|
reports: {
|
|
386
399
|
runtime_smoke: smoke,
|
|
400
|
+
runtime_real: runtimeReal,
|
|
387
401
|
recovery_fixtures: recovery,
|
|
388
402
|
multi_agent_soak: multiAgent,
|
|
403
|
+
multi_agent_real: multiAgentReal,
|
|
389
404
|
},
|
|
390
405
|
};
|
|
391
406
|
if (options.writeManifest) {
|
|
@@ -449,12 +464,21 @@ function evaluateReleaseManifest(manifest, options = {}) {
|
|
|
449
464
|
if (!manifest.reports.runtime_smoke.present || !manifest.reports.runtime_smoke.ok) {
|
|
450
465
|
blockers.push('runtime smoke matrix incompleta ou com falhas');
|
|
451
466
|
}
|
|
467
|
+
if (!manifest.reports.runtime_real.present || !manifest.reports.runtime_real.ok) {
|
|
468
|
+
blockers.push('runtime real report incompleto ou com falhas');
|
|
469
|
+
}
|
|
452
470
|
if (!manifest.reports.recovery_fixtures.present || !manifest.reports.recovery_fixtures.ok) {
|
|
453
471
|
blockers.push('recovery fixture report incompleto ou com falhas');
|
|
454
472
|
}
|
|
455
473
|
if (!manifest.reports.multi_agent_soak.present || !manifest.reports.multi_agent_soak.ok) {
|
|
456
474
|
blockers.push('multi-agent soak report incompleto ou com falhas');
|
|
457
475
|
}
|
|
476
|
+
const releaseVersion = semver.valid(versions.root) ? versions.root : null;
|
|
477
|
+
if (releaseVersion && semver.gte(releaseVersion, '1.9.1')) {
|
|
478
|
+
if (!manifest.reports.multi_agent_real.present || !manifest.reports.multi_agent_real.ok) {
|
|
479
|
+
blockers.push('multi-agent real report incompleto ou com falhas');
|
|
480
|
+
}
|
|
481
|
+
}
|
|
458
482
|
if (versions.banner.mode === 'unknown') {
|
|
459
483
|
warnings.push('banner.txt sem placeholder v{version} nem versão fixa detectável');
|
|
460
484
|
}
|
|
@@ -486,8 +510,10 @@ module.exports = {
|
|
|
486
510
|
releasePaths,
|
|
487
511
|
collectWrapperHashes,
|
|
488
512
|
loadRuntimeSmokeReport,
|
|
513
|
+
loadRuntimeRealReport,
|
|
489
514
|
loadRecoveryFixtureReport,
|
|
490
515
|
loadMultiAgentSoakReport,
|
|
516
|
+
loadMultiAgentRealReport,
|
|
491
517
|
buildReleaseManifest,
|
|
492
518
|
inspectCanonicalSource,
|
|
493
519
|
evaluateReleaseManifest,
|
package/bin/oxe-cc.js
CHANGED
|
@@ -4764,23 +4764,32 @@ async function runRuntime(opts) {
|
|
|
4764
4764
|
}
|
|
4765
4765
|
},
|
|
4766
4766
|
});
|
|
4767
|
-
if (opts.jsonOutput) {
|
|
4768
|
-
console.log(JSON.stringify(result, null, 2));
|
|
4769
|
-
if (result.result && result.result.failed && result.result.failed.length > 0) process.exitCode = 1;
|
|
4770
|
-
return;
|
|
4771
|
-
}
|
|
4772
|
-
const r = result.result || {};
|
|
4773
|
-
const completed = Array.isArray(r.completed) ? r.completed : [];
|
|
4774
|
-
const failed = Array.isArray(r.failed) ? r.failed : [];
|
|
4775
|
-
const blocked = Array.isArray(r.blocked) ? r.blocked : [];
|
|
4776
|
-
|
|
4777
|
-
console.log(` ${c ?
|
|
4767
|
+
if (opts.jsonOutput) {
|
|
4768
|
+
console.log(JSON.stringify(result, null, 2));
|
|
4769
|
+
if (result.result && ((result.result.failed && result.result.failed.length > 0) || result.result.status === 'blocked')) process.exitCode = 1;
|
|
4770
|
+
return;
|
|
4771
|
+
}
|
|
4772
|
+
const r = result.result || {};
|
|
4773
|
+
const completed = Array.isArray(r.completed) ? r.completed : [];
|
|
4774
|
+
const failed = Array.isArray(r.failed) ? r.failed : [];
|
|
4775
|
+
const blocked = Array.isArray(r.blocked) ? r.blocked : [];
|
|
4776
|
+
const okColor = r.status === 'blocked' ? yellow : green;
|
|
4777
|
+
console.log(` ${c ? okColor : ''}${r.status === 'blocked' ? '!' : '✓'}${c ? reset : ''} Runtime execute concluído (modo: ${result.mode})`);
|
|
4778
|
+
if (result.preflight) {
|
|
4779
|
+
console.log(` ${c ? green : ''}Preflight:${c ? reset : ''} ${result.preflight.ok ? 'ready' : 'blocked'}`);
|
|
4780
|
+
if (Array.isArray(result.preflight.blockers) && result.preflight.blockers.length) {
|
|
4781
|
+
for (const blocker of result.preflight.blockers) {
|
|
4782
|
+
console.log(` ${yellow}BLOCKER${reset} ${blocker}`);
|
|
4783
|
+
}
|
|
4784
|
+
}
|
|
4785
|
+
}
|
|
4786
|
+
console.log(` ${c ? green : ''}Completados:${c ? reset : ''} ${completed.length}`);
|
|
4778
4787
|
if (failed.length > 0)
|
|
4779
4788
|
console.log(` ${c ? red : ''}Falhos:${c ? reset : ''} ${failed.length} — ${failed.join(', ')}`);
|
|
4780
4789
|
if (blocked.length > 0)
|
|
4781
4790
|
console.log(` ${c ? '\x1b[33m' : ''}Bloqueados:${c ? reset : ''} ${blocked.length} — ${blocked.join(', ')}`);
|
|
4782
|
-
if (failed.length > 0) process.exitCode = 1;
|
|
4783
|
-
return;
|
|
4791
|
+
if (failed.length > 0 || r.status === 'blocked') process.exitCode = 1;
|
|
4792
|
+
return;
|
|
4784
4793
|
} catch (err) {
|
|
4785
4794
|
console.error(`${red}${err && err.message ? err.message : 'Falha ao executar runtime.'}${reset}`);
|
|
4786
4795
|
process.exit(1);
|
|
@@ -8,6 +8,7 @@ Este é o contrato mínimo para publicar uma versão estável do OXE sem drift e
|
|
|
8
8
|
npm test
|
|
9
9
|
npm run scan:assets
|
|
10
10
|
npm run build:vscode-ext
|
|
11
|
+
npm run release:pack-check
|
|
11
12
|
npx oxe-cc doctor --release --write-manifest
|
|
12
13
|
```
|
|
13
14
|
|
|
@@ -21,6 +22,7 @@ O `doctor --release` deve bloquear a publicação quando encontrar:
|
|
|
21
22
|
- wrappers dirty após `sync-runtime-metadata` e `sync:cursor`
|
|
22
23
|
- drift semântico entre workflows canónicos e superfícies geradas
|
|
23
24
|
- ausência ou falha dos relatórios obrigatórios da release
|
|
25
|
+
- tarball npm contendo `.tgz`, `.vsix`, `.oxe/` ou sem arquivos obrigatórios do pacote
|
|
24
26
|
|
|
25
27
|
## Relatórios obrigatórios
|
|
26
28
|
|
|
@@ -28,8 +30,10 @@ Todos os artefatos abaixo devem existir em `.oxe/release/`:
|
|
|
28
30
|
|
|
29
31
|
- `release-manifest.json`
|
|
30
32
|
- `runtime-smoke-report.json`
|
|
33
|
+
- `runtime-real-report.json`
|
|
31
34
|
- `recovery-fixture-report.json`
|
|
32
35
|
- `multi-agent-soak-report.json`
|
|
36
|
+
- `multi-agent-real-report.json` para versões `>=1.9.1`
|
|
33
37
|
|
|
34
38
|
## Defaults estáveis desta publicação
|
|
35
39
|
|
|
@@ -44,7 +48,8 @@ O pipeline de CI e o pipeline de release devem rodar o mesmo gate:
|
|
|
44
48
|
|
|
45
49
|
1. `npm test`
|
|
46
50
|
2. `npm run scan:assets`
|
|
47
|
-
3. `npm run release:
|
|
51
|
+
3. `npm run release:pack-check`
|
|
52
|
+
4. `npm run release:doctor`
|
|
48
53
|
|
|
49
54
|
Se qualquer etapa falhar, a release não está pronta.
|
|
50
55
|
|