oxe-cc 1.4.1 → 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 (63) 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-verify-audit.md +46 -0
  5. package/.cursor/commands/oxe-workflow-authoring.md +47 -0
  6. package/.github/prompts/oxe-compact.prompt.md +1 -1
  7. package/.github/prompts/oxe-dashboard.prompt.md +2 -2
  8. package/.github/prompts/oxe-execute.prompt.md +2 -2
  9. package/.github/prompts/oxe-plan-agent.prompt.md +1 -0
  10. package/.github/prompts/oxe-plan.prompt.md +2 -2
  11. package/.github/prompts/oxe-verify-audit.prompt.md +46 -0
  12. package/.github/prompts/oxe-workflow-authoring.prompt.md +47 -0
  13. package/.github/workflows/ci.yml +1 -0
  14. package/.github/workflows/release.yml +1 -0
  15. package/AGENTS.md +3 -1
  16. package/CHANGELOG.md +50 -0
  17. package/QUICKSTART.md +99 -0
  18. package/README.md +20 -11
  19. package/bin/lib/oxe-context-engine.cjs +9 -4
  20. package/bin/lib/oxe-dashboard.cjs +119 -53
  21. package/bin/lib/oxe-install-resolve.cjs +10 -0
  22. package/bin/lib/oxe-operational.cjs +34 -28
  23. package/bin/lib/oxe-project-health.cjs +407 -118
  24. package/bin/lib/oxe-rationality.cjs +385 -0
  25. package/bin/lib/oxe-release.cjs +423 -0
  26. package/bin/oxe-cc.js +446 -325
  27. package/commands/oxe/dashboard.md +2 -2
  28. package/commands/oxe/execute.md +2 -2
  29. package/commands/oxe/plan.md +2 -2
  30. package/commands/oxe/verify-audit.md +50 -0
  31. package/commands/oxe/workflow-authoring.md +50 -0
  32. package/docs/INCIDENT-PLAYBOOK.md +181 -0
  33. package/docs/RELEASE-READINESS.md +46 -0
  34. package/docs/ROLES.md +129 -0
  35. package/docs/RUNTIME-SMOKE-MATRIX.md +128 -0
  36. package/docs/TEAM-ADOPTION.md +153 -0
  37. package/docs/WALKTHROUGH.md +241 -0
  38. package/lib/runtime/scheduler/multi-agent-coordinator.d.ts +28 -0
  39. package/lib/runtime/scheduler/multi-agent-coordinator.js +152 -26
  40. package/lib/sdk/README.md +2 -0
  41. package/lib/sdk/index.cjs +32 -14
  42. package/lib/sdk/index.d.ts +138 -40
  43. package/oxe/templates/CONFIG.md +1 -1
  44. package/oxe/templates/EXECUTION-RUNTIME.template.md +1 -1
  45. package/oxe/templates/FIXTURE-PACK.template.json +34 -0
  46. package/oxe/templates/FIXTURE-PACK.template.md +21 -0
  47. package/oxe/templates/IMPLEMENTATION-PACK.template.json +52 -0
  48. package/oxe/templates/IMPLEMENTATION-PACK.template.md +36 -0
  49. package/oxe/templates/PLAN.template.md +46 -37
  50. package/oxe/templates/REFERENCE-ANCHORS.template.md +24 -0
  51. package/oxe/templates/config.template.json +2 -1
  52. package/oxe/workflows/execute.md +36 -20
  53. package/oxe/workflows/next.md +1 -1
  54. package/oxe/workflows/plan.md +80 -22
  55. package/oxe/workflows/references/flow-robustness-contract.md +3 -3
  56. package/oxe/workflows/references/workflow-runtime-contracts.json +127 -95
  57. package/oxe/workflows/verify.md +4 -4
  58. package/package.json +28 -20
  59. package/packages/runtime/package.json +1 -1
  60. package/packages/runtime/src/scheduler/multi-agent-coordinator.ts +357 -193
  61. package/vscode-extension/oxe-agents-1.5.0.vsix +0 -0
  62. package/vscode-extension/oxe-agents-1.5.1.vsix +0 -0
  63. 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 = [
@@ -24,6 +25,7 @@ const ALLOWED_CONFIG_KEYS = [
24
25
  'verification_depth',
25
26
  'plan_confidence_threshold',
26
27
  'security_in_verify',
28
+ 'adversarial_verify',
27
29
  'install',
28
30
  'plugins',
29
31
  'workstreams',
@@ -52,17 +54,77 @@ const INSTALL_PROFILES = ['recommended', 'cursor', 'copilot', 'core', 'cli', 'al
52
54
  const INSTALL_REPO_LAYOUTS = ['nested', 'classic'];
53
55
 
54
56
  /** @type {string[]} */
55
- const INSTALL_OBJECT_KEYS = ['profile', 'repo_layout', 'vscode', 'include_commands_dir', 'include_agents_md'];
56
-
57
- const EXPECTED_CODEBASE_MAPS = [
58
- 'OVERVIEW.md',
59
- 'STACK.md',
60
- 'STRUCTURE.md',
61
- 'TESTING.md',
62
- 'INTEGRATIONS.md',
63
- 'CONVENTIONS.md',
64
- 'CONCERNS.md',
65
- ];
57
+ const INSTALL_OBJECT_KEYS = ['profile', 'repo_layout', 'ide_scope', 'vscode', 'include_commands_dir', 'include_agents_md'];
58
+
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
+ }
66
128
 
67
129
  /**
68
130
  * @param {string} targetProject
@@ -142,9 +204,9 @@ function loadOxeConfigMerged(targetProject) {
142
204
  discuss_before_plan: false,
143
205
  after_verify_suggest_pr: true,
144
206
  after_verify_draft_commit: true,
145
- after_verify_suggest_uat: false,
146
- verification_depth: 'standard',
147
- plan_confidence_threshold: 70,
207
+ after_verify_suggest_uat: false,
208
+ verification_depth: 'standard',
209
+ plan_confidence_threshold: 90,
148
210
  default_verify_command: '',
149
211
  scan_max_age_days: 0,
150
212
  compact_max_age_days: 0,
@@ -152,6 +214,8 @@ function loadOxeConfigMerged(targetProject) {
152
214
  scan_ignore_globs: [],
153
215
  spec_required_sections: [],
154
216
  plan_max_tasks_per_wave: 0,
217
+ lessons_max_age_days: 0,
218
+ install: {},
155
219
  azure: {
156
220
  enabled: false,
157
221
  default_resource_group: '',
@@ -212,15 +276,18 @@ function loadOxeConfigMerged(targetProject) {
212
276
  if (typeof layer.profile === 'string') {
213
277
  Object.assign(merged, expandExecutionProfile(layer.profile));
214
278
  }
279
+ const layerFlat = { ...layer };
215
280
  // Azure: merge aninhado para não sobrescrever campos não especificados
216
281
  if (layer.azure && typeof layer.azure === 'object' && !Array.isArray(layer.azure)) {
217
282
  merged.azure = { .../** @type {any} */ (merged.azure), ...layer.azure };
218
- const layerWithoutAzure = { ...layer };
219
- delete layerWithoutAzure.azure;
220
- Object.assign(merged, layerWithoutAzure);
221
- } else {
222
- Object.assign(merged, layer);
283
+ delete layerFlat.azure;
284
+ }
285
+ // Install: merge aninhado para não sobrescrever campos não especificados
286
+ if (layer.install && typeof layer.install === 'object' && !Array.isArray(layer.install)) {
287
+ merged.install = { .../** @type {any} */ (merged.install || {}), ...layer.install };
288
+ delete layerFlat.install;
223
289
  }
