oxe-cc 1.5.0 → 1.6.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 (50) 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/AGENTS.md +1 -1
  8. package/CHANGELOG.md +52 -0
  9. package/README.md +17 -15
  10. package/bin/lib/oxe-context-engine.cjs +9 -4
  11. package/bin/lib/oxe-dashboard.cjs +140 -58
  12. package/bin/lib/oxe-project-health.cjs +486 -151
  13. package/bin/lib/oxe-rationality.cjs +385 -0
  14. package/bin/lib/oxe-release.cjs +76 -4
  15. package/bin/oxe-cc.js +113 -58
  16. package/commands/oxe/dashboard.md +2 -2
  17. package/commands/oxe/execute.md +2 -2
  18. package/commands/oxe/plan.md +2 -2
  19. package/docs/RELEASE-READINESS.md +8 -0
  20. package/docs/RUNTIME-SMOKE-MATRIX.md +9 -2
  21. package/lib/sdk/index.cjs +20 -11
  22. package/lib/sdk/index.d.ts +99 -34
  23. package/oxe/templates/CONFIG.md +3 -3
  24. package/oxe/templates/EXECUTION-RUNTIME.template.md +1 -1
  25. package/oxe/templates/FIXTURE-PACK.template.json +34 -0
  26. package/oxe/templates/FIXTURE-PACK.template.md +21 -0
  27. package/oxe/templates/IMPLEMENTATION-PACK.template.json +52 -0
  28. package/oxe/templates/IMPLEMENTATION-PACK.template.md +36 -0
  29. package/oxe/templates/INVESTIGATION.template.md +38 -38
  30. package/oxe/templates/PLAN.template.md +23 -14
  31. package/oxe/templates/REFERENCE-ANCHORS.template.md +24 -0
  32. package/oxe/templates/RESEARCH.template.md +11 -11
  33. package/oxe/templates/SPEC.template.md +6 -6
  34. package/oxe/templates/SUMMARY.template.md +20 -20
  35. package/oxe/templates/config.template.json +1 -1
  36. package/oxe/workflows/execute.md +18 -2
  37. package/oxe/workflows/milestone.md +12 -12
  38. package/oxe/workflows/next.md +1 -1
  39. package/oxe/workflows/plan.md +115 -57
  40. package/oxe/workflows/references/adaptive-discovery.md +27 -27
  41. package/oxe/workflows/references/flow-robustness-contract.md +80 -80
  42. package/oxe/workflows/references/session-path-resolution.md +71 -71
  43. package/oxe/workflows/references/workflow-runtime-contracts.json +36 -4
  44. package/oxe/workflows/verify.md +4 -4
  45. package/oxe/workflows/workstream.md +16 -16
  46. package/package.json +1 -1
  47. package/packages/runtime/package.json +1 -1
  48. package/vscode-extension/oxe-agents-1.5.1.vsix +0 -0
  49. package/vscode-extension/oxe-agents-1.6.0.vsix +0 -0
  50. 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,105 @@ 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
+ }
749
+
750
+ /**
751
+ * @param {string} target
752
+ * @returns {{ workspaceMode: 'product_package' | 'oxe_project', packageName: string | null, canonicalTreePresent: boolean, commandsTreePresent: boolean }}
753
+ */
754
+ function detectWorkspaceMode(target) {
755
+ const packageJsonPath = path.join(target, 'package.json');
756
+ let packageName = null;
757
+ try {
758
+ if (fs.existsSync(packageJsonPath)) {
759
+ const parsed = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
760
+ packageName = parsed && typeof parsed.name === 'string' ? parsed.name : null;
761
+ }
762
+ } catch {
763
+ packageName = null;
764
+ }
765
+ const canonicalTreePresent = fs.existsSync(path.join(target, 'oxe', 'workflows'));
766
+ const commandsTreePresent = fs.existsSync(path.join(target, 'commands', 'oxe'));
767
+ const packageRepo =
768
+ packageName === 'oxe-cc'
769
+ && fs.existsSync(path.join(target, 'bin', 'oxe-cc.js'))
770
+ && fs.existsSync(path.join(target, 'packages', 'runtime', 'package.json'))
771
+ && canonicalTreePresent;
772
+ return {
773
+ workspaceMode: packageRepo ? 'product_package' : 'oxe_project',
774
+ packageName,
775
+ canonicalTreePresent,
776
+ commandsTreePresent,
777
+ };
778
+ }
779
+
780
+ /**
781
+ * @param {'product_package' | 'oxe_project'} workspaceMode
782
+ * @param {string | null} phase
783
+ * @param {string | null} activeSession
784
+ * @param {Record<string, unknown> | null} activeRun
785
+ * @returns {boolean}
786
+ */
787
+ function shouldSuppressExecutionWorkspaceGates(workspaceMode, phase, activeSession, activeRun) {
788
+ if (workspaceMode !== 'product_package') return false;
789
+ if (activeSession) return false;
790
+ if (activeRun && typeof activeRun === 'object') return false;
791
+ return !phase || phase === 'initial';
792
+ }
637
793
 
