oxe-cc 1.8.3 → 1.10.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.
Files changed (42) hide show
  1. package/.cursor/commands/oxe-dashboard.md +2 -2
  2. package/.cursor/commands/oxe-execute.md +2 -2
  3. package/.cursor/commands/oxe-plan.md +2 -2
  4. package/.cursor/commands/oxe-quick.md +2 -2
  5. package/.cursor/commands/oxe-spec.md +3 -3
  6. package/.github/prompts/oxe-dashboard.prompt.md +2 -2
  7. package/.github/prompts/oxe-execute.prompt.md +2 -2
  8. package/.github/prompts/oxe-plan.prompt.md +2 -2
  9. package/.github/prompts/oxe-quick.prompt.md +2 -2
  10. package/.github/prompts/oxe-spec.prompt.md +3 -3
  11. package/AGENTS.md +1 -1
  12. package/CHANGELOG.md +45 -0
  13. package/README.md +32 -26
  14. package/bin/lib/oxe-context-engine.cjs +2 -0
  15. package/bin/lib/oxe-operational.cjs +230 -74
  16. package/bin/lib/oxe-project-health.cjs +43 -9
  17. package/bin/lib/oxe-rationality.cjs +146 -1
  18. package/bin/lib/oxe-release.cjs +55 -0
  19. package/bin/oxe-cc.js +60 -37
  20. package/commands/oxe/dashboard.md +2 -2
  21. package/commands/oxe/execute.md +2 -2
  22. package/commands/oxe/plan.md +2 -2
  23. package/commands/oxe/quick.md +2 -2
  24. package/commands/oxe/spec.md +3 -3
  25. package/docs/RELEASE-READINESS.md +8 -1
  26. package/lib/runtime/scheduler/multi-agent-coordinator.d.ts +48 -0
  27. package/lib/runtime/scheduler/multi-agent-coordinator.js +274 -14
  28. package/lib/runtime/workspace/strategies/git-worktree.js +18 -9
  29. package/oxe/templates/REFERENCE-ANCHORS.template.md +12 -5
  30. package/oxe/templates/SPEC.template.md +10 -0
  31. package/oxe/templates/VISUAL-INPUTS.template.json +27 -0
  32. package/oxe/templates/VISUAL-INPUTS.template.md +36 -0
  33. package/oxe/workflows/execute.md +3 -0
  34. package/oxe/workflows/plan.md +13 -9
  35. package/oxe/workflows/references/workflow-runtime-contracts.json +44 -29
  36. package/oxe/workflows/spec.md +19 -8
  37. package/oxe/workflows/ui-spec.md +3 -2
  38. package/package.json +6 -3
  39. package/packages/runtime/package.json +1 -1
  40. package/packages/runtime/src/scheduler/multi-agent-coordinator.ts +379 -47
  41. package/packages/runtime/src/workspace/strategies/git-worktree.ts +24 -16
  42. package/vscode-extension/package.json +1 -1
@@ -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,26 +905,49 @@ 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
- };
840
- }
908
+ orphanReassignments: [],
909
+ handoffs: [],
910
+ arbitrationResults: [],
911
+ workspaceMergeReport: null,
912
+ worktrees: [],
913
+ mergeBlockers: [],
914
+ mergeReadiness: null,
915
+ arbitrationRequired: false,
916
+ health: 'unknown',
917
+ nextAction: 'Execute ou compile uma run antes de inspecionar status multi-agent.',
918
+ summary: null,
919
+ };
920
+ }
841
921
  const runDir = path.join(projectRoot, '.oxe', 'runs', runId);
842
922
  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');
923
+ const summaryPath = path.join(runDir, 'multi-agent-summary.json');
924
+ const handoffsPath = path.join(runDir, 'handoffs.json');
925
+ const arbitrationPath = path.join(runDir, 'arbitration-results.json');
926
+ const workspaceMergePath = path.join(runDir, 'workspace-merge-report.json');
846
927
  const state = runtime && typeof runtime.loadMultiAgentState === 'function'
847
928
  ? runtime.loadMultiAgentState(projectRoot, runId)
848
929
  : readJsonIfExists(statePath);
