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 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
  [![npm](https://img.shields.io/npm/v/oxe-cc.svg?style=flat-square)](https://www.npmjs.com/package/oxe-cc)
8
8
  [![license](https://img.shields.io/npm/l/oxe-cc.svg?style=flat-square)](LICENSE)
9
9
 
10
- **Versão:** `1.8.3` · [package.json](package.json)
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
- - `node bin/oxe-cc.js status --full`
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/recovery-fixture-report.json`
473
- - `.oxe/release/multi-agent-soak-report.json`
474
-
475
- Não há outro bloqueador funcional do plano runtime core para esta publicação. O que sobra depois dela é evolução de ergonomia e expansão de targets, não correção estrutural do contrato atual.
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
- async function runRuntimeExecute(projectRoot, activeSession, options = {}) {
727
- const runtime = loadRuntimeModule();
728
- if (!runtime) throw new Error('Runtime package não está disponível. Rode npm run build:runtime.');
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
- if (!current || !current.compiled_graph) {
749
- current = compileExecutionGraphFromArtifacts(projectRoot, activeSession, { runState: current }).run;
750
- }
751
- if (!current || !current.compiled_graph) {
752
- throw new Error('Nenhum grafo compilado encontrado. Execute oxe-cc runtime compile primeiro.');
753
- }
754
- const graph = runtime.fromSerializable
755
- ? runtime.fromSerializable(current.compiled_graph)
756
- : current.compiled_graph;
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
- if (!Array.isArray(agentPlan.agents) || agentPlan.agents.length === 0) {
789
- throw new Error('plan-agents.json não contém agentes válidos (campo "agents" vazio ou ausente).');
790
- }
791
- if (typeof runtime.MultiAgentCoordinator !== 'function') {
792
- throw new Error('Runtime não exporta MultiAgentCoordinator. Verifique a versão do runtime.');
793
- }
794
- const agents = agentPlan.agents.map((spec) => ({
795
- id: spec.id,
796
- executor: options.executorFactory ? options.executorFactory(spec) : (options.executor || null),
797
- workspaceManager: options.workspaceManager || null,
798
- assignedTaskIds: Array.isArray(spec.tasks) ? spec.tasks : [],
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
- onEvent: ctx.onEvent,
809
- });
810
- return { mode: agentPlan.mode || 'parallel', agentPlan, result, run: current };
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
- return { mode: 'single', agentPlan: null, result, run: current };
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
- summary: null,
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
- summary: summary || null,
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
- '## Tentativas incompletas',
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
- pending_gates: readRuntimeGates(projectRoot, activeSession, { runId: current.run_id }).pending.map((gate) => gate.gate_id),
1449
- consistency,
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
- runRuntimeExecute,
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
  }
@@ -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
- console.log(` ${c ? green : ''}✓${c ? reset : ''} Runtime execute concluído (modo: ${result.mode})`);
4777
- console.log(` ${c ? green : ''}Completados:${c ? reset : ''} ${completed.length}`);
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:doctor`
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