290
+ Object.assign(merged, layerFlat);
224
291
  }
225
292
 
226
293
  const primaryPath = sources.project || sources.user || sources.system || null;
@@ -262,6 +329,11 @@ function validateConfigShape(cfg) {
262
329
  );
263
330
  }
264
331
  }
332
+ if (inst.ide_scope != null) {
333
+ if (typeof inst.ide_scope !== 'string' || !['global', 'local'].includes(inst.ide_scope)) {
334
+ typeErrors.push('install.ide_scope deve ser "global" ou "local"');
335
+ }
336
+ }
265
337
  if (inst.vscode != null && typeof inst.vscode !== 'boolean') {
266
338
  typeErrors.push('install.vscode deve ser boolean');
267
339
  }
@@ -314,6 +386,27 @@ function validateConfigShape(cfg) {
314
386
  if (cfg.scale_adaptive != null && typeof cfg.scale_adaptive !== 'boolean') {
315
387
  typeErrors.push('scale_adaptive deve ser boolean');
316
388
  }
389
+ if (cfg.discuss_before_plan != null && typeof cfg.discuss_before_plan !== 'boolean') {
390
+ typeErrors.push('discuss_before_plan deve ser boolean');
391
+ }
392
+ if (cfg.after_verify_suggest_pr != null && typeof cfg.after_verify_suggest_pr !== 'boolean') {
393
+ typeErrors.push('after_verify_suggest_pr deve ser boolean');
394
+ }
395
+ if (cfg.after_verify_draft_commit != null && typeof cfg.after_verify_draft_commit !== 'boolean') {
396
+ typeErrors.push('after_verify_draft_commit deve ser boolean');
397
+ }
398
+ if (cfg.security_in_verify != null && typeof cfg.security_in_verify !== 'boolean') {
399
+ typeErrors.push('security_in_verify deve ser boolean');
400
+ }
401
+ if (cfg.adversarial_verify != null && typeof cfg.adversarial_verify !== 'boolean') {
402
+ typeErrors.push('adversarial_verify deve ser boolean');
403
+ }
404
+ if (cfg.lessons_max_age_days != null && typeof cfg.lessons_max_age_days !== 'number') {
405
+ typeErrors.push('lessons_max_age_days deve ser número (use 0 para desligar)');
406
+ }
407
+ if (cfg.default_verify_command != null && typeof cfg.default_verify_command !== 'string') {
408
+ typeErrors.push('default_verify_command deve ser string');
409
+ }
317
410
  if (cfg.azure != null) {
318
411
  if (typeof cfg.azure !== 'object' || Array.isArray(cfg.azure)) {
319
412
  typeErrors.push('azure deve ser um objeto');
@@ -558,10 +651,15 @@ function oxePaths(target) {
558
651
  events: path.join(oxe, 'OXE-EVENTS.ndjson'),
559
652
  copilotManifest: path.join(oxe, 'install', 'copilot-vscode.json'),
560
653
  runtimeSemanticsManifest: path.join(oxe, 'install', 'runtime-semantics.json'),
561
- spec: path.join(oxe, 'SPEC.md'),
562
- plan: path.join(oxe, 'PLAN.md'),
563
- quick: path.join(oxe, 'QUICK.md'),
564
- 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'),
565
663
  discuss: path.join(oxe, 'DISCUSS.md'),
566
664
  summary: path.join(oxe, 'SUMMARY.md'),
567
665
  codebase: path.join(oxe, 'codebase'),
@@ -574,11 +672,11 @@ function oxePaths(target) {
574
672
  * @param {string} target
575
673
  * @param {string | null} activeSession
576
674
  */
577
- function scopedOxePaths(target, activeSession) {
578
- const base = oxePaths(target);
579
- if (!activeSession) return { ...base, activeSession: null, scopedRoot: base.oxe };
580
- const sessionRoot = path.join(base.oxe, ...activeSession.split('/'));
581
- 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 {
582
680
  ...base,
583
681
  activeSession,
584
682
  scopedRoot: sessionRoot,
@@ -593,15 +691,61 @@ function scopedOxePaths(target, activeSession) {
593
691
  events: path.join(sessionRoot, 'execution', 'OXE-EVENTS.ndjson'),
594
692
  investigationsIndex: path.join(sessionRoot, 'research', 'INVESTIGATIONS.md'),
595
693
  investigationsDir: path.join(sessionRoot, 'research', 'investigations'),
596
- spec: path.join(sessionRoot, 'spec', 'SPEC.md'),
597
- discuss: path.join(sessionRoot, 'spec', 'DISCUSS.md'),
598
- plan: path.join(sessionRoot, 'plan', 'PLAN.md'),
599
- quick: path.join(sessionRoot, 'plan', 'QUICK.md'),
600
- 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'),
601
705
  summary: path.join(sessionRoot, 'verification', 'SUMMARY.md'),
602
- executionState: path.join(sessionRoot, 'execution', 'STATE.md'),
603
- };
604
- }
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
+ }
605
749
 
606
750
  /**
607
751
  * @param {string} target
@@ -1404,10 +1548,10 @@ function planWaveWarningsFixed(planPath, maxPerWave) {
1404
1548
  * warnings: string[],
1405
1549
  * }}
1406
1550
  */
1407
- function parsePlanSelfEvaluation(planPath) {
1408
- const empty = { hasSection: false, bestPlan: null, confidence: null, warnings: [] };
1409
- if (!fs.existsSync(planPath)) return empty;
1410
- 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');
1411
1555
  const m = raw.match(/##\s*Autoavaliação do Plano\s*([\s\S]*?)(?=\n## |\n#[^\#]|$)/i);
1412
1556
  if (!m) {
1413
1557
  return {
@@ -1415,27 +1559,60 @@ function parsePlanSelfEvaluation(planPath) {
1415
1559
  warnings: ['PLAN.md sem a seção obrigatória "## Autoavaliação do Plano"'],
1416
1560
  };
1417
1561
  }
1418
- const body = m[1];
1419
- const best = body.match(/\*\*Melhor plano atual:\*\*\s*(sim|não|nao)/i);
1420
- const confidence = body.match(/\*\*Confiança:\*\*\s*(\d{1,3})\s*%/i);
1421
- /** @type {string[]} */
1422
- const warnings = [];
1423
- const rubricLabels = [
1424
- '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',
1425
1570
  'Dependências conhecidas',
1426
1571
  'Risco técnico',
1427
1572
  'Impacto no código existente',
1428
1573
  'Clareza da validação / testes',
1429
1574
  'Lacunas externas / decisões pendentes',
1430
1575
  ];
1431
- if (!best) warnings.push('PLAN.md: autoavaliação sem "Melhor plano atual: sim|não"');
1432
- if (!confidence) warnings.push('PLAN.md: autoavaliação sem "Confiança: NN%"');
1433
- if (!/\*\*Principais incertezas:\*\*/i.test(body)) warnings.push('PLAN.md: autoavaliação sem "Principais incertezas"');
1434
- if (!/\*\*Alternativas descartadas:\*\*/i.test(body)) warnings.push('PLAN.md: autoavaliação sem "Alternativas descartadas"');
1435
- if (!/\*\*Condição para replanejar:\*\*/i.test(body)) warnings.push('PLAN.md: autoavaliação sem "Condição para replanejar"');
1436
- for (const label of rubricLabels) {
1437
- if (!body.includes(label)) warnings.push(`PLAN.md: rubrica sem "${label}"`);
1438
- }
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
+ }
1439
1616
  const parsedConfidence = confidence ? Number(confidence[1]) : null;
1440
1617
  if (parsedConfidence != null && (parsedConfidence < 0 || parsedConfidence > 100)) {
1441
1618
  warnings.push('PLAN.md: confiança fora do intervalo 0–100%');
@@ -1446,23 +1623,70 @@ function parsePlanSelfEvaluation(planPath) {
1446
1623
  confidence: parsedConfidence,
1447
1624
  warnings,
1448
1625
  };
1449
- }
1450
-
1451
- /**
1452
- * @param {string} planPath
1453
- * @param {number} threshold
1454
- * @returns {string[]}
1455
- */
1456
- function planSelfEvaluationWarnings(planPath, threshold) {
1457
- const info = parsePlanSelfEvaluation(planPath);
1458
- const warns = [...info.warnings];
1459
- if (!fs.existsSync(planPath)) return warns;
1460
- if (info.bestPlan === 'não') warns.push('PLAN.md: autoavaliação declara que este não é o melhor plano atual');
1461
- if (info.confidence != null && info.confidence < threshold) {
1462
- warns.push(`PLAN.md: confiança ${info.confidence}% abaixo do limiar executável (${threshold}%)`);
1463
- }
1464
- return warns;
1465
- }
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
+ }
1466
1690
 
1467
1691
  /**
1468
1692
  * @param {string} target
@@ -1616,12 +1840,12 @@ function planReviewWarnings(stateText, p) {
1616
1840
  * @param {string} target
1617
1841
  * @param {{ discuss_before_plan?: boolean }} cfg
1618
1842
  */
1619
- function suggestNextStep(target, cfg = {}) {
1843
+ function suggestNextStep(target, cfg = {}) {
1620
1844
  const base = oxePaths(target);
1621
1845
  const stateText = fs.existsSync(base.state) ? fs.readFileSync(base.state, 'utf8') : '';
1622
- const p = scopedOxePaths(target, parseActiveSession(stateText));
1846
+ const p = resolvedReadableOxePaths(target, parseActiveSession(stateText));
1623
1847
  const discussBefore = Boolean(cfg.discuss_before_plan);
1624
- const threshold = Number(cfg.plan_confidence_threshold) || 70;
1848
+ const threshold = normalizePlanConfidenceThreshold(cfg.plan_confidence_threshold);
1625
1849
  const has = (/** @type {string} */ f) => fs.existsSync(f);
1626
1850
  const mapsComplete = EXPECTED_CODEBASE_MAPS.every((f) => has(path.join(p.codebase, f)));
1627
1851
  const azureActive = azure.isAzureContextEnabled(target, cfg);
@@ -1720,17 +1944,49 @@ function suggestNextStep(target, cfg = {}) {
1720
1944
  };
1721
1945
  }
1722
1946
 
1723
- const selfEval = parsePlanSelfEvaluation(p.plan);
1724
- if (selfEval.bestPlan === 'não' || (selfEval.confidence != null && selfEval.confidence < threshold)) {
1725
- return {
1726
- step: 'plan',
1727
- cursorCmd: '/oxe-plan --replan',
1728
- reason: `O plano atual ainda não atingiu confiança executável (limiar ${threshold}%)`,
1729
- artifacts: ['.oxe/PLAN.md', '.oxe/STATE.md'],
1730
- };
1731
- }
1732
-
1733
- 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);
1734
1990
  if (phase === 'plan_ready' && (reviewStatus === 'needs_revision' || reviewStatus === 'rejected')) {
1735
1991
  return {
1736
1992
  step: 'plan',
@@ -1861,7 +2117,7 @@ function buildHealthReport(target) {
1861
2117
  }
1862
2118
  }
1863
2119
  const activeSession = parseActiveSession(stateText);
1864
- const p = scopedOxePaths(target, activeSession);
2120
+ const p = resolvedReadableOxePaths(target, activeSession);
1865
2121
  const phase = parseStatePhase(stateText);
1866
2122
  const scanDate = parseLastScanDate(stateText);
1867
2123
  const stale = isStaleScan(scanDate, Number(config.scan_max_age_days) || 0);
@@ -1872,23 +2128,43 @@ function buildHealthReport(target) {
1872
2128
  const phaseWarn = phase ? phaseCoherenceWarnings(phase, p) : [];
1873
2129
  const runtimeWarn = runtimeWarnings(stateText, p);
1874
2130
  const sumWarn = verifyGapsWithoutSummaryWarning(p.verify, p.summary);
1875
- const specReq = Array.isArray(config.spec_required_sections) ? config.spec_required_sections : [];
1876
- const specWarn = specSectionWarnings(p.spec, specReq.map(String));
1877
- const threshold = Number(config.plan_confidence_threshold) || 70;
1878
- const capabilityWarn = capabilityWarnings(p);
1879
- const investigationWarn = investigationWarnings(p);
1880
- const planWarn = [
1881
- ...planWaveWarningsFixed(p.plan, Number(config.plan_max_tasks_per_wave) || 0),
1882
- ...planTaskAceiteWarnings(p.plan),
1883
- ...planSelfEvaluationWarnings(p.plan, threshold),
1884
- ...planAgentsWarnings(target),
1885
- ];
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
+ ];
1886
2153
  const sessionWarn = sessionWarnings(target, activeSession);
1887
2154
  const installWarn = installationCompletenessWarnings(target);
1888
2155
  const copilot = copilotIntegrationReport(target);
1889
2156
  const copilotWarn = copilot.warnings;
1890
2157
  const reviewWarn = planReviewWarnings(stateText, p);
1891
- 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
+ };
1892
2168
  const activeRun = operational.readRunState(target, activeSession);
1893
2169
  const eventsSummary = operational.summarizeEvents(operational.readEvents(target, activeSession));
1894
2170
  const memoryLayers = operational.buildMemoryLayers(target, activeSession);
@@ -2103,9 +2379,17 @@ function buildHealthReport(target) {
2103
2379
  copilot,
2104
2380
  summaryGapWarn: sumWarn,
2105
2381
  specWarn,
2106
- planWarn,
2107
- planSelfEvaluation,
2108
- 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),
2109
2393
  activeRun,
2110
2394
  eventsSummary,
2111
2395
  memoryLayers,
@@ -2174,13 +2458,17 @@ module.exports = {
2174
2458
  isStaleLessons,
2175
2459
  copilotWorkspacePaths,
2176
2460
  copilotLegacyPaths,
2177
- copilotIntegrationReport,
2178
- planAgentsWarnings,
2461
+ copilotIntegrationReport,
2462
+ normalizePlanConfidenceThreshold,
2463
+ isExecutablePlanConfidence,
2464
+ hasExecutablePlanSelfEvaluation,
2465
+ planAgentsWarnings,
2179
2466
  installationCompletenessWarnings,
2180
- parsePlanSelfEvaluation,
2181
- planSelfEvaluationWarnings,
2182
- runtimeWarnings,
2183
- planReviewWarnings,
2467
+ parsePlanSelfEvaluation,
2468
+ planSelfEvaluationWarnings,
2469
+ executionRationalityWarningsFromSummary,
2470
+ runtimeWarnings,
2471
+ planReviewWarnings,
2184
2472
  capabilityWarnings,
2185
2473
  investigationWarnings,
2186
2474
  phaseCoherenceWarnings,
@@ -2188,8 +2476,9 @@ module.exports = {
2188
2476
  specSectionWarnings,
2189
2477
  planWaveWarningsFixed,
2190
2478
  planTaskAceiteWarnings,
2191
- suggestNextStep,
2192
- buildHealthReport,
2193
- oxePaths,
2194
- scopedOxePaths,
2195
- };
2479
+ suggestNextStep,
2480
+ buildHealthReport,
2481
+ buildExecutionRationality: rationality.buildExecutionRationality,
2482
+ oxePaths,
2483
+ scopedOxePaths,
2484
+ };