oxe-cc 1.5.0 → 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) 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/.github/prompts/oxe-dashboard.prompt.md +2 -2
  5. package/.github/prompts/oxe-execute.prompt.md +2 -2
  6. package/.github/prompts/oxe-plan.prompt.md +2 -2
  7. package/CHANGELOG.md +25 -0
  8. package/README.md +2 -2
  9. package/bin/lib/oxe-context-engine.cjs +9 -4
  10. package/bin/lib/oxe-dashboard.cjs +119 -53
  11. package/bin/lib/oxe-project-health.cjs +368 -111
  12. package/bin/lib/oxe-rationality.cjs +385 -0
  13. package/bin/oxe-cc.js +57 -31
  14. package/commands/oxe/dashboard.md +2 -2
  15. package/commands/oxe/execute.md +2 -2
  16. package/commands/oxe/plan.md +2 -2
  17. package/docs/RUNTIME-SMOKE-MATRIX.md +1 -1
  18. package/lib/sdk/index.cjs +10 -6
  19. package/lib/sdk/index.d.ts +78 -24
  20. package/oxe/templates/CONFIG.md +1 -1
  21. package/oxe/templates/EXECUTION-RUNTIME.template.md +1 -1
  22. package/oxe/templates/FIXTURE-PACK.template.json +34 -0
  23. package/oxe/templates/FIXTURE-PACK.template.md +21 -0
  24. package/oxe/templates/IMPLEMENTATION-PACK.template.json +52 -0
  25. package/oxe/templates/IMPLEMENTATION-PACK.template.md +36 -0
  26. package/oxe/templates/PLAN.template.md +46 -37
  27. package/oxe/templates/REFERENCE-ANCHORS.template.md +24 -0
  28. package/oxe/templates/config.template.json +1 -1
  29. package/oxe/workflows/execute.md +36 -20
  30. package/oxe/workflows/next.md +1 -1
  31. package/oxe/workflows/plan.md +80 -22
  32. package/oxe/workflows/references/flow-robustness-contract.md +3 -3
  33. package/oxe/workflows/references/workflow-runtime-contracts.json +127 -95
  34. package/oxe/workflows/verify.md +4 -4
  35. package/package.json +1 -1
  36. package/packages/runtime/package.json +1 -1
  37. package/vscode-extension/oxe-agents-1.5.1.vsix +0 -0
  38. package/vscode-extension/package.json +1 -1
@@ -1,10 +1,11 @@
1
1
  'use strict';
2
2
 
3
- const fs = require('fs');
4
- const os = require('os');
5
- const path = require('path');
6
- const operational = require('./oxe-operational.cjs');
7
- const azure = require('./oxe-azure.cjs');
3
+ const fs = require('fs');
4
+ const os = require('os');
5
+ const path = require('path');
6
+ const operational = require('./oxe-operational.cjs');
7
+ const azure = require('./oxe-azure.cjs');
8
+ const rationality = require('./oxe-rationality.cjs');
8
9
 
9
10
  /** @type {string[]} */