638
794
  /**
639
795
  * @param {string} target
@@ -1436,10 +1592,10 @@ function planWaveWarningsFixed(planPath, maxPerWave) {
1436
1592
  * warnings: string[],
1437
1593
  * }}
1438
1594
  */
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');
1595
+ function parsePlanSelfEvaluation(planPath) {
1596
+ const empty = { hasSection: false, bestPlan: null, confidence: null, warnings: [] };
1597
+ if (!fs.existsSync(planPath)) return empty;
1598
+ const raw = fs.readFileSync(planPath, 'utf8');
1443
1599
  const m = raw.match(/##\s*Autoavaliação do Plano\s*([\s\S]*?)(?=\n## |\n#[^\#]|$)/i);
1444
1600
  if (!m) {
1445
1601
  return {
@@ -1447,27 +1603,60 @@ function parsePlanSelfEvaluation(planPath) {
1447
1603
  warnings: ['PLAN.md sem a seção obrigatória "## Autoavaliação do Plano"'],
1448
1604
  };
1449
1605
  }
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',
1606
+ const body = m[1];
1607
+ const best = body.match(/\*\*Melhor plano atual:\*\*\s*(sim|não|nao)/i);
1608
+ const confidence = body.match(/\*\*Confiança:\*\*\s*(\d{1,3})\s*%/i);
1609
+ const confidenceVector = raw.match(/<confidence_vector\b[\s\S]*?<\/confidence_vector>/i);
1610
+ /** @type {string[]} */
1611
+ const warnings = [];
1612
+ const rubricLabels = [
1613
+ 'Completude dos requisitos',
1457
1614
  'Dependências conhecidas',
1458
1615
  'Risco técnico',
1459
1616
  'Impacto no código existente',
1460
1617
  'Clareza da validação / testes',
1461
1618
  'Lacunas externas / decisões pendentes',
1462
1619
  ];
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
- }
1620
+ if (!best) warnings.push('PLAN.md: autoavaliação sem "Melhor plano atual: sim|não"');
1621
+ if (!confidence) warnings.push('PLAN.md: autoavaliação sem "Confiança: NN%"');
1622
+ if (!/\*\*Principais incertezas:\*\*/i.test(body)) warnings.push('PLAN.md: autoavaliação sem "Principais incertezas"');
1623
+ if (!/\*\*Alternativas descartadas:\*\*/i.test(body)) warnings.push('PLAN.md: autoavaliação sem "Alternativas descartadas"');
1624
+ if (!/\*\*Condição para replanejar:\*\*/i.test(body)) warnings.push('PLAN.md: autoavaliação sem "Condição para replanejar"');
1625
+ if (!confidenceVector) {
1626
+ warnings.push('PLAN.md: autoavaliação sem bloco <confidence_vector>');
1627
+ } else {
1628
+ const vectorBlock = confidenceVector[0];
1629
+ const vectorGlobal = vectorBlock.match(/<global\b[^>]*score="([0-9.]+)"/i);
1630
+ const requiredDims = [
1631
+ 'requirements',
1632
+ 'dependencies',
1633
+ 'technical_risk',
1634
+ 'code_impact',
1635
+ 'validation',
1636
+ 'open_gaps',
1637
+ ];
1638
+ if (!vectorGlobal) warnings.push('PLAN.md: confidence_vector sem nó <global score="...">');
1639
+ for (const dim of requiredDims) {
1640
+ if (!vectorBlock.includes(`name="${dim}"`)) {
1641
+ warnings.push(`PLAN.md: confidence_vector sem dimensão "${dim}"`);
1642
+ }
1643
+ }
1644
+ if (vectorGlobal) {
1645
+ const vectorGlobalScore = Number(vectorGlobal[1]);
1646
+ if (!Number.isFinite(vectorGlobalScore) || vectorGlobalScore < 0 || vectorGlobalScore > 1) {
1647
+ warnings.push('PLAN.md: confidence_vector com <global score> fora do intervalo 0.0–1.0');
1648
+ } else if (confidence) {
1649
+ const confidencePercent = Number(confidence[1]);
1650
+ const vectorPercent = Math.round(vectorGlobalScore * 100);
1651
+ if (Math.abs(vectorPercent - confidencePercent) > 5) {
1652
+ warnings.push(`PLAN.md: confiança declarada (${confidencePercent}%) diverge do confidence_vector (${vectorPercent}%)`);
1653
+ }
1654
+ }
1655
+ }
1656
+ }
1657
+ for (const label of rubricLabels) {
1658
+ if (!body.includes(label)) warnings.push(`PLAN.md: rubrica sem "${label}"`);
1659
+ }
1471
1660
  const parsedConfidence = confidence ? Number(confidence[1]) : null;
1472
1661
  if (parsedConfidence != null && (parsedConfidence < 0 || parsedConfidence > 100)) {
1473
1662
  warnings.push('PLAN.md: confiança fora do intervalo 0–100%');
@@ -1478,23 +1667,70 @@ function parsePlanSelfEvaluation(planPath) {
1478
1667
  confidence: parsedConfidence,
1479
1668
  warnings,
1480
1669
  };
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
- }
1670
+ }
1671
+
1672
+ /**
1673
+ * @param {{
1674
+ * hasSection: boolean,
1675
+ * bestPlan: string | null,
1676
+ * confidence: number | null,
1677
+ * warnings: string[],
1678
+ * }} info
1679
+ * @param {number} threshold
1680
+ * @returns {string[]}
1681
+ */
1682
+ function planSelfEvaluationWarningsFromInfo(info, threshold) {
1683
+ const warns = [...info.warnings];
1684
+ if (info.bestPlan === 'não') warns.push('PLAN.md: autoavaliação declara que este não é o melhor plano atual');
1685
+ if (info.confidence != null && !isExecutablePlanConfidence(info.confidence, threshold)) {
1686
+ const normalizedThreshold = normalizePlanConfidenceThreshold(threshold);
1687
+ warns.push(`PLAN.md: confiança ${info.confidence}% não supera o limiar executável (>${normalizedThreshold}%)`);
1688
+ }
1689
+ return warns;
1690
+ }
1691
+
1692
+ /**
1693
+ * @param {string} planPath
1694
+ * @param {number} threshold
1695
+ * @returns {string[]}
1696
+ */
1697
+ function planSelfEvaluationWarnings(planPath, threshold) {
1698
+ if (!fs.existsSync(planPath)) return [];
1699
+ return planSelfEvaluationWarningsFromInfo(parsePlanSelfEvaluation(planPath), threshold);
1700
+ }
1701
+
1702
+ /**
1703
+ * @param {{
1704
+ * applicable: boolean,
1705
+ * implementationPackReady: boolean,
1706
+ * referenceAnchorsReady: boolean,
1707
+ * fixturePackReady: boolean,
1708
+ * executionRationalityReady: boolean,
1709
+ * criticalExecutionGaps: string[],
1710
+ * implementationPack: { path?: string | null, tasks?: unknown[] } | null,
1711
+ * referenceAnchors: { path?: string | null, anchors?: unknown[], missingCriticalCount?: number } | null,
1712
+ * fixturePack: { path?: string | null, fixtures?: unknown[] } | null,
1713
+ * }} summary
1714
+ * @returns {string[]}
1715
+ */
1716
+ function executionRationalityWarningsFromSummary(summary) {
1717
+ if (!summary || !summary.applicable) return [];
1718
+ /** @type {string[]} */
1719
+ const warns = [];
1720
+ if (!summary.implementationPackReady) {
1721
+ warns.push(`IMPLEMENTATION-PACK não está pronto em ${summary.implementationPack && summary.implementationPack.path ? summary.implementationPack.path : '.oxe/IMPLEMENTATION-PACK.json'}`);
1722
+ }
1723
+ if (!summary.referenceAnchorsReady) {
1724
+ warns.push(`REFERENCE-ANCHORS não está pronto em ${summary.referenceAnchors && summary.referenceAnchors.path ? summary.referenceAnchors.path : '.oxe/REFERENCE-ANCHORS.md'}`);
1725
+ }
1726
+ if (!summary.fixturePackReady) {
1727
+ warns.push(`FIXTURE-PACK não está pronto em ${summary.fixturePack && summary.fixturePack.path ? summary.fixturePack.path : '.oxe/FIXTURE-PACK.json'}`);
1728
+ }
1729
+ if (Array.isArray(summary.criticalExecutionGaps) && summary.criticalExecutionGaps.length) {
1730
+ warns.push(...summary.criticalExecutionGaps);
1731
+ }
1732
+ return Array.from(new Set(warns));
1733
+ }
1498
1734
 
1499
1735
  /**
1500
1736
  * @param {string} target
@@ -1648,17 +1884,20 @@ function planReviewWarnings(stateText, p) {
1648
1884
  * @param {string} target
1649
1885
  * @param {{ discuss_before_plan?: boolean }} cfg
1650
1886
  */
1651
- function suggestNextStep(target, cfg = {}) {
1652
- const base = oxePaths(target);
1653
- const stateText = fs.existsSync(base.state) ? fs.readFileSync(base.state, 'utf8') : '';
1654
- const p = scopedOxePaths(target, parseActiveSession(stateText));
1655
- const discussBefore = Boolean(cfg.discuss_before_plan);
1656
- const threshold = Number(cfg.plan_confidence_threshold) || 70;
1657
- const has = (/** @type {string} */ f) => fs.existsSync(f);
1658
- const mapsComplete = EXPECTED_CODEBASE_MAPS.every((f) => has(path.join(p.codebase, f)));
1659
- const azureActive = azure.isAzureContextEnabled(target, cfg);
1660
-
1661
- if (!has(p.oxe) || !has(p.state)) {
1887
+ function suggestNextStep(target, cfg = {}) {
1888
+ const base = oxePaths(target);
1889
+ const stateText = fs.existsSync(base.state) ? fs.readFileSync(base.state, 'utf8') : '';
1890
+ const activeSession = parseActiveSession(stateText);
1891
+ const p = resolvedReadableOxePaths(target, activeSession);
1892
+ const discussBefore = Boolean(cfg.discuss_before_plan);
1893
+ const threshold = normalizePlanConfidenceThreshold(cfg.plan_confidence_threshold);
1894
+ const has = (/** @type {string} */ f) => fs.existsSync(f);
1895
+ const mapsComplete = EXPECTED_CODEBASE_MAPS.every((f) => has(path.join(p.codebase, f)));
1896
+ const azureActive = azure.isAzureContextEnabled(target, cfg);
1897
+ const activeRun = operational.readRunState(target, activeSession);
1898
+ const workspaceInfo = detectWorkspaceMode(target);
1899
+
1900
+ if (!has(p.oxe) || !has(p.state)) {
1662
1901
  return {
1663
1902
  step: 'scan',
1664
1903
  cursorCmd: '/oxe-scan',
@@ -1666,10 +1905,27 @@ function suggestNextStep(target, cfg = {}) {
1666
1905
  artifacts: ['.oxe/'],
1667
1906
  };
1668
1907
  }
1669
-
1670
- const phase = parseStatePhase(stateText);
1671
-
1672
- if (!mapsComplete && !has(p.quick)) {
1908
+
1909
+ const phase = parseStatePhase(stateText);
1910
+ if (shouldSuppressExecutionWorkspaceGates(workspaceInfo.workspaceMode, phase, activeSession, activeRun)) {
1911
+ const release = require('./oxe-release.cjs');
1912
+ const readiness = release.inspectReleaseReadiness(target, { packageRoot: target });
1913
+ return {
1914
+ step: 'doctor',
1915
+ cursorCmd: 'oxe-cc doctor --release --write-manifest',
1916
+ reason: readiness.ok
1917
+ ? 'Repositório do pacote OXE detectado — o próximo passo operacional é validar/publicar a release, não replanejar um workspace de entrega.'
1918
+ : `Repositório do pacote OXE detectado — trate primeiro os blockers de release (${readiness.blockers[0] || 'release readiness incompleta'}).`,
1919
+ artifacts: [
1920
+ '.oxe/release/release-manifest.json',
1921
+ '.oxe/release/runtime-smoke-report.json',
1922
+ '.oxe/release/recovery-fixture-report.json',
1923
+ '.oxe/release/multi-agent-soak-report.json',
1924
+ ],
1925
+ };
1926
+ }
1927
+
1928
+ if (!mapsComplete && !has(p.quick)) {
1673
1929
  return {
1674
1930
  step: 'scan',
1675
1931
  cursorCmd: '/oxe-scan',
@@ -1752,17 +2008,49 @@ function suggestNextStep(target, cfg = {}) {
1752
2008
  };
1753
2009
  }
1754
2010
 
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);
2011
+ const selfEval = parsePlanSelfEvaluation(p.plan);
2012
+ const selfEvalWarnings = planSelfEvaluationWarningsFromInfo(selfEval, threshold);
2013
+ if (!hasExecutablePlanSelfEvaluation(selfEval, threshold)) {
2014
+ return {
2015
+ step: 'plan',
2016
+ cursorCmd: '/oxe-plan --replan',
2017
+ reason: selfEvalWarnings[0]
2018
+ ? `${selfEvalWarnings[0]} — replaneje antes de executar`
2019
+ : `O plano atual ainda não passou no gate executável (> ${threshold}%)`,
2020
+ artifacts: ['.oxe/PLAN.md', '.oxe/STATE.md'],
2021
+ };
2022
+ }
2023
+
2024
+ const executionRationality = rationality.buildExecutionRationality({
2025
+ plan: p.plan,
2026
+ planAgents: p.planAgents,
2027
+ implementationPackJson: p.implementationPackJson,
2028
+ implementationPackMd: p.implementationPackMd,
2029
+ referenceAnchors: p.referenceAnchors,
2030
+ fixturePackJson: p.fixturePackJson,
2031
+ fixturePackMd: p.fixturePackMd,
2032
+ });
2033
+ if (
2034
+ shouldEnforceExecutionRationalityGate(phase)
2035
+ && executionRationality.applicable
2036
+ && !executionRationality.executionRationalityReady
2037
+ ) {
2038
+ const reason = executionRationality.criticalExecutionGaps[0]
2039
+ || 'Artefatos racionais de execução ainda não estão íntegros';
2040
+ return {
2041
+ step: 'plan',
2042
+ cursorCmd: '/oxe-plan --replan',
2043
+ reason: `${reason} — replaneje antes de executar`,
2044
+ artifacts: [
2045
+ '.oxe/PLAN.md',
2046
+ '.oxe/IMPLEMENTATION-PACK.json',
2047
+ '.oxe/REFERENCE-ANCHORS.md',
2048
+ '.oxe/FIXTURE-PACK.json',
2049
+ ],
2050
+ };
2051
+ }
2052
+
2053
+ const reviewStatus = parsePlanReviewStatus(stateText);
1766
2054
  if (phase === 'plan_ready' && (reviewStatus === 'needs_revision' || reviewStatus === 'rejected')) {
1767
2055
  return {
1768
2056
  step: 'plan',
@@ -1779,9 +2067,7 @@ function suggestNextStep(target, cfg = {}) {
1779
2067
  artifacts: ['.oxe/PLAN.md', '.oxe/PLAN-REVIEW.md', '.oxe/STATE.md'],
1780
2068
  };
1781
2069
  }
1782
-
1783
- const activeRun = operational.readRunState(target, parseActiveSession(stateText));
1784
- if (activeRun && activeRun.status === 'waiting_approval') {
2070
+ if (activeRun && activeRun.status === 'waiting_approval') {
1785
2071
  return {
1786
2072
  step: 'dashboard',
1787
2073
  cursorCmd: '/oxe-dashboard',
@@ -1878,7 +2164,7 @@ function suggestNextStep(target, cfg = {}) {
1878
2164
  /**
1879
2165
  * @param {string} target
1880
2166
  */
1881
- function buildHealthReport(target) {
2167
+ function buildHealthReport(target) {
1882
2168
  const contextEngine = require('./oxe-context-engine.cjs');
1883
2169
  const runtimeSemantics = require('./oxe-runtime-semantics.cjs');
1884
2170
  const { config, path: cfgPath, parseError } = loadOxeConfigMerged(target);
@@ -1892,10 +2178,11 @@ function buildHealthReport(target) {
1892
2178
  stateText = '';
1893
2179
  }
1894
2180
  }
1895
- const activeSession = parseActiveSession(stateText);
1896
- const p = scopedOxePaths(target, activeSession);
1897
- const phase = parseStatePhase(stateText);
1898
- const scanDate = parseLastScanDate(stateText);
2181
+ const activeSession = parseActiveSession(stateText);
2182
+ const p = resolvedReadableOxePaths(target, activeSession);
2183
+ const phase = parseStatePhase(stateText);
2184
+ const workspaceInfo = detectWorkspaceMode(target);
2185
+ const scanDate = parseLastScanDate(stateText);
1899
2186
  const stale = isStaleScan(scanDate, Number(config.scan_max_age_days) || 0);
1900
2187
  const compactDate = parseLastCompactDate(stateText);
1901
2188
  const staleCompact = isStaleScan(compactDate, Number(config.compact_max_age_days) || 0);
@@ -1904,24 +2191,54 @@ function buildHealthReport(target) {
1904
2191
  const phaseWarn = phase ? phaseCoherenceWarnings(phase, p) : [];
1905
2192
  const runtimeWarn = runtimeWarnings(stateText, p);
1906
2193
  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
- ];
2194
+ const specReq = Array.isArray(config.spec_required_sections) ? config.spec_required_sections : [];
2195
+ const specWarn = specSectionWarnings(p.spec, specReq.map(String));
2196
+ const threshold = normalizePlanConfidenceThreshold(config.plan_confidence_threshold);
2197
+ const capabilityWarn = capabilityWarnings(p);
2198
+ const investigationWarn = investigationWarnings(p);
2199
+ const parsedPlanSelfEvaluation = parsePlanSelfEvaluation(p.plan);
2200
+ const activeRun = operational.readRunState(target, activeSession);
2201
+ const executionRationality = rationality.buildExecutionRationality({
2202
+ plan: p.plan,
2203
+ planAgents: p.planAgents,
2204
+ implementationPackJson: p.implementationPackJson,
2205
+ implementationPackMd: p.implementationPackMd,
2206
+ referenceAnchors: p.referenceAnchors,
2207
+ fixturePackJson: p.fixturePackJson,
2208
+ fixturePackMd: p.fixturePackMd,
2209
+ });
2210
+ const suppressExecutionWorkspaceGates = shouldSuppressExecutionWorkspaceGates(
2211
+ workspaceInfo.workspaceMode,
2212
+ phase,
2213
+ activeSession,
2214
+ activeRun
2215
+ );
2216
+ const executionPlanWarn = [
2217
+ ...planWaveWarningsFixed(p.plan, Number(config.plan_max_tasks_per_wave) || 0),
2218
+ ...planTaskAceiteWarnings(p.plan),
2219
+ ...planSelfEvaluationWarningsFromInfo(parsedPlanSelfEvaluation, threshold),
2220
+ ...executionRationalityWarningsFromSummary(executionRationality),
2221
+ ...planAgentsWarnings(target),
2222
+ ];
2223
+ const planWarn = suppressExecutionWorkspaceGates ? [] : executionPlanWarn;
1918
2224
  const sessionWarn = sessionWarnings(target, activeSession);
1919
- const installWarn = installationCompletenessWarnings(target);
1920
- const copilot = copilotIntegrationReport(target);
1921
- const copilotWarn = copilot.warnings;
1922
- const reviewWarn = planReviewWarnings(stateText, p);
1923
- const planSelfEvaluation = parsePlanSelfEvaluation(p.plan);
1924
- const activeRun = operational.readRunState(target, activeSession);
2225
+ const installWarn = installationCompletenessWarnings(target);
2226
+ const copilot = copilotIntegrationReport(target);
2227
+ const copilotWarn = copilot.warnings;
2228
+ const reviewWarn = suppressExecutionWorkspaceGates ? [] : planReviewWarnings(stateText, p);
2229
+ const planSelfEvaluation = {
2230
+ ...parsedPlanSelfEvaluation,
2231
+ best_plan_current: parsedPlanSelfEvaluation.bestPlan === 'sim'
2232
+ ? true
2233
+ : parsedPlanSelfEvaluation.bestPlan === 'não'
2234
+ ? false
2235
+ : null,
2236
+ threshold,
2237
+ executable: hasExecutablePlanSelfEvaluation(parsedPlanSelfEvaluation, threshold),
2238
+ };
2239
+ const releaseReadiness = workspaceInfo.workspaceMode === 'product_package'
2240
+ ? require('./oxe-release.cjs').inspectReleaseReadiness(target, { packageRoot: target })
2241
+ : null;
1925
2242
  const eventsSummary = operational.summarizeEvents(operational.readEvents(target, activeSession));
1926
2243
  const memoryLayers = operational.buildMemoryLayers(target, activeSession);
1927
2244
  const enterpriseRuntime = summarizeEnterpriseRuntime(target, activeRun, activeSession, config);
@@ -2068,7 +2385,7 @@ function buildHealthReport(target) {
2068
2385
  if (semanticsAudit.mismatches.length) {
2069
2386
  semanticsWarn.push(`${semanticsAudit.mismatches.length} wrapper(s) com drift semântico detectado.`);
2070
2387
  }
2071
- const semanticsDrift = {
2388
+ const semanticsDrift = {
2072
2389
  ok: semanticsWarn.length === 0 && semanticsAudit.ok,
2073
2390
  contractVersion: runtimeSemantics.CONTRACT_VERSION,
2074
2391
  manifestPath: base.runtimeSemanticsManifest,
@@ -2090,15 +2407,16 @@ function buildHealthReport(target) {
2090
2407
  ),
2091
2408
  },
2092
2409
  };
2093
- const hardFailure = Boolean(parseError) || sessionWarn.some((w) => /não existe|sem SESSION\.md/i.test(w));
2094
- const warningCount =
2095
- phaseWarn.length +
2096
- runtimeWarn.length +
2097
- reviewWarn.length +
2098
- enterpriseRuntime.enterpriseWarnings.length +
2099
- specWarn.length +
2100
- planWarn.length +
2101
- capabilityWarn.length +
2410
+ const hardFailure = Boolean(parseError) || sessionWarn.some((w) => /não existe|sem SESSION\.md/i.test(w));
2411
+ const planWarningCount = suppressExecutionWorkspaceGates ? 0 : planWarn.length;
2412
+ const warningCount =
2413
+ phaseWarn.length +
2414
+ runtimeWarn.length +
2415
+ reviewWarn.length +
2416
+ enterpriseRuntime.enterpriseWarnings.length +
2417
+ specWarn.length +
2418
+ planWarningCount +
2419
+ capabilityWarn.length +
2102
2420
  investigationWarn.length +
2103
2421
  sessionWarn.length +
2104
2422
  installWarn.length +
@@ -2109,8 +2427,9 @@ function buildHealthReport(target) {
2109
2427
  (sumWarn ? 1 : 0);
2110
2428
  const healthStatus = hardFailure ? 'broken' : warningCount > 0 ? 'warning' : 'healthy';
2111
2429
 
2112
- return {
2113
- configPath: cfgPath,
2430
+ return {
2431
+ workspaceMode: workspaceInfo.workspaceMode,
2432
+ configPath: cfgPath,
2114
2433
  configParseError: parseError,
2115
2434
  unknownConfigKeys: shape.unknownKeys,
2116
2435
  typeErrors: shape.typeErrors,
@@ -2135,9 +2454,17 @@ function buildHealthReport(target) {
2135
2454
  copilot,
2136
2455
  summaryGapWarn: sumWarn,
2137
2456
  specWarn,
2138
- planWarn,
2139
- planSelfEvaluation,
2140
- planReviewStatus: parsePlanReviewStatus(stateText),
2457
+ planWarn,
2458
+ planSelfEvaluation,
2459
+ implementationPackReady: executionRationality.implementationPackReady,
2460
+ referenceAnchorsReady: executionRationality.referenceAnchorsReady,
2461
+ fixturePackReady: executionRationality.fixturePackReady,
2462
+ executionRationalityReady: executionRationality.executionRationalityReady,
2463
+ criticalExecutionGaps: executionRationality.criticalExecutionGaps,
2464
+ executionRationality,
2465
+ planConfidenceThreshold: threshold,
2466
+ planConfidenceExecutable: planSelfEvaluation.executable,
2467
+ planReviewStatus: parsePlanReviewStatus(stateText),
2141
2468
  activeRun,
2142
2469
  eventsSummary,
2143
2470
  memoryLayers,
@@ -2175,8 +2502,9 @@ function buildHealthReport(target) {
2175
2502
  : null,
2176
2503
  contextPacks,
2177
2504
  contextQuality,
2178
- semanticsDrift,
2179
- packFreshness,
2505
+ semanticsDrift,
2506
+ releaseReadiness,
2507
+ packFreshness,
2180
2508
  activeSummaryRefs,
2181
2509
  healthStatus,
2182
2510
  next,
@@ -2206,22 +2534,29 @@ module.exports = {
2206
2534
  isStaleLessons,
2207
2535
  copilotWorkspacePaths,
2208
2536
  copilotLegacyPaths,
2209
- copilotIntegrationReport,
2210
- planAgentsWarnings,
2537
+ copilotIntegrationReport,
2538
+ normalizePlanConfidenceThreshold,
2539
+ isExecutablePlanConfidence,
2540
+ hasExecutablePlanSelfEvaluation,
2541
+ planAgentsWarnings,
2211
2542
  installationCompletenessWarnings,
2212
- parsePlanSelfEvaluation,
2213
- planSelfEvaluationWarnings,
2214
- runtimeWarnings,
2215
- planReviewWarnings,
2543
+ parsePlanSelfEvaluation,
2544
+ planSelfEvaluationWarnings,
2545
+ executionRationalityWarningsFromSummary,
2546
+ runtimeWarnings,
2547
+ planReviewWarnings,
2216
2548
  capabilityWarnings,
2217
2549
  investigationWarnings,
2218
2550
  phaseCoherenceWarnings,
2219
2551
  verifyGapsWithoutSummaryWarning,
2220
2552
  specSectionWarnings,
2221
- planWaveWarningsFixed,
2222
- planTaskAceiteWarnings,
2223
- suggestNextStep,
2224
- buildHealthReport,
2225
- oxePaths,
2226
- scopedOxePaths,
2227
- };
2553
+ planWaveWarningsFixed,
2554
+ planTaskAceiteWarnings,
2555
+ detectWorkspaceMode,
2556
+ shouldSuppressExecutionWorkspaceGates,
2557
+ suggestNextStep,
2558
+ buildHealthReport,
2559
+ buildExecutionRationality: rationality.buildExecutionRationality,
2560
+ oxePaths,
2561
+ scopedOxePaths,
2562
+ };