oxe-cc 1.7.0 → 1.8.3
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/CHANGELOG.md +106 -0
- package/README.md +37 -37
- package/bin/lib/oxe-agent-install.cjs +24 -8
- package/bin/lib/oxe-manifest.cjs +20 -13
- package/bin/lib/oxe-operational.cjs +234 -41
- package/bin/lib/oxe-project-health.cjs +219 -52
- package/bin/lib/oxe-rationality.cjs +9 -7
- package/bin/oxe-cc.js +443 -236
- package/lib/runtime/compiler/graph-compiler.js +1 -1
- package/lib/runtime/executor/action-tool-map.js +4 -0
- package/lib/runtime/executor/built-in-tools.js +27 -0
- package/lib/runtime/executor/llm-task-executor.d.ts +4 -1
- package/lib/runtime/executor/llm-task-executor.js +41 -5
- package/lib/runtime/executor/node-prompt-builder.d.ts +4 -1
- package/lib/runtime/executor/node-prompt-builder.js +13 -2
- package/lib/runtime/models/failure.d.ts +1 -1
- package/lib/runtime/scheduler/scheduler.d.ts +5 -1
- package/lib/runtime/scheduler/scheduler.js +82 -14
- package/lib/runtime/verification/verification-compiler.js +7 -5
- package/lib/sdk/index.cjs +48 -44
- package/oxe/templates/PLAN.template.md +23 -9
- package/oxe/templates/SPEC.template.md +55 -22
- package/oxe/workflows/plan.md +18 -6
- package/oxe/workflows/spec.md +31 -9
- package/package.json +103 -100
- package/packages/runtime/package.json +14 -14
- package/packages/runtime/src/compiler/graph-compiler.ts +1 -1
- package/packages/runtime/src/evidence/evidence-store.ts +2 -2
- package/packages/runtime/src/executor/action-tool-map.ts +4 -0
- package/packages/runtime/src/executor/built-in-tools.ts +29 -0
- package/packages/runtime/src/executor/llm-task-executor.ts +46 -4
- package/packages/runtime/src/executor/node-prompt-builder.ts +18 -1
- package/packages/runtime/src/models/failure.ts +2 -0
- package/packages/runtime/src/scheduler/scheduler.ts +93 -15
- package/packages/runtime/src/verification/verification-compiler.ts +7 -5
- package/vscode-extension/package.json +184 -184
- package/vscode-extension/oxe-agents-0.9.1.vsix +0 -0
- package/vscode-extension/oxe-agents-0.9.2.vsix +0 -0
- package/vscode-extension/oxe-agents-1.0.0.vsix +0 -0
- package/vscode-extension/oxe-agents-1.4.0.vsix +0 -0
- package/vscode-extension/oxe-agents-1.5.0.vsix +0 -0
- package/vscode-extension/oxe-agents-1.5.1.vsix +0 -0
- package/vscode-extension/oxe-agents-1.6.0.vsix +0 -0
- package/vscode-extension/oxe-agents-1.7.0.vsix +0 -0
|
@@ -79,6 +79,31 @@ function loadRuntimeModule() {
|
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
// Gap 1: factory that always wires GateManager into ctx
|
|
83
|
+
function createExecutionContext(projectRoot, activeSession, options = {}) {
|
|
84
|
+
const runtime = loadRuntimeModule();
|
|
85
|
+
const runId = options.runId || makeRunId();
|
|
86
|
+
const gateManager = (runtime && typeof runtime.GateManager === 'function')
|
|
87
|
+
? new runtime.GateManager(projectRoot, activeSession || null, runId)
|
|
88
|
+
: null;
|
|
89
|
+
return {
|
|
90
|
+
projectRoot,
|
|
91
|
+
sessionId: activeSession || null,
|
|
92
|
+
runId,
|
|
93
|
+
executor: options.executor || null,
|
|
94
|
+
workspaceManager: options.workspaceManager || null,
|
|
95
|
+
gateManager,
|
|
96
|
+
policyEngine: options.policyEngine || null,
|
|
97
|
+
policyActor: options.policyActor || 'runtime',
|
|
98
|
+
quota: options.quota || null,
|
|
99
|
+
pluginRegistry: options.pluginRegistry || null,
|
|
100
|
+
auditTrail: options.auditTrail || null,
|
|
101
|
+
evidenceStore: options.evidenceStore || null,
|
|
102
|
+
onEvent: options.onEvent || null,
|
|
103
|
+
options: options.schedulerOptions || {},
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
82
107
|
function buildRuntimePluginRegistry(projectRoot) {
|
|
83
108
|
const runtime = loadRuntimeModule();
|
|
84
109
|
if (!runtime || typeof runtime.PluginRegistry !== 'function') return null;
|
|
@@ -431,6 +456,61 @@ function reduceCanonicalRunState(projectRoot, activeSession, options = {}) {
|
|
|
431
456
|
return serializeCanonicalState(reduceCanonicalRunStateLive(projectRoot, activeSession, options));
|
|
432
457
|
}
|
|
433
458
|
|
|
459
|
+
/**
|
|
460
|
+
* Static-analysis lints for common pitfalls detectable before execution.
|
|
461
|
+
* Returns hint strings to be appended to validationErrors (shown at compile time).
|
|
462
|
+
*/
|
|
463
|
+
function lintPlanForCommonPitfalls(parsedPlan, parsedSpec, projectRoot, rawSpecText = '') {
|
|
464
|
+
const hints = [];
|
|
465
|
+
const specText = (parsedSpec && parsedSpec.objective ? parsedSpec.objective : '') +
|
|
466
|
+
JSON.stringify(parsedSpec && parsedSpec.criteria ? parsedSpec.criteria : []) +
|
|
467
|
+
(rawSpecText || '');
|
|
468
|
+
const planTasks = parsedPlan && Array.isArray(parsedPlan.tasks) ? parsedPlan.tasks : [];
|
|
469
|
+
|
|
470
|
+
// ── Lint 1: HTML/JS SPA sem restrição de file:// ──────────────────────────
|
|
471
|
+
// Detects: spec mentions HTML/SPA + no files restriction, but no verify command
|
|
472
|
+
// checks for fetch() absence. Warns to add a fetch-detection verify.
|
|
473
|
+
const isHtmlApp = /html|spa|browser|page|frontend|estático|static|aplicação web|web app|web page|interface web|\.html/i.test(specText);
|
|
474
|
+
if (isHtmlApp) {
|
|
475
|
+
const hasFetchGuard = planTasks.some(t =>
|
|
476
|
+
t.verifyCommand && /fetch|XMLHttpRequest|file:\/\//i.test(t.verifyCommand)
|
|
477
|
+
);
|
|
478
|
+
const specMentionsServer = /servidor|server|http-server|localhost|npx serve|vite|webpack/i.test(specText);
|
|
479
|
+
const specMentionsFileProtocol = /file:\/\/|sem servidor|without server|no.server/i.test(specText);
|
|
480
|
+
if (!hasFetchGuard && !specMentionsServer && !specMentionsFileProtocol) {
|
|
481
|
+
hints.push(
|
|
482
|
+
'HINT(html-fetch): SPEC não declara se o app precisa de servidor HTTP. ' +
|
|
483
|
+
'Se abrir em file://, adicione à SPEC: "sem servidor HTTP" e um verify que detecte fetch(): ' +
|
|
484
|
+
'`node -e "if(require(\'fs\').readFileSync(\'app.js\',\'utf8\').includes(\'fetch(\'))throw new Error(\'fetch not allowed in file://\')"`'
|
|
485
|
+
);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// ── Lint 2: Verify commands que só verificam existência de função, não comportamento ──
|
|
490
|
+
const existenceOnlyVerify = planTasks.filter(t =>
|
|
491
|
+
t.verifyCommand &&
|
|
492
|
+
/s\.includes\(/.test(t.verifyCommand) &&
|
|
493
|
+
!/existsSync|readFileSync.*utf8|require\(/.test(t.verifyCommand)
|
|
494
|
+
);
|
|
495
|
+
if (existenceOnlyVerify.length > 2) {
|
|
496
|
+
hints.push(
|
|
497
|
+
`HINT(verify-depth): ${existenceOnlyVerify.length} tarefa(s) verificam apenas presença de string no código ` +
|
|
498
|
+
`(s.includes). Considere adicionar verificações de comportamento: executar o código, não só inspecioná-lo.`
|
|
499
|
+
);
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// ── Lint 3: Tarefas sem verify command (T-level) ──────────────────────────
|
|
503
|
+
const noVerify = planTasks.filter(t => !t.verifyCommand && !t.done);
|
|
504
|
+
if (noVerify.length > 0) {
|
|
505
|
+
hints.push(
|
|
506
|
+
`HINT(no-verify): ${noVerify.length} tarefa(s) sem Comando de verificação: ${noVerify.map(t => t.id).join(', ')}. ` +
|
|
507
|
+
`Tarefas sem verify não serão testadas inline pelo scheduler.`
|
|
508
|
+
);
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
return hints;
|
|
512
|
+
}
|
|
513
|
+
|
|
434
514
|
function compileExecutionGraphFromArtifacts(projectRoot, activeSession, options = {}) {
|
|
435
515
|
const runtime = loadRuntimeModule();
|
|
436
516
|
const parsers = loadSdkParsers();
|
|
@@ -443,12 +523,17 @@ function compileExecutionGraphFromArtifacts(projectRoot, activeSession, options
|
|
|
443
523
|
const artifactPaths = resolveRuntimeArtifactPaths(projectRoot, activeSession);
|
|
444
524
|
const specText = readTextIfExists(artifactPaths.spec);
|
|
445
525
|
const planText = readTextIfExists(artifactPaths.plan);
|
|
446
|
-
if (!specText) throw new Error(`SPEC.md ausente em ${artifactPaths.spec}`);
|
|
447
|
-
if (!planText) throw new Error(`PLAN.md ausente em ${artifactPaths.plan}`);
|
|
526
|
+
if (!specText) throw new Error(`SPEC.md ausente em ${artifactPaths.spec}\n Crie .oxe/SPEC.md com /oxe-spec no seu agente, ou use o template em oxe/templates/SPEC.template.md`);
|
|
527
|
+
if (!planText) throw new Error(`PLAN.md ausente em ${artifactPaths.plan}\n Crie .oxe/PLAN.md com /oxe-plan no seu agente (requer SPEC.md), ou use o template em oxe/templates/PLAN.template.md`);
|
|
448
528
|
const parsedSpec = parsers.parseSpec(specText);
|
|
449
529
|
const parsedPlan = parsers.parsePlan(planText);
|
|
450
530
|
const graph = runtime.compile(parsedPlan, parsedSpec, options.compilerOptions || {});
|
|
451
531
|
const validationErrors = typeof runtime.validateGraph === 'function' ? runtime.validateGraph(graph) : [];
|
|
532
|
+
|
|
533
|
+
// Static-analysis lints: detect common patterns that cause runtime failures
|
|
534
|
+
const lintHints = lintPlanForCommonPitfalls(parsedPlan, parsedSpec, projectRoot, specText);
|
|
535
|
+
if (lintHints.length) validationErrors.push(...lintHints);
|
|
536
|
+
|
|
452
537
|
const compiledGraph = runtime.toSerializable(graph);
|
|
453
538
|
const current = options.runState || readRunState(projectRoot, activeSession) || {};
|
|
454
539
|
const runId = current.run_id || makeRunId();
|
|
@@ -637,11 +722,108 @@ async function resolveRuntimeGate(projectRoot, activeSession, options = {}) {
|
|
|
637
722
|
};
|
|
638
723
|
}
|
|
639
724
|
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
const
|
|
643
|
-
|
|
644
|
-
|
|
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.');
|
|
729
|
+
const parsers = loadSdkParsers();
|
|
730
|
+
if (!parsers) throw new Error('SDK parsers não disponíveis.');
|
|
731
|
+
|
|
732
|
+
// Auto-wire LlmTaskExecutor if providerConfig is supplied
|
|
733
|
+
let executor = options.executor || null;
|
|
734
|
+
if (!executor && options.providerConfig) {
|
|
735
|
+
if (typeof runtime.LlmTaskExecutor !== 'function') throw new Error('Runtime não exporta LlmTaskExecutor.');
|
|
736
|
+
executor = new runtime.LlmTaskExecutor(options.providerConfig, null, options.onProgress || null);
|
|
737
|
+
}
|
|
738
|
+
// Auto-wire InplaceWorkspaceManager as default
|
|
739
|
+
let workspaceManager = options.workspaceManager || null;
|
|
740
|
+
if (!workspaceManager) {
|
|
741
|
+
if (typeof runtime.InplaceWorkspaceManager === 'function') {
|
|
742
|
+
workspaceManager = new runtime.InplaceWorkspaceManager(projectRoot);
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
// Resolve compiled graph from run state or compile on demand
|
|
747
|
+
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;
|
|
757
|
+
|
|
758
|
+
// Detect plan-agents.json (session path takes priority over root)
|
|
759
|
+
const rootAgentPlan = path.join(projectRoot, '.oxe', 'plan-agents.json');
|
|
760
|
+
const sessAgentPlan = activeSession
|
|
761
|
+
? path.join(projectRoot, '.oxe', activeSession, 'plan', 'plan-agents.json')
|
|
762
|
+
: null;
|
|
763
|
+
const agentPlanPath = (sessAgentPlan && fs.existsSync(sessAgentPlan))
|
|
764
|
+
? sessAgentPlan
|
|
765
|
+
: (fs.existsSync(rootAgentPlan) ? rootAgentPlan : null);
|
|
766
|
+
|
|
767
|
+
// Build ctx with GateManager (Gap 1)
|
|
768
|
+
const ctx = createExecutionContext(projectRoot, activeSession, {
|
|
769
|
+
runId: current.run_id,
|
|
770
|
+
executor,
|
|
771
|
+
workspaceManager,
|
|
772
|
+
pluginRegistry: options.pluginRegistry || buildRuntimePluginRegistry(projectRoot),
|
|
773
|
+
schedulerOptions: options.schedulerOptions || {},
|
|
774
|
+
onEvent: (event) => {
|
|
775
|
+
appendEvent(projectRoot, activeSession, event);
|
|
776
|
+
options.onProgress?.(event);
|
|
777
|
+
},
|
|
778
|
+
});
|
|
779
|
+
|
|
780
|
+
// Gap 5: multi-agent path if plan-agents.json exists
|
|
781
|
+
if (agentPlanPath) {
|
|
782
|
+
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
|
+
}));
|
|
800
|
+
const coordinator = new runtime.MultiAgentCoordinator();
|
|
801
|
+
const result = await coordinator.run(graph, {
|
|
802
|
+
mode: agentPlan.mode || 'parallel',
|
|
803
|
+
agents,
|
|
804
|
+
projectRoot,
|
|
805
|
+
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
|
+
}
|
|
812
|
+
|
|
813
|
+
// Single-agent fallback
|
|
814
|
+
if (typeof runtime.Scheduler !== 'function') {
|
|
815
|
+
throw new Error('Runtime não exporta Scheduler. Verifique a versão do runtime.');
|
|
816
|
+
}
|
|
817
|
+
const scheduler = new runtime.Scheduler();
|
|
818
|
+
const result = await scheduler.run(graph, ctx);
|
|
819
|
+
return { mode: 'single', agentPlan: null, result, run: current };
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
function readRuntimeMultiAgentStatus(projectRoot, activeSession, options = {}) {
|
|
823
|
+
const runtime = loadRuntimeModule();
|
|
824
|
+
const current = readRunState(projectRoot, activeSession);
|
|
825
|
+
const runId = options.runId || (current && current.run_id) || null;
|
|
826
|
+
if (!runId) {
|
|
645
827
|
return {
|
|
646
828
|
path: null,
|
|
647
829
|
enabled: false,
|
|
@@ -649,40 +831,40 @@ function readRuntimeMultiAgentStatus(projectRoot, activeSession, options = {}) {
|
|
|
649
831
|
mode: null,
|
|
650
832
|
workspaceIsolationEnforced: false,
|
|
651
833
|
agents: [],
|
|
652
|
-
ownership: [],
|
|
653
|
-
orphanReassignments: [],
|
|
654
|
-
handoffs: [],
|
|
655
|
-
arbitrationResults: [],
|
|
656
|
-
summary: null,
|
|
657
|
-
};
|
|
658
|
-
}
|
|
659
|
-
const runDir = path.join(projectRoot, '.oxe', 'runs', runId);
|
|
660
|
-
const statePath = path.join(runDir, 'multi-agent-state.json');
|
|
661
|
-
const summaryPath = path.join(runDir, 'multi-agent-summary.json');
|
|
662
|
-
const handoffsPath = path.join(runDir, 'handoffs.json');
|
|
663
|
-
const arbitrationPath = path.join(runDir, 'arbitration-results.json');
|
|
664
|
-
const state = runtime && typeof runtime.loadMultiAgentState === 'function'
|
|
665
|
-
? runtime.loadMultiAgentState(projectRoot, runId)
|
|
666
|
-
: readJsonIfExists(statePath);
|
|
667
|
-
const summary = runtime && typeof runtime.loadMultiAgentSummary === 'function'
|
|
668
|
-
? runtime.loadMultiAgentSummary(projectRoot, runId)
|
|
669
|
-
: readJsonIfExists(summaryPath);
|
|
670
|
-
const handoffs = readJsonIfExists(handoffsPath);
|
|
671
|
-
const arbitrationResults = readJsonIfExists(arbitrationPath);
|
|
672
|
-
return {
|
|
673
|
-
path: statePath,
|
|
674
|
-
enabled: Boolean(state),
|
|
834
|
+
ownership: [],
|
|
835
|
+
orphanReassignments: [],
|
|
836
|
+
handoffs: [],
|
|
837
|
+
arbitrationResults: [],
|
|
838
|
+
summary: null,
|
|
839
|
+
};
|
|
840
|
+
}
|
|
841
|
+
const runDir = path.join(projectRoot, '.oxe', 'runs', runId);
|
|
842
|
+
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');
|
|
846
|
+
const state = runtime && typeof runtime.loadMultiAgentState === 'function'
|
|
847
|
+
? runtime.loadMultiAgentState(projectRoot, runId)
|
|
848
|
+
: 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 {
|
|
855
|
+
path: statePath,
|
|
856
|
+
enabled: Boolean(state),
|
|
675
857
|
runId,
|
|
676
858
|
mode: state && state.mode ? state.mode : null,
|
|
677
859
|
workspaceIsolationEnforced: Boolean(state && state.workspace_isolation_enforced),
|
|
678
860
|
agents: state && Array.isArray(state.agent_results) ? state.agent_results : [],
|
|
679
861
|
ownership: state && Array.isArray(state.ownership) ? state.ownership : [],
|
|
680
|
-
orphanReassignments: state && Array.isArray(state.orphan_reassignments) ? state.orphan_reassignments : [],
|
|
681
|
-
handoffs: Array.isArray(handoffs) ? handoffs : [],
|
|
682
|
-
arbitrationResults: Array.isArray(arbitrationResults) ? arbitrationResults : [],
|
|
683
|
-
summary: summary || null,
|
|
684
|
-
};
|
|
685
|
-
}
|
|
862
|
+
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
|
+
}
|
|
686
868
|
|
|
687
869
|
function loadRuntimeVerificationArtifacts(projectRoot, runState) {
|
|
688
870
|
const runtime = loadRuntimeModule();
|
|
@@ -801,6 +983,11 @@ function buildRecoveryConsistency(projectRoot, activeSession, runState, journal,
|
|
|
801
983
|
const runDir = runState && runState.run_id ? path.join(projectRoot, '.oxe', 'runs', runState.run_id) : null;
|
|
802
984
|
const allEvents = readEvents(projectRoot, activeSession);
|
|
803
985
|
const runEvents = runState && runState.run_id ? allEvents.filter((event) => event.run_id === runState.run_id) : [];
|
|
986
|
+
// Detect if execution has ever started (at least one attempt recorded)
|
|
987
|
+
const attemptCount = runState && runState.canonical_state && runState.canonical_state.summary
|
|
988
|
+
? (runState.canonical_state.summary.attempt_count || 0)
|
|
989
|
+
: 0;
|
|
990
|
+
const executionStarted = attemptCount > 0;
|
|
804
991
|
const issues = [];
|
|
805
992
|
if (!activeRunRef || activeRunRef.run_id !== (runState && runState.run_id)) {
|
|
806
993
|
issues.push('ACTIVE-RUN.json não referencia o mesmo run persistido em .oxe/runs/.');
|
|
@@ -808,10 +995,12 @@ function buildRecoveryConsistency(projectRoot, activeSession, runState, journal,
|
|
|
808
995
|
if (!runFile || !fs.existsSync(runFile)) {
|
|
809
996
|
issues.push('Arquivo canónico da run ausente em .oxe/runs/<run>.json.');
|
|
810
997
|
}
|
|
811
|
-
|
|
998
|
+
// Journal is only created after execution starts — skip this check pre-execution
|
|
999
|
+
if (!journal && executionStarted) {
|
|
812
1000
|
issues.push('Journal ausente para recover/replay.');
|
|
813
1001
|
}
|
|
814
|
-
|
|
1002
|
+
// Events for this run only exist after execution — skip pre-execution
|
|
1003
|
+
if (runEvents.length === 0 && executionStarted) {
|
|
815
1004
|
issues.push('Nenhum evento NDJSON encontrado para a run ativa.');
|
|
816
1005
|
}
|
|
817
1006
|
if (!runState || !runState.canonical_state) {
|
|
@@ -1041,7 +1230,7 @@ function projectRuntimeArtifacts(projectRoot, activeSession, options = {}) {
|
|
|
1041
1230
|
const paths = resolveRuntimeArtifactPaths(projectRoot, activeSession);
|
|
1042
1231
|
const op = operationalPaths(projectRoot, activeSession);
|
|
1043
1232
|
const projectionRefs = {
|
|
1044
|
-
plan_ref: path.relative(projectRoot, paths.plan).replace(/\\/g, '/'),
|
|
1233
|
+
plan_ref: path.relative(projectRoot, paths.plan.replace(/PLAN\.md$/, 'PLAN-STATUS.md')).replace(/\\/g, '/'),
|
|
1045
1234
|
verify_ref: path.relative(projectRoot, paths.verify).replace(/\\/g, '/'),
|
|
1046
1235
|
state_ref: path.relative(projectRoot, paths.state).replace(/\\/g, '/'),
|
|
1047
1236
|
run_summary_ref: path.relative(projectRoot, path.join(op.executionRoot, 'RUN-SUMMARY.md')).replace(/\\/g, '/'),
|
|
@@ -1051,10 +1240,12 @@ function projectRuntimeArtifacts(projectRoot, activeSession, options = {}) {
|
|
|
1051
1240
|
generated_at: new Date().toISOString(),
|
|
1052
1241
|
};
|
|
1053
1242
|
if (options.write !== false) {
|
|
1054
|
-
|
|
1243
|
+
// Write plan projection to PLAN-STATUS.md — never overwrite the source PLAN.md
|
|
1244
|
+
const planStatusPath = paths.plan.replace(/PLAN\.md$/, 'PLAN-STATUS.md');
|
|
1245
|
+
ensureDirForFile(planStatusPath);
|
|
1055
1246
|
ensureDirForFile(paths.verify);
|
|
1056
1247
|
ensureDirForFile(paths.state);
|
|
1057
|
-
fs.writeFileSync(
|
|
1248
|
+
fs.writeFileSync(planStatusPath, projections.plan + '\n', 'utf8');
|
|
1058
1249
|
fs.writeFileSync(paths.verify, projections.verify + '\n', 'utf8');
|
|
1059
1250
|
fs.writeFileSync(paths.state, projections.state + '\n', 'utf8');
|
|
1060
1251
|
fs.writeFileSync(path.join(op.executionRoot, 'RUN-SUMMARY.md'), projections.runSummary + '\n', 'utf8');
|
|
@@ -2138,6 +2329,8 @@ module.exports = {
|
|
|
2138
2329
|
buildRecoveryConsistency,
|
|
2139
2330
|
readRuntimeGates,
|
|
2140
2331
|
resolveRuntimeGate,
|
|
2332
|
+
createExecutionContext,
|
|
2333
|
+
runRuntimeExecute,
|
|
2141
2334
|
runRuntimeVerify,
|
|
2142
2335
|
projectRuntimeArtifacts,
|
|
2143
2336
|
runRuntimeCiChecks,
|