10
11
  const ALLOWED_CONFIG_KEYS = [
@@ -55,15 +56,75 @@ const INSTALL_REPO_LAYOUTS = ['nested', 'classic'];
55
56
  /** @type {string[]} */
56
57
  const INSTALL_OBJECT_KEYS = ['profile', 'repo_layout', 'ide_scope', 'vscode', 'include_commands_dir', 'include_agents_md'];
57
58
 
58
- const EXPECTED_CODEBASE_MAPS = [
59
- 'OVERVIEW.md',
60
- 'STACK.md',
61
- 'STRUCTURE.md',
62
- 'TESTING.md',
63
- 'INTEGRATIONS.md',
64
- 'CONVENTIONS.md',
65
- 'CONCERNS.md',
66
- ];
59
+ const EXPECTED_CODEBASE_MAPS = [
60
+ 'OVERVIEW.md',
61
+ 'STACK.md',
62
+ 'STRUCTURE.md',
63
+ 'TESTING.md',
64
+ 'INTEGRATIONS.md',
65
+ 'CONVENTIONS.md',
66
+ 'CONCERNS.md',
67
+ ];
68
+
69
+ const MIN_EXECUTABLE_PLAN_CONFIDENCE = 90;
70
+
71
+ /**
72
+ * @param {unknown} value
73
+ * @returns {number}
74
+ */
75
+ function normalizePlanConfidenceThreshold(value) {
76
+ const parsed = Number(value);
77
+ if (!Number.isFinite(parsed)) return MIN_EXECUTABLE_PLAN_CONFIDENCE;
78
+ return Math.max(MIN_EXECUTABLE_PLAN_CONFIDENCE, parsed);
79
+ }
80
+
81
+ /**
82
+ * @param {number | null | undefined} confidence
83
+ * @param {number} threshold
84
+ * @returns {boolean}
85
+ */
86
+ function isExecutablePlanConfidence(confidence, threshold) {
87
+ return Number.isFinite(confidence) && Number(confidence) > normalizePlanConfidenceThreshold(threshold);
88
+ }
89
+
90
+ /**
91
+ * O gate racional só deve bloquear antes da primeira mutação real.
92
+ * Depois que a execução entrou em curso ou já foi verificada, os packs
93
+ * continuam úteis para diagnóstico, mas não podem reescrever o próximo passo.
94
+ *
95
+ * @param {string | null} phase
96
+ * @returns {boolean}
97
+ */
98
+ function shouldEnforceExecutionRationalityGate(phase) {
99
+ const low = String(phase || '').trim().toLowerCase();
100
+ return !new Set([
101
+ 'executing',
102
+ 'verifying',
103
+ 'verify_complete',
104
+ 'verify_failed',
105
+ 'retro_complete',
106
+ ]).has(low);
107
+ }
108
+
109
+ /**
110
+ * @param {{
111
+ * hasSection: boolean,
112
+ * bestPlan: string | null,
113
+ * confidence: number | null,
114
+ * warnings: string[],
115
+ * }} info
116
+ * @param {number} threshold
117
+ * @returns {boolean}
118
+ */
119
+ function hasExecutablePlanSelfEvaluation(info, threshold) {
120
+ return Boolean(
121
+ info
122
+ && info.hasSection
123
+ && info.bestPlan === 'sim'
124
+ && !info.warnings.length
125
+ && isExecutablePlanConfidence(info.confidence, threshold)
126
+ );
127
+ }
67
128
 
68
129
  /**
69
130
  * @param {string} targetProject
@@ -143,9 +204,9 @@ function loadOxeConfigMerged(targetProject) {
143
204
  discuss_before_plan: false,
144
205
  after_verify_suggest_pr: true,
145
206
  after_verify_draft_commit: true,
146
- after_verify_suggest_uat: false,
147
- verification_depth: 'standard',
148
- plan_confidence_threshold: 70,
207
+ after_verify_suggest_uat: false,
208
+ verification_depth: 'standard',
209
+ plan_confidence_threshold: 90,
149
210
  default_verify_command: '',
150
211
  scan_max_age_days: 0,
151
212
  compact_max_age_days: 0,
@@ -590,10 +651,15 @@ function oxePaths(target) {
590
651
  events: path.join(oxe, 'OXE-EVENTS.ndjson'),
591
652
  copilotManifest: path.join(oxe, 'install', 'copilot-vscode.json'),
592
653
  runtimeSemanticsManifest: path.join(oxe, 'install', 'runtime-semantics.json'),
593
- spec: path.join(oxe, 'SPEC.md'),
594
- plan: path.join(oxe, 'PLAN.md'),
595
- quick: path.join(oxe, 'QUICK.md'),
596
- verify: path.join(oxe, 'VERIFY.md'),
654
+ spec: path.join(oxe, 'SPEC.md'),
655
+ plan: path.join(oxe, 'PLAN.md'),
656
+ implementationPackMd: path.join(oxe, 'IMPLEMENTATION-PACK.md'),
657
+ implementationPackJson: path.join(oxe, 'IMPLEMENTATION-PACK.json'),
658
+ referenceAnchors: path.join(oxe, 'REFERENCE-ANCHORS.md'),
659
+ fixturePackMd: path.join(oxe, 'FIXTURE-PACK.md'),
660
+ fixturePackJson: path.join(oxe, 'FIXTURE-PACK.json'),
661
+ quick: path.join(oxe, 'QUICK.md'),
662
+ verify: path.join(oxe, 'VERIFY.md'),
597
663
  discuss: path.join(oxe, 'DISCUSS.md'),
598
664
  summary: path.join(oxe, 'SUMMARY.md'),
599
665
  codebase: path.join(oxe, 'codebase'),
@@ -606,11 +672,11 @@ function oxePaths(target) {
606
672
  * @param {string} target
607
673
  * @param {string | null} activeSession
608
674
  */
609
- function scopedOxePaths(target, activeSession) {
610
- const base = oxePaths(target);
611
- if (!activeSession) return { ...base, activeSession: null, scopedRoot: base.oxe };
612
- const sessionRoot = path.join(base.oxe, ...activeSession.split('/'));
613
- return {
675
+ function scopedOxePaths(target, activeSession) {
676
+ const base = oxePaths(target);
677
+ if (!activeSession) return { ...base, activeSession: null, scopedRoot: base.oxe };
678
+ const sessionRoot = path.join(base.oxe, ...activeSession.split('/'));
679
+ return {
614
680
  ...base,
615
681
  activeSession,
616
682
  scopedRoot: sessionRoot,
@@ -625,15 +691,61 @@ function scopedOxePaths(target, activeSession) {
625
691
  events: path.join(sessionRoot, 'execution', 'OXE-EVENTS.ndjson'),
626
692
  investigationsIndex: path.join(sessionRoot, 'research', 'INVESTIGATIONS.md'),
627
693
  investigationsDir: path.join(sessionRoot, 'research', 'investigations'),
628
- spec: path.join(sessionRoot, 'spec', 'SPEC.md'),
629
- discuss: path.join(sessionRoot, 'spec', 'DISCUSS.md'),
630
- plan: path.join(sessionRoot, 'plan', 'PLAN.md'),
631
- quick: path.join(sessionRoot, 'plan', 'QUICK.md'),
632
- verify: path.join(sessionRoot, 'verification', 'VERIFY.md'),
694
+ spec: path.join(sessionRoot, 'spec', 'SPEC.md'),
695
+ discuss: path.join(sessionRoot, 'spec', 'DISCUSS.md'),
696
+ plan: path.join(sessionRoot, 'plan', 'PLAN.md'),
697
+ planAgents: path.join(sessionRoot, 'plan', 'plan-agents.json'),
698
+ implementationPackMd: path.join(sessionRoot, 'plan', 'IMPLEMENTATION-PACK.md'),
699
+ implementationPackJson: path.join(sessionRoot, 'plan', 'IMPLEMENTATION-PACK.json'),
700
+ referenceAnchors: path.join(sessionRoot, 'plan', 'REFERENCE-ANCHORS.md'),
701
+ fixturePackMd: path.join(sessionRoot, 'plan', 'FIXTURE-PACK.md'),
702
+ fixturePackJson: path.join(sessionRoot, 'plan', 'FIXTURE-PACK.json'),
703
+ quick: path.join(sessionRoot, 'plan', 'QUICK.md'),
704
+ verify: path.join(sessionRoot, 'verification', 'VERIFY.md'),
633
705
  summary: path.join(sessionRoot, 'verification', 'SUMMARY.md'),
634
- executionState: path.join(sessionRoot, 'execution', 'STATE.md'),
635
- };
636
- }
706
+ executionState: path.join(sessionRoot, 'execution', 'STATE.md'),
707
+ };
708
+ }
709
+
710
+ /**
711
+ * Para leitura, preferimos o layout session-scoped quando ele existe.
712
+ * Enquanto a migração não é total, mantemos fallback explícito para os
713
+ * artefatos canónicos ainda materializados na raiz `.oxe/`.
714
+ *
715
+ * @param {string} target
716
+ * @param {string | null} activeSession
717
+ */
718
+ function resolvedReadableOxePaths(target, activeSession) {
719
+ const base = oxePaths(target);
720
+ const scoped = scopedOxePaths(target, activeSession);
721
+ if (!activeSession) return scoped;
722
+ /**
723
+ * @param {string} key
724
+ * @returns {string}
725
+ */
726
+ function preferScoped(key) {
727
+ return fs.existsSync(scoped[key]) ? scoped[key] : base[key];
728
+ }
729
+ return {
730
+ ...scoped,
731
+ planReview: preferScoped('planReview'),
732
+ planReviewComments: preferScoped('planReviewComments'),
733
+ runtime: preferScoped('runtime'),
734
+ checkpoints: preferScoped('checkpoints'),
735
+ spec: preferScoped('spec'),
736
+ discuss: preferScoped('discuss'),
737
+ plan: preferScoped('plan'),
738
+ planAgents: preferScoped('planAgents'),
739
+ implementationPackMd: preferScoped('implementationPackMd'),
740
+ implementationPackJson: preferScoped('implementationPackJson'),
741
+ referenceAnchors: preferScoped('referenceAnchors'),
742
+ fixturePackMd: preferScoped('fixturePackMd'),
743
+ fixturePackJson: preferScoped('fixturePackJson'),
744
+ quick: preferScoped('quick'),
745
+ verify: preferScoped('verify'),
746
+ summary: preferScoped('summary'),
747
+ };
748
+ }
637
749
 
638
750
  /**
639
751
  * @param {string} target
@@ -1436,10 +1548,10 @@ function planWaveWarningsFixed(planPath, maxPerWave) {
1436
1548
  * warnings: string[],
1437
1549
  * }}
1438
1550
  */
1439
- function parsePlanSelfEvaluation(planPath) {
1440
- const empty = { hasSection: false, bestPlan: null, confidence: null, warnings: [] };
1441
- if (!fs.existsSync(planPath)) return empty;
1442
- const raw = fs.readFileSync(planPath, 'utf8');
1551
+ function parsePlanSelfEvaluation(planPath) {
1552
+ const empty = { hasSection: false, bestPlan: null, confidence: null, warnings: [] };
1553
+ if (!fs.existsSync(planPath)) return empty;
1554
+ const raw = fs.readFileSync(planPath, 'utf8');
1443
1555
  const m = raw.match(/##\s*Autoavaliação do Plano\s*([\s\S]*?)(?=\n## |\n#[^\#]|$)/i);
1444
1556
  if (!m) {
1445
1557
  return {
@@ -1447,27 +1559,60 @@ function parsePlanSelfEvaluation(planPath) {
1447
1559
  warnings: ['PLAN.md sem a seção obrigatória "## Autoavaliação do Plano"'],
1448
1560
  };
1449
1561
  }
1450
- const body = m[1];
1451
- const best = body.match(/\*\*Melhor plano atual:\*\*\s*(sim|não|nao)/i);
1452
- const confidence = body.match(/\*\*Confiança:\*\*\s*(\d{1,3})\s*%/i);
1453
- /** @type {string[]} */
1454
- const warnings = [];
1455
- const rubricLabels = [
1456
- 'Completude dos requisitos',
1562
+ const body = m[1];
1563
+ const best = body.match(/\*\*Melhor plano atual:\*\*\s*(sim|não|nao)/i);
1564
+ const confidence = body.match(/\*\*Confiança:\*\*\s*(\d{1,3})\s*%/i);
1565
+ const confidenceVector = raw.match(/<confidence_vector\b[\s\S]*?<\/confidence_vector>/i);
1566
+ /** @type {string[]} */
1567
+ const warnings = [];
1568
+ const rubricLabels = [
1569
+ 'Completude dos requisitos',
1457
1570
  'Dependências conhecidas',
1458
1571
  'Risco técnico',
1459
1572
  'Impacto no código existente',
1460
1573
  'Clareza da validação / testes',
1461
1574
  'Lacunas externas / decisões pendentes',
1462
1575
  ];
1463
- if (!best) warnings.push('PLAN.md: autoavaliação sem "Melhor plano atual: sim|não"');
1464
- if (!confidence) warnings.push('PLAN.md: autoavaliação sem "Confiança: NN%"');
1465
- if (!/\*\*Principais incertezas:\*\*/i.test(body)) warnings.push('PLAN.md: autoavaliação sem "Principais incertezas"');
1466
- if (!/\*\*Alternativas descartadas:\*\*/i.test(body)) warnings.push('PLAN.md: autoavaliação sem "Alternativas descartadas"');
1467
- if (!/\*\*Condição para replanejar:\*\*/i.test(body)) warnings.push('PLAN.md: autoavaliação sem "Condição para replanejar"');
1468
- for (const label of rubricLabels) {
1469
- if (!body.includes(label)) warnings.push(`PLAN.md: rubrica sem "${label}"`);
1470
- }
1576
+ if (!best) warnings.push('PLAN.md: autoavaliação sem "Melhor plano atual: sim|não"');
1577
+ if (!confidence) warnings.push('PLAN.md: autoavaliação sem "Confiança: NN%"');
1578
+ if (!/\*\*Principais incertezas:\*\*/i.test(body)) warnings.push('PLAN.md: autoavaliação sem "Principais incertezas"');
1579
+ if (!/\*\*Alternativas descartadas:\*\*/i.test(body)) warnings.push('PLAN.md: autoavaliação sem "Alternativas descartadas"');
1580
+ if (!/\*\*Condição para replanejar:\*\*/i.test(body)) warnings.push('PLAN.md: autoavaliação sem "Condição para replanejar"');
1581
+ if (!confidenceVector) {
1582
+ warnings.push('PLAN.md: autoavaliação sem bloco <confidence_vector>');
1583
+ } else {
1584
+ const vectorBlock = confidenceVector[0];
1585
+ const vectorGlobal = vectorBlock.match(/<global\b[^>]*score="([0-9.]+)"/i);
1586
+ const requiredDims = [
1587
+ 'requirements',
1588
+ 'dependencies',
1589
+ 'technical_risk',
1590
+ 'code_impact',
1591
+ 'validation',
1592
+ 'open_gaps',
1593
+ ];
1594
+ if (!vectorGlobal) warnings.push('PLAN.md: confidence_vector sem nó <global score="...">');
1595
+ for (const dim of requiredDims) {
1596
+ if (!vectorBlock.includes(`name="${dim}"`)) {
1597
+ warnings.push(`PLAN.md: confidence_vector sem dimensão "${dim}"`);
1598
+ }
1599
+ }
1600
+ if (vectorGlobal) {
1601
+ const vectorGlobalScore = Number(vectorGlobal[1]);
1602
+ if (!Number.isFinite(vectorGlobalScore) || vectorGlobalScore < 0 || vectorGlobalScore > 1) {
1603
+ warnings.push('PLAN.md: confidence_vector com <global score> fora do intervalo 0.0–1.0');
1604
+ } else if (confidence) {
1605
+ const confidencePercent = Number(confidence[1]);
1606
+ const vectorPercent = Math.round(vectorGlobalScore * 100);
1607
+ if (Math.abs(vectorPercent - confidencePercent) > 5) {
1608
+ warnings.push(`PLAN.md: confiança declarada (${confidencePercent}%) diverge do confidence_vector (${vectorPercent}%)`);
1609
+ }
1610
+ }
1611
+ }
1612
+ }
1613
+ for (const label of rubricLabels) {
1614
+ if (!body.includes(label)) warnings.push(`PLAN.md: rubrica sem "${label}"`);
1615
+ }
1471
1616
  const parsedConfidence = confidence ? Number(confidence[1]) : null;
1472
1617
  if (parsedConfidence != null && (parsedConfidence < 0 || parsedConfidence > 100)) {
1473
1618
  warnings.push('PLAN.md: confiança fora do intervalo 0–100%');
@@ -1478,23 +1623,70 @@ function parsePlanSelfEvaluation(planPath) {
1478
1623
  confidence: parsedConfidence,
1479
1624
  warnings,
1480
1625
  };
1481
- }
1482
-
1483
- /**
1484
- * @param {string} planPath
1485
- * @param {number} threshold
1486
- * @returns {string[]}
1487
- */
1488
- function planSelfEvaluationWarnings(planPath, threshold) {
1489
- const info = parsePlanSelfEvaluation(planPath);
1490
- const warns = [...info.warnings];
1491
- if (!fs.existsSync(planPath)) return warns;
1492
- if (info.bestPlan === 'não') warns.push('PLAN.md: autoavaliação declara que este não é o melhor plano atual');
1493
- if (info.confidence != null && info.confidence < threshold) {
1494
- warns.push(`PLAN.md: confiança ${info.confidence}% abaixo do limiar executável (${threshold}%)`);
1495
- }
1496
- return warns;
1497
- }
1626
+ }
1627
+
1628
+ /**
1629
+ * @param {{
1630
+ * hasSection: boolean,
1631
+ * bestPlan: string | null,
1632
+ * confidence: number | null,
1633
+ * warnings: string[],
1634
+ * }} info
1635
+ * @param {number} threshold
1636
+ * @returns {string[]}
1637
+ */
1638
+ function planSelfEvaluationWarningsFromInfo(info, threshold) {
1639
+ const warns = [...info.warnings];
1640
+ if (info.bestPlan === 'não') warns.push('PLAN.md: autoavaliação declara que este não é o melhor plano atual');
1641
+ if (info.confidence != null && !isExecutablePlanConfidence(info.confidence, threshold)) {
1642
+ const normalizedThreshold = normalizePlanConfidenceThreshold(threshold);
1643
+ warns.push(`PLAN.md: confiança ${info.confidence}% não supera o limiar executável (>${normalizedThreshold}%)`);
1644
+ }
1645
+ return warns;
1646
+ }
1647
+
1648
+ /**
1649
+ * @param {string} planPath
1650
+ * @param {number} threshold
1651
+ * @returns {string[]}
1652
+ */
1653
+ function planSelfEvaluationWarnings(planPath, threshold) {
1654
+ if (!fs.existsSync(planPath)) return [];
1655
+ return planSelfEvaluationWarningsFromInfo(parsePlanSelfEvaluation(planPath), threshold);
1656
+ }
1657
+
1658
+ /**
1659
+ * @param {{
1660
+ * applicable: boolean,
1661
+ * implementationPackReady: boolean,
1662
+ * referenceAnchorsReady: boolean,
1663
+ * fixturePackReady: boolean,
1664
+ * executionRationalityReady: boolean,
1665
+ * criticalExecutionGaps: string[],
1666
+ * implementationPack: { path?: string | null, tasks?: unknown[] } | null,
1667
+ * referenceAnchors: { path?: string | null, anchors?: unknown[], missingCriticalCount?: number } | null,
1668
+ * fixturePack: { path?: string | null, fixtures?: unknown[] } | null,
1669
+ * }} summary
1670
+ * @returns {string[]}
1671
+ */
1672
+ function executionRationalityWarningsFromSummary(summary) {
1673
+ if (!summary || !summary.applicable) return [];
1674
+ /** @type {string[]} */
1675
+ const warns = [];
1676
+ if (!summary.implementationPackReady) {
1677
+ warns.push(`IMPLEMENTATION-PACK não está pronto em ${summary.implementationPack && summary.implementationPack.path ? summary.implementationPack.path : '.oxe/IMPLEMENTATION-PACK.json'}`);
1678
+ }
1679
+ if (!summary.referenceAnchorsReady) {
1680
+ warns.push(`REFERENCE-ANCHORS não está pronto em ${summary.referenceAnchors && summary.referenceAnchors.path ? summary.referenceAnchors.path : '.oxe/REFERENCE-ANCHORS.md'}`);
1681
+ }
1682
+ if (!summary.fixturePackReady) {
1683
+ warns.push(`FIXTURE-PACK não está pronto em ${summary.fixturePack && summary.fixturePack.path ? summary.fixturePack.path : '.oxe/FIXTURE-PACK.json'}`);
1684
+ }
1685
+ if (Array.isArray(summary.criticalExecutionGaps) && summary.criticalExecutionGaps.length) {
1686
+ warns.push(...summary.criticalExecutionGaps);
1687
+ }
1688
+ return Array.from(new Set(warns));
1689
+ }
1498
1690
 
1499
1691
  /**
1500
1692
  * @param {string} target
@@ -1648,12 +1840,12 @@ function planReviewWarnings(stateText, p) {
1648
1840
  * @param {string} target
1649
1841
  * @param {{ discuss_before_plan?: boolean }} cfg
1650
1842
  */
1651
- function suggestNextStep(target, cfg = {}) {
1843
+ function suggestNextStep(target, cfg = {}) {
1652
1844
  const base = oxePaths(target);
1653
1845
  const stateText = fs.existsSync(base.state) ? fs.readFileSync(base.state, 'utf8') : '';
1654
- const p = scopedOxePaths(target, parseActiveSession(stateText));
1846
+ const p = resolvedReadableOxePaths(target, parseActiveSession(stateText));
1655
1847
  const discussBefore = Boolean(cfg.discuss_before_plan);
1656
- const threshold = Number(cfg.plan_confidence_threshold) || 70;
1848
+ const threshold = normalizePlanConfidenceThreshold(cfg.plan_confidence_threshold);
1657
1849
  const has = (/** @type {string} */ f) => fs.existsSync(f);
1658
1850
  const mapsComplete = EXPECTED_CODEBASE_MAPS.every((f) => has(path.join(p.codebase, f)));
1659
1851
  const azureActive = azure.isAzureContextEnabled(target, cfg);
@@ -1752,17 +1944,49 @@ function suggestNextStep(target, cfg = {}) {
1752
1944
  };
1753
1945
  }
1754
1946
 
1755
- const selfEval = parsePlanSelfEvaluation(p.plan);
1756
- if (selfEval.bestPlan === 'não' || (selfEval.confidence != null && selfEval.confidence < threshold)) {
1757
- return {
1758
- step: 'plan',
1759
- cursorCmd: '/oxe-plan --replan',
1760
- reason: `O plano atual ainda não atingiu confiança executável (limiar ${threshold}%)`,
1761
- artifacts: ['.oxe/PLAN.md', '.oxe/STATE.md'],
1762
- };
1763
- }
1764
-
1765
- const reviewStatus = parsePlanReviewStatus(stateText);
1947
+ const selfEval = parsePlanSelfEvaluation(p.plan);
1948
+ const selfEvalWarnings = planSelfEvaluationWarningsFromInfo(selfEval, threshold);
1949
+ if (!hasExecutablePlanSelfEvaluation(selfEval, threshold)) {
1950
+ return {
1951
+ step: 'plan',
1952
+ cursorCmd: '/oxe-plan --replan',
1953
+ reason: selfEvalWarnings[0]
1954
+ ? `${selfEvalWarnings[0]} — replaneje antes de executar`
1955
+ : `O plano atual ainda não passou no gate executável (> ${threshold}%)`,
1956
+ artifacts: ['.oxe/PLAN.md', '.oxe/STATE.md'],
1957
+ };
1958
+ }
1959
+
1960
+ const executionRationality = rationality.buildExecutionRationality({
1961
+ plan: p.plan,
1962
+ planAgents: p.planAgents,
1963
+ implementationPackJson: p.implementationPackJson,
1964
+ implementationPackMd: p.implementationPackMd,
1965
+ referenceAnchors: p.referenceAnchors,
1966
+ fixturePackJson: p.fixturePackJson,
1967
+ fixturePackMd: p.fixturePackMd,
1968
+ });
1969
+ if (
1970
+ shouldEnforceExecutionRationalityGate(phase)
1971
+ && executionRationality.applicable
1972
+ && !executionRationality.executionRationalityReady
1973
+ ) {
1974
+ const reason = executionRationality.criticalExecutionGaps[0]
1975
+ || 'Artefatos racionais de execução ainda não estão íntegros';
1976
+ return {
1977
+ step: 'plan',
1978
+ cursorCmd: '/oxe-plan --replan',
1979
+ reason: `${reason} — replaneje antes de executar`,
1980
+ artifacts: [
1981
+ '.oxe/PLAN.md',
1982
+ '.oxe/IMPLEMENTATION-PACK.json',
1983
+ '.oxe/REFERENCE-ANCHORS.md',
1984
+ '.oxe/FIXTURE-PACK.json',
1985
+ ],
1986
+ };
1987
+ }
1988
+
1989
+ const reviewStatus = parsePlanReviewStatus(stateText);
1766
1990
  if (phase === 'plan_ready' && (reviewStatus === 'needs_revision' || reviewStatus === 'rejected')) {
1767
1991
  return {
1768
1992
  step: 'plan',
@@ -1893,7 +2117,7 @@ function buildHealthReport(target) {
1893
2117
  }
1894
2118
  }
1895
2119
  const activeSession = parseActiveSession(stateText);
1896
- const p = scopedOxePaths(target, activeSession);
2120
+ const p = resolvedReadableOxePaths(target, activeSession);
1897
2121
  const phase = parseStatePhase(stateText);
1898
2122
  const scanDate = parseLastScanDate(stateText);
1899
2123
  const stale = isStaleScan(scanDate, Number(config.scan_max_age_days) || 0);
@@ -1904,23 +2128,43 @@ function buildHealthReport(target) {
1904
2128
  const phaseWarn = phase ? phaseCoherenceWarnings(phase, p) : [];
1905
2129
  const runtimeWarn = runtimeWarnings(stateText, p);
1906
2130
  const sumWarn = verifyGapsWithoutSummaryWarning(p.verify, p.summary);
1907
- const specReq = Array.isArray(config.spec_required_sections) ? config.spec_required_sections : [];
1908
- const specWarn = specSectionWarnings(p.spec, specReq.map(String));
1909
- const threshold = Number(config.plan_confidence_threshold) || 70;
1910
- const capabilityWarn = capabilityWarnings(p);
1911
- const investigationWarn = investigationWarnings(p);
1912
- const planWarn = [
1913
- ...planWaveWarningsFixed(p.plan, Number(config.plan_max_tasks_per_wave) || 0),
1914
- ...planTaskAceiteWarnings(p.plan),
1915
- ...planSelfEvaluationWarnings(p.plan, threshold),
1916
- ...planAgentsWarnings(target),
1917
- ];
2131
+ const specReq = Array.isArray(config.spec_required_sections) ? config.spec_required_sections : [];
2132
+ const specWarn = specSectionWarnings(p.spec, specReq.map(String));
2133
+ const threshold = normalizePlanConfidenceThreshold(config.plan_confidence_threshold);
2134
+ const capabilityWarn = capabilityWarnings(p);
2135
+ const investigationWarn = investigationWarnings(p);
2136
+ const parsedPlanSelfEvaluation = parsePlanSelfEvaluation(p.plan);
2137
+ const executionRationality = rationality.buildExecutionRationality({
2138
+ plan: p.plan,
2139
+ planAgents: p.planAgents,
2140
+ implementationPackJson: p.implementationPackJson,
2141
+ implementationPackMd: p.implementationPackMd,
2142
+ referenceAnchors: p.referenceAnchors,
2143
+ fixturePackJson: p.fixturePackJson,
2144
+ fixturePackMd: p.fixturePackMd,
2145
+ });
2146
+ const planWarn = [
2147
+ ...planWaveWarningsFixed(p.plan, Number(config.plan_max_tasks_per_wave) || 0),
2148
+ ...planTaskAceiteWarnings(p.plan),
2149
+ ...planSelfEvaluationWarningsFromInfo(parsedPlanSelfEvaluation, threshold),
2150
+ ...executionRationalityWarningsFromSummary(executionRationality),
2151
+ ...planAgentsWarnings(target),
2152
+ ];
1918
2153
  const sessionWarn = sessionWarnings(target, activeSession);
1919
2154
  const installWarn = installationCompletenessWarnings(target);
1920
2155
  const copilot = copilotIntegrationReport(target);
1921
2156
  const copilotWarn = copilot.warnings;
1922
2157
  const reviewWarn = planReviewWarnings(stateText, p);
1923
- const planSelfEvaluation = parsePlanSelfEvaluation(p.plan);
2158
+ const planSelfEvaluation = {
2159
+ ...parsedPlanSelfEvaluation,
2160
+ best_plan_current: parsedPlanSelfEvaluation.bestPlan === 'sim'
2161
+ ? true
2162
+ : parsedPlanSelfEvaluation.bestPlan === 'não'
2163
+ ? false
2164
+ : null,
2165
+ threshold,
2166
+ executable: hasExecutablePlanSelfEvaluation(parsedPlanSelfEvaluation, threshold),
2167
+ };
1924
2168
  const activeRun = operational.readRunState(target, activeSession);
1925
2169
  const eventsSummary = operational.summarizeEvents(operational.readEvents(target, activeSession));
1926
2170
  const memoryLayers = operational.buildMemoryLayers(target, activeSession);
@@ -2135,9 +2379,17 @@ function buildHealthReport(target) {
2135
2379
  copilot,
2136
2380
  summaryGapWarn: sumWarn,
2137
2381
  specWarn,
2138
- planWarn,
2139
- planSelfEvaluation,
2140
- planReviewStatus: parsePlanReviewStatus(stateText),
2382
+ planWarn,
2383
+ planSelfEvaluation,
2384
+ implementationPackReady: executionRationality.implementationPackReady,
2385
+ referenceAnchorsReady: executionRationality.referenceAnchorsReady,
2386
+ fixturePackReady: executionRationality.fixturePackReady,
2387
+ executionRationalityReady: executionRationality.executionRationalityReady,
2388
+ criticalExecutionGaps: executionRationality.criticalExecutionGaps,
2389
+ executionRationality,
2390
+ planConfidenceThreshold: threshold,
2391
+ planConfidenceExecutable: planSelfEvaluation.executable,
2392
+ planReviewStatus: parsePlanReviewStatus(stateText),
2141
2393
  activeRun,
2142
2394
  eventsSummary,
2143
2395
  memoryLayers,
@@ -2206,13 +2458,17 @@ module.exports = {
2206
2458
  isStaleLessons,
2207
2459
  copilotWorkspacePaths,
2208
2460
  copilotLegacyPaths,
2209
- copilotIntegrationReport,
2210
- planAgentsWarnings,
2461
+ copilotIntegrationReport,
2462
+ normalizePlanConfidenceThreshold,
2463
+ isExecutablePlanConfidence,
2464
+ hasExecutablePlanSelfEvaluation,
2465
+ planAgentsWarnings,
2211
2466
  installationCompletenessWarnings,
2212
- parsePlanSelfEvaluation,
2213
- planSelfEvaluationWarnings,
2214
- runtimeWarnings,
2215
- planReviewWarnings,
2467
+ parsePlanSelfEvaluation,
2468
+ planSelfEvaluationWarnings,
2469
+ executionRationalityWarningsFromSummary,
2470
+ runtimeWarnings,
2471
+ planReviewWarnings,
2216
2472
  capabilityWarnings,
2217
2473
  investigationWarnings,
2218
2474
  phaseCoherenceWarnings,
@@ -2220,8 +2476,9 @@ module.exports = {
2220
2476
  specSectionWarnings,
2221
2477
  planWaveWarningsFixed,
2222
2478
  planTaskAceiteWarnings,
2223
- suggestNextStep,
2224
- buildHealthReport,
2225
- oxePaths,
2226
- scopedOxePaths,
2227
- };
2479
+ suggestNextStep,
2480
+ buildHealthReport,
2481
+ buildExecutionRationality: rationality.buildExecutionRationality,
2482
+ oxePaths,
2483
+ scopedOxePaths,
2484
+ };