849
- const summary = runtime && typeof runtime.loadMultiAgentSummary === 'function'
850
- ? runtime.loadMultiAgentSummary(projectRoot, runId)
851
- : readJsonIfExists(summaryPath);
852
- const handoffs = readJsonIfExists(handoffsPath);
853
- const arbitrationResults = readJsonIfExists(arbitrationPath);
854
- return {
930
+ const summary = runtime && typeof runtime.loadMultiAgentSummary === 'function'
931
+ ? runtime.loadMultiAgentSummary(projectRoot, runId)
932
+ : readJsonIfExists(summaryPath);
933
+ const workspaceMergeReport = runtime && typeof runtime.loadWorkspaceMergeReport === 'function'
934
+ ? runtime.loadWorkspaceMergeReport(projectRoot, runId)
935
+ : readJsonIfExists(workspaceMergePath);
936
+ const handoffs = readJsonIfExists(handoffsPath);
937
+ const arbitrationResults = readJsonIfExists(arbitrationPath);
938
+ const mergeBlockers = workspaceMergeReport && Array.isArray(workspaceMergeReport.blockers) ? workspaceMergeReport.blockers : [];
939
+ const mergeReadiness = workspaceMergeReport && workspaceMergeReport.merge_readiness ? workspaceMergeReport.merge_readiness : null;
940
+ let nextAction = null;
941
+ if (!state) {
942
+ nextAction = 'Execute runtime com plan-agents.json válido para materializar o estado multi-agent.';
943
+ } else if (mergeBlockers.length > 0) {
944
+ nextAction = 'Resolva os merge blockers do workspace antes de promover ou aplicar novos resultados.';
945
+ } else if (mergeReadiness === 'partial') {
946
+ nextAction = 'Conclua verify/evidence pós-merge ou aplique os worktrees pendentes antes de fechar a run.';
947
+ } else if (mergeReadiness === 'ready') {
948
+ nextAction = 'Multi-agent merge pronto; avance para runtime verify ou promotion conforme o ciclo.';
949
+ }
950
+ return {
855
951
  path: statePath,
856
952
  enabled: Boolean(state),
857
953
  runId,
@@ -860,11 +956,44 @@ function readRuntimeMultiAgentStatus(projectRoot, activeSession, options = {}) {
860
956
  agents: state && Array.isArray(state.agent_results) ? state.agent_results : [],
861
957
  ownership: state && Array.isArray(state.ownership) ? state.ownership : [],
862
958
  orphanReassignments: state && Array.isArray(state.orphan_reassignments) ? state.orphan_reassignments : [],
863
- handoffs: Array.isArray(handoffs) ? handoffs : [],
864
- arbitrationResults: Array.isArray(arbitrationResults) ? arbitrationResults : [],
865
- summary: summary || null,
866
- };
867
- }
959
+ handoffs: Array.isArray(handoffs) ? handoffs : [],
960
+ arbitrationResults: Array.isArray(arbitrationResults) ? arbitrationResults : [],
961
+ workspaceMergeReport: workspaceMergeReport || null,
962
+ worktrees: workspaceMergeReport && Array.isArray(workspaceMergeReport.records) ? workspaceMergeReport.records : [],
963
+ mergeBlockers,
964
+ mergeReadiness,
965
+ arbitrationRequired: Boolean(workspaceMergeReport && workspaceMergeReport.arbitration_required),
966
+ health: summary && summary.health ? summary.health : (mergeBlockers.length > 0 ? 'degraded' : 'unknown'),
967
+ nextAction,
968
+ summary: summary || null,
969
+ };
970
+ }
971
+
972
+ function validateMultiAgentPlan(agentPlan) {
973
+ const errors = [];
974
+ const allowedModes = new Set(['parallel', 'competitive', 'cooperative']);
975
+ const mode = agentPlan && agentPlan.mode ? String(agentPlan.mode) : 'parallel';
976
+ if (!allowedModes.has(mode)) errors.push(`mode inválido: ${mode}`);
977
+ if (!Array.isArray(agentPlan && agentPlan.agents) || agentPlan.agents.length === 0) {
978
+ errors.push('campo "agents" vazio ou ausente');
979
+ return errors;
980
+ }
981
+ const schemaVersion = Number(agentPlan.schema_version || agentPlan.schema || 0);
982
+ const seen = new Set();
983
+ for (const [idx, spec] of agentPlan.agents.entries()) {
984
+ const id = spec && spec.id ? String(spec.id) : '';
985
+ if (!id) errors.push(`agents[${idx}].id ausente`);
986
+ if (id && seen.has(id)) errors.push(`agent duplicado: ${id}`);
987
+ if (id) seen.add(id);
988
+ if (schemaVersion >= 3 && !spec.persona) errors.push(`${id || `agents[${idx}]`}.persona ausente`);
989
+ if (schemaVersion >= 3 && !spec.model_hint) errors.push(`${id || `agents[${idx}]`}.model_hint ausente`);
990
+ if (spec.tasks != null && !Array.isArray(spec.tasks)) errors.push(`${id || `agents[${idx}]`}.tasks deve ser array`);
991
+ }
992
+ if ((mode === 'competitive' || mode === 'cooperative') && agentPlan.agents.length < 2) {
993
+ errors.push(`${mode} exige pelo menos 2 agentes`);
994
+ }
995
+ return errors;
996
+ }
868
997
 
869
998
  function loadRuntimeVerificationArtifacts(projectRoot, runState) {
870
999
  const runtime = loadRuntimeModule();
@@ -890,7 +1019,7 @@ function loadRuntimeVerificationArtifacts(projectRoot, runState) {
890
1019
  return { manifest, residualRisks, evidenceCoverage };
891
1020
  }
892
1021
 
893
- function countVerificationEvidenceRefs(runState, verificationArtifacts) {
1022
+ function countVerificationEvidenceRefs(runState, verificationArtifacts) {
894
1023
  if (verificationArtifacts && verificationArtifacts.manifest && Array.isArray(verificationArtifacts.manifest.checks)) {
895
1024
  return verificationArtifacts.manifest.checks.reduce((sum, check) => {
896
1025
  return sum + (Array.isArray(check.evidence_refs) ? check.evidence_refs.length : 0);
@@ -902,7 +1031,26 @@ function countVerificationEvidenceRefs(runState, verificationArtifacts) {
902
1031
  }, 0);
903
1032
  }
904
1033
  return 0;
905
- }
1034
+ }
1035
+
1036
+ function detectOrphanWorktrees(projectRoot, runId) {
1037
+ const workspacesDir = path.join(projectRoot, '.oxe', 'workspaces');
1038
+ if (!fs.existsSync(workspacesDir)) return [];
1039
+ const active = new Set();
1040
+ const mergeReport = readJsonIfExists(path.join(projectRoot, '.oxe', 'runs', runId, 'workspace-merge-report.json'));
1041
+ for (const record of (mergeReport && Array.isArray(mergeReport.records) ? mergeReport.records : [])) {
1042
+ if (record && record.workspace_id) active.add(String(record.workspace_id));
1043
+ }
1044
+ return fs.readdirSync(workspacesDir, { withFileTypes: true })
1045
+ .filter((entry) => entry.isDirectory())
1046
+ .map((entry) => entry.name)
1047
+ .filter((name) => !active.has(name))
1048
+ .map((name) => ({
1049
+ workspace_id: name,
1050
+ path: path.join(workspacesDir, name),
1051
+ next_action: 'inspecione o worktree órfão; depois remova com git worktree remove --force se não houver diffs úteis',
1052
+ }));
1053
+ }
906
1054
 
907
1055
  function buildRuntimeModeStatus(runState) {
908
1056
  if (!runState) {
@@ -1074,11 +1222,17 @@ function writeRecoverySummaryMarkdown(projectRoot, activeSession, runState, reco
1074
1222
  '',
1075
1223
  '## Work items órfãos',
1076
1224
  '',
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',
1225
+ ...(Array.isArray(recoverySummary.orphan_work_items) && recoverySummary.orphan_work_items.length
1226
+ ? recoverySummary.orphan_work_items.map((item) => `- ${item}`)
1227
+ : ['- Nenhum']),
1228
+ '',
1229
+ '## Worktrees órfãos',
1230
+ '',
1231
+ ...(Array.isArray(recoverySummary.orphan_worktrees) && recoverySummary.orphan_worktrees.length
1232
+ ? recoverySummary.orphan_worktrees.map((item) => `- ${item.workspace_id} · ${item.next_action}`)
1233
+ : ['- Nenhum']),
1234
+ '',
1235
+ '## Tentativas incompletas',
1082
1236
  '',
1083
1237
  ...(recoverySummary.consistency && Array.isArray(recoverySummary.consistency.incomplete_attempts) && recoverySummary.consistency.incomplete_attempts.length
1084
1238
  ? recoverySummary.consistency.incomplete_attempts.map((item) => `- ${item.work_item_id} · ${item.attempt_id || 'attempt'} · ${item.outcome || 'unknown'}`)
@@ -1441,13 +1595,14 @@ function recoverRuntimeState(projectRoot, activeSession, options = {}) {
1441
1595
  journal,
1442
1596
  verificationArtifacts
1443
1597
  );
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
- };
1598
+ const recoverySummary = {
1599
+ recovered_at: new Date().toISOString(),
1600
+ journal_state: journal.scheduler_state,
1601
+ orphan_work_items: orphanWorkItems,
1602
+ orphan_worktrees: detectOrphanWorktrees(projectRoot, current.run_id),
1603
+ pending_gates: readRuntimeGates(projectRoot, activeSession, { runId: current.run_id }).pending.map((gate) => gate.gate_id),
1604
+ consistency,
1605
+ };
1451
1606
  const runDir = path.join(projectRoot, '.oxe', 'runs', current.run_id);
1452
1607
  ensureDir(runDir);
1453
1608
  const recoverySummaryPath = path.join(runDir, 'recovery-summary.json');
@@ -2329,8 +2484,9 @@ module.exports = {
2329
2484
  buildRecoveryConsistency,
2330
2485
  readRuntimeGates,
2331
2486
  resolveRuntimeGate,
2332
- createExecutionContext,
2333
- runRuntimeExecute,
2487
+ createExecutionContext,
2488
+ buildRuntimeExecutePreflight,
2489
+ runRuntimeExecute,
2334
2490
  runRuntimeVerify,
2335
2491
  projectRuntimeArtifacts,
2336
2492
  runRuntimeCiChecks,
@@ -658,6 +658,8 @@ function oxePaths(target) {
658
658
  referenceAnchors: path.join(oxe, 'REFERENCE-ANCHORS.md'),
659
659
  fixturePackMd: path.join(oxe, 'FIXTURE-PACK.md'),
660
660
  fixturePackJson: path.join(oxe, 'FIXTURE-PACK.json'),
661
+ visualInputsMd: path.join(oxe, 'investigations', 'visual', 'VISUAL-INPUTS.md'),
662
+ visualInputsJson: path.join(oxe, 'investigations', 'visual', 'VISUAL-INPUTS.json'),
661
663
  quick: path.join(oxe, 'QUICK.md'),
662
664
  verify: path.join(oxe, 'VERIFY.md'),
663
665
  discuss: path.join(oxe, 'DISCUSS.md'),
@@ -700,6 +702,8 @@ function scopedOxePaths(target, activeSession) {
700
702
  referenceAnchors: path.join(sessionRoot, 'plan', 'REFERENCE-ANCHORS.md'),
701
703
  fixturePackMd: path.join(sessionRoot, 'plan', 'FIXTURE-PACK.md'),
702
704
  fixturePackJson: path.join(sessionRoot, 'plan', 'FIXTURE-PACK.json'),
705
+ visualInputsMd: path.join(sessionRoot, 'research', 'investigations', 'visual', 'VISUAL-INPUTS.md'),
706
+ visualInputsJson: path.join(sessionRoot, 'research', 'investigations', 'visual', 'VISUAL-INPUTS.json'),
703
707
  quick: path.join(sessionRoot, 'plan', 'QUICK.md'),
704
708
  verify: path.join(sessionRoot, 'verification', 'VERIFY.md'),
705
709
  summary: path.join(sessionRoot, 'verification', 'SUMMARY.md'),
@@ -741,6 +745,8 @@ function resolvedReadableOxePaths(target, activeSession) {
741
745
  referenceAnchors: preferScoped('referenceAnchors'),
742
746
  fixturePackMd: preferScoped('fixturePackMd'),
743
747
  fixturePackJson: preferScoped('fixturePackJson'),
748
+ visualInputsMd: preferScoped('visualInputsMd'),
749
+ visualInputsJson: preferScoped('visualInputsJson'),
744
750
  quick: preferScoped('quick'),
745
751
  verify: preferScoped('verify'),
746
752
  summary: preferScoped('summary'),
@@ -1317,8 +1323,8 @@ function listOxePromptFiles(dir) {
1317
1323
  if (!fs.existsSync(dir)) return [];
1318
1324
  return fs
1319
1325
  .readdirSync(dir, { withFileTypes: true })
1320
- .filter((entry) => entry.isFile() && /^oxe-.*\.prompt\.md$/i.test(entry.name))
1321
- .map((entry) => path.join(dir, entry.name))
1326
+ .filter((entry) => entry.isFile() && (entry.name === 'oxe.prompt.md' || /^oxe-.*\.prompt\.md$/i.test(entry.name)))
1327
+ .map((entry) => path.join(dir, entry.name))
1322
1328
  .sort();
1323
1329
  }
1324
1330
 
@@ -1439,9 +1445,9 @@ function copilotIntegrationReport(target) {
1439
1445
  if (legacyHasOxeBlock) {
1440
1446
  warnings.push('Bloco OXE legado detectado em ~/.copilot/copilot-instructions.md');
1441
1447
  }
1442
- if (legacyHasOtherManagedBlocks) {
1443
- warnings.push('copilot-instructions global contém blocos geridos por outro framework; isso pode contaminar respostas do Copilot');
1444
- }
1448
+ if (legacyHasOtherManagedBlocks) {
1449
+ warnings.push('copilot-instructions global contém blocos geridos por outro framework; isso pode contaminar respostas do Copilot — execute `oxe-cc uninstall --copilot-legacy-clean` se quiser limpar o legado global.');
1450
+ }
1445
1451
  if (!manifestRaw.ok && fs.existsSync(workspace.manifest)) {
1446
1452
  warnings.push(`Manifesto Copilot VS Code inválido: ${manifestRaw.error}`);
1447
1453
  } else if (!fs.existsSync(workspace.manifest) && workspacePromptFiles.length > 0) {
@@ -1856,11 +1862,14 @@ function planSelfEvaluationWarnings(planPath, threshold) {
1856
1862
  * implementationPackReady: boolean,
1857
1863
  * referenceAnchorsReady: boolean,
1858
1864
  * fixturePackReady: boolean,
1865
+ * visualInputReadiness?: string,
1866
+ * visualInputsReady?: boolean,
1859
1867
  * executionRationalityReady: boolean,
1860
1868
  * criticalExecutionGaps: string[],
1861
1869
  * implementationPack: { path?: string | null, tasks?: unknown[] } | null,
1862
1870
  * referenceAnchors: { path?: string | null, anchors?: unknown[], missingCriticalCount?: number } | null,
1863
1871
  * fixturePack: { path?: string | null, fixtures?: unknown[] } | null,
1872
+ * visualInputs?: { path?: string | null, inputCount?: number, criticalInputCount?: number } | null,
1864
1873
  * }} summary
1865
1874
  * @returns {string[]}
1866
1875
  */
@@ -1877,6 +1886,9 @@ function executionRationalityWarningsFromSummary(summary) {
1877
1886
  if (!summary.fixturePackReady) {
1878
1887
  warns.push(`FIXTURE-PACK não está pronto em ${summary.fixturePack && summary.fixturePack.path ? summary.fixturePack.path : '.oxe/FIXTURE-PACK.json'}`);
1879
1888
  }
1889
+ if (summary.visualInputReadiness === 'blocked') {
1890
+ warns.push(`VISUAL-INPUTS bloqueado em ${summary.visualInputs && summary.visualInputs.path ? summary.visualInputs.path : '.oxe/investigations/visual/VISUAL-INPUTS.json'}`);
1891
+ }
1880
1892
  if (Array.isArray(summary.criticalExecutionGaps) && summary.criticalExecutionGaps.length) {
1881
1893
  warns.push(...summary.criticalExecutionGaps);
1882
1894
  }
@@ -2070,8 +2082,10 @@ function suggestNextStep(target, cfg = {}) {
2070
2082
  artifacts: [
2071
2083
  '.oxe/release/release-manifest.json',
2072
2084
  '.oxe/release/runtime-smoke-report.json',
2085
+ '.oxe/release/runtime-real-report.json',
2073
2086
  '.oxe/release/recovery-fixture-report.json',
2074
2087
  '.oxe/release/multi-agent-soak-report.json',
2088
+ '.oxe/release/multi-agent-real-report.json',
2075
2089
  ],
2076
2090
  };
2077
2091
  }
@@ -2189,6 +2203,9 @@ function suggestNextStep(target, cfg = {}) {
2189
2203
  referenceAnchors: p.referenceAnchors,
2190
2204
  fixturePackJson: p.fixturePackJson,
2191
2205
  fixturePackMd: p.fixturePackMd,
2206
+ spec: p.spec,
2207
+ visualInputsJson: p.visualInputsJson,
2208
+ visualInputsMd: p.visualInputsMd,
2192
2209
  });
2193
2210
  if (
2194
2211
  shouldEnforceExecutionRationalityGate(phase)
@@ -2366,6 +2383,9 @@ function buildHealthReport(target) {
2366
2383
  referenceAnchors: p.referenceAnchors,
2367
2384
  fixturePackJson: p.fixturePackJson,
2368
2385
  fixturePackMd: p.fixturePackMd,
2386
+ spec: p.spec,
2387
+ visualInputsJson: p.visualInputsJson,
2388
+ visualInputsMd: p.visualInputsMd,
2369
2389
  });
2370
2390
  const suppressExecutionWorkspaceGates = shouldSuppressExecutionWorkspaceGates(
2371
2391
  workspaceInfo.workspaceMode,
@@ -2518,10 +2538,22 @@ function buildHealthReport(target) {
2518
2538
  contextQuality.primaryStatus = contextPacks[firstWorkflow].context_quality.status;
2519
2539
  }
2520
2540
  }
2521
- } catch (err) {
2522
- contextWarn.push(`Contexto — falha ao inspecionar context packs: ${err instanceof Error ? err.message : String(err)}`);
2523
- }
2524
- const semanticsManifest = readJsonFileSafe(base.runtimeSemanticsManifest);
2541
+ } catch (err) {
2542
+ contextWarn.push(`Contexto — falha ao inspecionar context packs: ${err instanceof Error ? err.message : String(err)}`);
2543
+ }
2544
+ if (suppressExecutionWorkspaceGates) {
2545
+ for (const key of Object.keys(contextPacks)) delete contextPacks[key];
2546
+ for (const key of Object.keys(packFreshness)) delete packFreshness[key];
2547
+ contextWarn.length = 0;
2548
+ activeSummaryRefs = { project: null, session: null, phase: null };
2549
+ contextQuality = {
2550
+ primaryWorkflow: 'release',
2551
+ primaryScore: null,
2552
+ primaryStatus: 'not_applicable',
2553
+ byWorkflow: {},
2554
+ };
2555
+ }
2556
+ const semanticsManifest = readJsonFileSafe(base.runtimeSemanticsManifest);
2525
2557
  const semanticsAudit = runtimeSemantics.auditRuntimeTargets(target);
2526
2558
  /** @type {string[]} */
2527
2559
  const semanticsWarn = [];
@@ -2624,6 +2656,8 @@ function buildHealthReport(target) {
2624
2656
  implementationPackReady: executionRationality.implementationPackReady,
2625
2657
  referenceAnchorsReady: executionRationality.referenceAnchorsReady,
2626
2658
  fixturePackReady: executionRationality.fixturePackReady,
2659
+ visualInputReadiness: executionRationality.visualInputReadiness,
2660
+ visualInputsReady: executionRationality.visualInputsReady,
2627
2661
  executionRationalityReady: executionRationality.executionRationalityReady,
2628
2662
  criticalExecutionGaps: executionRationality.criticalExecutionGaps,
2629
2663
  executionRationality,