oxe-cc 1.2.1 → 1.3.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 (276) hide show
  1. package/.cursor/commands/oxe-ask.md +2 -2
  2. package/.cursor/commands/oxe-capabilities.md +2 -2
  3. package/.cursor/commands/oxe-checkpoint.md +2 -2
  4. package/.cursor/commands/oxe-compact.md +2 -2
  5. package/.cursor/commands/oxe-dashboard.md +2 -2
  6. package/.cursor/commands/oxe-debug.md +2 -2
  7. package/.cursor/commands/oxe-discuss.md +2 -2
  8. package/.cursor/commands/oxe-execute.md +5 -2
  9. package/.cursor/commands/oxe-forensics.md +2 -2
  10. package/.cursor/commands/oxe-help.md +2 -2
  11. package/.cursor/commands/oxe-loop.md +2 -2
  12. package/.cursor/commands/oxe-milestone.md +2 -2
  13. package/.cursor/commands/oxe-next.md +2 -2
  14. package/.cursor/commands/oxe-obs.md +2 -2
  15. package/.cursor/commands/oxe-plan-agent.md +2 -2
  16. package/.cursor/commands/oxe-plan.md +2 -2
  17. package/.cursor/commands/oxe-project.md +2 -2
  18. package/.cursor/commands/oxe-quick.md +2 -2
  19. package/.cursor/commands/oxe-research.md +2 -2
  20. package/.cursor/commands/oxe-retro.md +2 -2
  21. package/.cursor/commands/oxe-review-pr.md +2 -2
  22. package/.cursor/commands/oxe-route.md +2 -2
  23. package/.cursor/commands/oxe-scan.md +2 -2
  24. package/.cursor/commands/oxe-security.md +2 -2
  25. package/.cursor/commands/oxe-session.md +2 -2
  26. package/.cursor/commands/oxe-ship.md +2 -2
  27. package/.cursor/commands/oxe-skill.md +2 -2
  28. package/.cursor/commands/oxe-spec.md +2 -2
  29. package/.cursor/commands/oxe-ui-review.md +2 -2
  30. package/.cursor/commands/oxe-ui-spec.md +2 -2
  31. package/.cursor/commands/oxe-update.md +2 -2
  32. package/.cursor/commands/oxe-validate-gaps.md +2 -2
  33. package/.cursor/commands/oxe-verify.md +5 -2
  34. package/.cursor/commands/oxe-workstream.md +2 -2
  35. package/.cursor/commands/oxe.md +2 -2
  36. package/.github/copilot-instructions.md +13 -13
  37. package/.github/prompts/oxe-ask.prompt.md +2 -2
  38. package/.github/prompts/oxe-capabilities.prompt.md +2 -2
  39. package/.github/prompts/oxe-checkpoint.prompt.md +2 -2
  40. package/.github/prompts/oxe-compact.prompt.md +2 -2
  41. package/.github/prompts/oxe-dashboard.prompt.md +2 -2
  42. package/.github/prompts/oxe-debug.prompt.md +2 -2
  43. package/.github/prompts/oxe-discuss.prompt.md +2 -2
  44. package/.github/prompts/oxe-execute.prompt.md +5 -2
  45. package/.github/prompts/oxe-forensics.prompt.md +2 -2
  46. package/.github/prompts/oxe-help.prompt.md +2 -2
  47. package/.github/prompts/oxe-loop.prompt.md +2 -2
  48. package/.github/prompts/oxe-milestone.prompt.md +2 -2
  49. package/.github/prompts/oxe-next.prompt.md +2 -2
  50. package/.github/prompts/oxe-obs.prompt.md +2 -2
  51. package/.github/prompts/oxe-plan-agent.prompt.md +2 -2
  52. package/.github/prompts/oxe-plan.prompt.md +2 -2
  53. package/.github/prompts/oxe-project.prompt.md +2 -2
  54. package/.github/prompts/oxe-quick.prompt.md +2 -2
  55. package/.github/prompts/oxe-research.prompt.md +2 -2
  56. package/.github/prompts/oxe-retro.prompt.md +2 -2
  57. package/.github/prompts/oxe-review-pr.prompt.md +2 -2
  58. package/.github/prompts/oxe-route.prompt.md +2 -2
  59. package/.github/prompts/oxe-scan.prompt.md +2 -2
  60. package/.github/prompts/oxe-security.prompt.md +2 -2
  61. package/.github/prompts/oxe-session.prompt.md +2 -2
  62. package/.github/prompts/oxe-ship.prompt.md +2 -2
  63. package/.github/prompts/oxe-skill.prompt.md +2 -2
  64. package/.github/prompts/oxe-spec.prompt.md +2 -2
  65. package/.github/prompts/oxe-ui-review.prompt.md +2 -2
  66. package/.github/prompts/oxe-ui-spec.prompt.md +2 -2
  67. package/.github/prompts/oxe-update.prompt.md +2 -2
  68. package/.github/prompts/oxe-validate-gaps.prompt.md +2 -2
  69. package/.github/prompts/oxe-verify.prompt.md +5 -2
  70. package/.github/prompts/oxe-workstream.prompt.md +2 -2
  71. package/.github/prompts/oxe.prompt.md +2 -2
  72. package/CHANGELOG.md +52 -17
  73. package/README.md +610 -551
  74. package/bin/banner.txt +1 -1
  75. package/bin/lib/oxe-agent-install.cjs +69 -69
  76. package/bin/lib/oxe-azure.cjs +1445 -1445
  77. package/bin/lib/oxe-context-engine.cjs +867 -867
  78. package/bin/lib/oxe-dashboard.cjs +76 -28
  79. package/bin/lib/oxe-operational.cjs +2144 -1340
  80. package/bin/lib/oxe-project-health.cjs +483 -1
  81. package/bin/lib/oxe-runtime-semantics.cjs +12 -0
  82. package/bin/oxe-cc.js +554 -152
  83. package/commands/oxe/ask.md +2 -2
  84. package/commands/oxe/capabilities.md +2 -2
  85. package/commands/oxe/checkpoint.md +2 -2
  86. package/commands/oxe/compact.md +2 -2
  87. package/commands/oxe/dashboard.md +2 -2
  88. package/commands/oxe/debug.md +2 -2
  89. package/commands/oxe/discuss.md +2 -2
  90. package/commands/oxe/execute.md +5 -2
  91. package/commands/oxe/forensics.md +2 -2
  92. package/commands/oxe/help.md +2 -2
  93. package/commands/oxe/loop.md +2 -2
  94. package/commands/oxe/milestone.md +2 -2
  95. package/commands/oxe/next.md +2 -2
  96. package/commands/oxe/obs.md +2 -2
  97. package/commands/oxe/oxe.md +2 -2
  98. package/commands/oxe/plan-agent.md +2 -2
  99. package/commands/oxe/plan.md +2 -2
  100. package/commands/oxe/project.md +2 -2
  101. package/commands/oxe/quick.md +2 -2
  102. package/commands/oxe/research.md +2 -2
  103. package/commands/oxe/retro.md +2 -2
  104. package/commands/oxe/review-pr.md +2 -2
  105. package/commands/oxe/route.md +2 -2
  106. package/commands/oxe/scan.md +2 -2
  107. package/commands/oxe/security.md +2 -2
  108. package/commands/oxe/session.md +2 -2
  109. package/commands/oxe/ship.md +2 -2
  110. package/commands/oxe/skill.md +2 -2
  111. package/commands/oxe/spec.md +2 -2
  112. package/commands/oxe/ui-review.md +2 -2
  113. package/commands/oxe/ui-spec.md +2 -2
  114. package/commands/oxe/update.md +2 -2
  115. package/commands/oxe/validate-gaps.md +2 -2
  116. package/commands/oxe/verify.md +5 -2
  117. package/commands/oxe/workstream.md +2 -2
  118. package/lib/runtime/delivery/branch-manager.d.ts +1 -0
  119. package/lib/runtime/delivery/branch-manager.js +7 -0
  120. package/lib/runtime/delivery/ci-checks.js +34 -1
  121. package/lib/runtime/delivery/delivery-records.d.ts +34 -0
  122. package/lib/runtime/delivery/delivery-records.js +48 -0
  123. package/lib/runtime/delivery/index.d.ts +1 -0
  124. package/lib/runtime/delivery/index.js +1 -0
  125. package/lib/runtime/delivery/promotion-pipeline.d.ts +26 -2
  126. package/lib/runtime/delivery/promotion-pipeline.js +111 -14
  127. package/lib/runtime/gate/gate-manager.d.ts +41 -0
  128. package/lib/runtime/gate/gate-manager.js +108 -1
  129. package/lib/runtime/index.d.ts +2 -2
  130. package/lib/runtime/index.js +3 -1
  131. package/lib/runtime/models/gate-decision.d.ts +4 -1
  132. package/lib/runtime/models/workspace.d.ts +3 -0
  133. package/lib/runtime/plugins/capability-adapter.d.ts +12 -0
  134. package/lib/runtime/plugins/capability-adapter.js +204 -0
  135. package/lib/runtime/plugins/capability-matrix.d.ts +5 -0
  136. package/lib/runtime/plugins/capability-matrix.js +48 -17
  137. package/lib/runtime/plugins/index.d.ts +1 -0
  138. package/lib/runtime/plugins/index.js +1 -0
  139. package/lib/runtime/plugins/plugin-abi.d.ts +2 -0
  140. package/lib/runtime/plugins/plugin-manifest.d.ts +1 -1
  141. package/lib/runtime/plugins/plugin-manifest.js +6 -2
  142. package/lib/runtime/plugins/plugin-registry.d.ts +46 -0
  143. package/lib/runtime/plugins/plugin-registry.js +79 -2
  144. package/lib/runtime/policy/policy-engine.d.ts +19 -0
  145. package/lib/runtime/policy/policy-engine.js +76 -4
  146. package/lib/runtime/projection/projection-engine.d.ts +9 -1
  147. package/lib/runtime/projection/projection-engine.js +73 -3
  148. package/lib/runtime/scheduler/multi-agent-coordinator.d.ts +43 -1
  149. package/lib/runtime/scheduler/multi-agent-coordinator.js +151 -39
  150. package/lib/runtime/scheduler/run-journal.d.ts +1 -1
  151. package/lib/runtime/scheduler/scheduler.d.ts +19 -1
  152. package/lib/runtime/scheduler/scheduler.js +258 -13
  153. package/lib/runtime/verification/verification-compiler.d.ts +43 -0
  154. package/lib/runtime/verification/verification-compiler.js +137 -0
  155. package/lib/runtime/verification/verification-manifest.d.ts +9 -0
  156. package/lib/runtime/verification/verification-manifest.js +56 -6
  157. package/lib/runtime/workspace/strategies/ephemeral-container.d.ts +1 -0
  158. package/lib/runtime/workspace/strategies/ephemeral-container.js +4 -0
  159. package/lib/runtime/workspace/strategies/git-worktree.d.ts +1 -0
  160. package/lib/runtime/workspace/strategies/git-worktree.js +2 -0
  161. package/lib/runtime/workspace/strategies/inplace.d.ts +1 -0
  162. package/lib/runtime/workspace/strategies/inplace.js +2 -0
  163. package/lib/runtime/workspace/workspace-manager.d.ts +2 -1
  164. package/lib/sdk/README.md +9 -9
  165. package/lib/sdk/index.cjs +33 -24
  166. package/lib/sdk/index.d.ts +149 -14
  167. package/oxe/templates/ACTIVE-RUN.template.json +32 -32
  168. package/oxe/templates/CAPABILITIES.template.md +7 -7
  169. package/oxe/templates/CAPABILITY.template.md +45 -45
  170. package/oxe/templates/CHECKPOINTS.template.md +7 -7
  171. package/oxe/templates/EXECUTION-RUNTIME.template.md +68 -68
  172. package/oxe/templates/HYPOTHESES.template.md +33 -33
  173. package/oxe/templates/LESSONS-METRICS.template.json +13 -13
  174. package/oxe/templates/NOTES.template.md +16 -16
  175. package/oxe/templates/PLAN-REVIEW.template.md +31 -31
  176. package/oxe/templates/SESSION.template.md +34 -34
  177. package/oxe/templates/SKILL.template.md +26 -26
  178. package/oxe/templates/STATE.md +55 -55
  179. package/oxe/templates/WORKFLOW_AUTHORING.md +18 -18
  180. package/oxe/workflows/ask.md +96 -96
  181. package/oxe/workflows/capabilities.md +25 -25
  182. package/oxe/workflows/dashboard.md +33 -33
  183. package/oxe/workflows/discuss.md +12 -12
  184. package/oxe/workflows/execute.md +14 -0
  185. package/oxe/workflows/help.md +352 -352
  186. package/oxe/workflows/next.md +22 -22
  187. package/oxe/workflows/oxe.md +6 -6
  188. package/oxe/workflows/plan-agent.md +9 -9
  189. package/oxe/workflows/quick.md +10 -10
  190. package/oxe/workflows/references/reasoning-discovery.md +28 -28
  191. package/oxe/workflows/references/reasoning-execution.md +29 -29
  192. package/oxe/workflows/references/reasoning-planning.md +32 -32
  193. package/oxe/workflows/references/reasoning-review.md +29 -29
  194. package/oxe/workflows/references/reasoning-status.md +24 -24
  195. package/oxe/workflows/references/robustness-elevation.md +295 -295
  196. package/oxe/workflows/references/workflow-runtime-contracts.json +952 -930
  197. package/oxe/workflows/route.md +16 -16
  198. package/oxe/workflows/session.md +213 -213
  199. package/oxe/workflows/ship.md +142 -142
  200. package/oxe/workflows/skill.md +44 -44
  201. package/oxe/workflows/ui-review.md +36 -36
  202. package/oxe/workflows/verify-audit.md +73 -73
  203. package/oxe/workflows/verify.md +10 -0
  204. package/package.json +92 -92
  205. package/packages/runtime/package.json +17 -17
  206. package/packages/runtime/src/audit/audit-trail.ts +243 -243
  207. package/packages/runtime/src/audit/index.ts +2 -2
  208. package/packages/runtime/src/audit/policy-pack.ts +62 -62
  209. package/packages/runtime/src/compiler/graph-compiler.ts +245 -245
  210. package/packages/runtime/src/compiler/index.ts +1 -1
  211. package/packages/runtime/src/context/context-pack-builder.ts +259 -259
  212. package/packages/runtime/src/context/context-pack-store.ts +197 -197
  213. package/packages/runtime/src/context/context-profiles.ts +60 -60
  214. package/packages/runtime/src/context/index.ts +3 -3
  215. package/packages/runtime/src/decision/decision-engine.ts +174 -174
  216. package/packages/runtime/src/decision/decision-memo.ts +211 -211
  217. package/packages/runtime/src/decision/index.ts +2 -2
  218. package/packages/runtime/src/delivery/branch-manager.ts +91 -84
  219. package/packages/runtime/src/delivery/ci-checks.ts +285 -252
  220. package/packages/runtime/src/delivery/delivery-records.ts +75 -0
  221. package/packages/runtime/src/delivery/index.ts +5 -4
  222. package/packages/runtime/src/delivery/pr-manager.ts +112 -112
  223. package/packages/runtime/src/delivery/promotion-pipeline.ts +334 -180
  224. package/packages/runtime/src/events/bus.ts +92 -92
  225. package/packages/runtime/src/events/catalog.ts +29 -29
  226. package/packages/runtime/src/events/envelope.ts +14 -14
  227. package/packages/runtime/src/events/index.ts +3 -3
  228. package/packages/runtime/src/evidence/evidence-store.ts +130 -130
  229. package/packages/runtime/src/evidence/index.ts +1 -1
  230. package/packages/runtime/src/gate/gate-manager.ts +289 -137
  231. package/packages/runtime/src/gate/index.ts +1 -1
  232. package/packages/runtime/src/index.ts +41 -37
  233. package/packages/runtime/src/models/attempt.ts +19 -19
  234. package/packages/runtime/src/models/evidence.ts +21 -21
  235. package/packages/runtime/src/models/gate-decision.ts +25 -21
  236. package/packages/runtime/src/models/index.ts +8 -8
  237. package/packages/runtime/src/models/run.ts +24 -24
  238. package/packages/runtime/src/models/session.ts +11 -11
  239. package/packages/runtime/src/models/verification-result.ts +10 -10
  240. package/packages/runtime/src/models/work-item.ts +25 -25
  241. package/packages/runtime/src/models/workspace.ts +31 -28
  242. package/packages/runtime/src/plugins/capability-adapter.ts +206 -0
  243. package/packages/runtime/src/plugins/capability-matrix.ts +126 -83
  244. package/packages/runtime/src/plugins/index.ts +5 -4
  245. package/packages/runtime/src/plugins/plugin-abi.ts +97 -95
  246. package/packages/runtime/src/plugins/plugin-manifest.ts +118 -113
  247. package/packages/runtime/src/plugins/plugin-registry.ts +232 -124
  248. package/packages/runtime/src/policy/index.ts +1 -1
  249. package/packages/runtime/src/policy/policy-engine.ts +330 -244
  250. package/packages/runtime/src/projection/index.ts +1 -1
  251. package/packages/runtime/src/projection/projection-engine.ts +328 -249
  252. package/packages/runtime/src/reducers/debug-reducer.ts +36 -36
  253. package/packages/runtime/src/reducers/index.ts +2 -2
  254. package/packages/runtime/src/reducers/run-state-reducer.ts +269 -269
  255. package/packages/runtime/src/scheduler/agent-registry.ts +132 -132
  256. package/packages/runtime/src/scheduler/agent-roles.ts +109 -109
  257. package/packages/runtime/src/scheduler/index.ts +4 -4
  258. package/packages/runtime/src/scheduler/multi-agent-coordinator.ts +521 -333
  259. package/packages/runtime/src/scheduler/run-journal.ts +62 -62
  260. package/packages/runtime/src/scheduler/scheduler.ts +722 -441
  261. package/packages/runtime/src/verification/index.ts +2 -2
  262. package/packages/runtime/src/verification/verification-compiler.ts +436 -225
  263. package/packages/runtime/src/verification/verification-manifest.ts +252 -192
  264. package/packages/runtime/src/workspace/index.ts +5 -5
  265. package/packages/runtime/src/workspace/strategies/ephemeral-container.ts +126 -121
  266. package/packages/runtime/src/workspace/strategies/git-worktree.ts +79 -77
  267. package/packages/runtime/src/workspace/strategies/inplace.ts +38 -35
  268. package/packages/runtime/src/workspace/workspace-manager.ts +16 -15
  269. package/packages/runtime/tsconfig.json +17 -17
  270. package/vscode-extension/.vscodeignore +7 -7
  271. package/vscode-extension/oxe-agents-1.0.0.vsix +0 -0
  272. package/vscode-extension/package.json +185 -185
  273. package/vscode-extension/src/extension.js +310 -310
  274. package/vscode-extension/src/shared/contextLoader.js +137 -137
  275. package/vscode-extension/src/shared/contractBuilder.js +159 -159
  276. package/vscode-extension/src/shared/stateReader.js +101 -101
@@ -31,6 +31,7 @@ const ALLOWED_CONFIG_KEYS = [
31
31
  'scale_adaptive',
32
32
  'permissions',
33
33
  'azure',
34
+ 'runtime',
34
35
  ];
35
36
 
36
37
  /**
@@ -159,6 +160,13 @@ function loadOxeConfigMerged(targetProject) {
159
160
  resource_graph_auto_install: true,
160
161
  vpn_required: false,
161
162
  },
163
+ runtime: {
164
+ quotas: {
165
+ max_work_items_per_run: null,
166
+ max_mutations_per_run: null,
167
+ max_retries_per_run: null,
168
+ },
169
+ },
162
170
  };
163
171
 
164
172
  const sources = { system: null, user: null, project: null };
@@ -383,6 +391,25 @@ function validateConfigShape(cfg) {
383
391
  }
384
392
  }
385
393
  }
394
+ if (cfg.runtime != null) {
395
+ if (typeof cfg.runtime !== 'object' || Array.isArray(cfg.runtime)) {
396
+ typeErrors.push('runtime deve ser um objeto');
397
+ } else {
398
+ const runtimeCfg = /** @type {Record<string, unknown>} */ (cfg.runtime);
399
+ if (runtimeCfg.quotas != null) {
400
+ if (typeof runtimeCfg.quotas !== 'object' || Array.isArray(runtimeCfg.quotas)) {
401
+ typeErrors.push('runtime.quotas deve ser um objeto');
402
+ } else {
403
+ for (const key of ['max_work_items_per_run', 'max_mutations_per_run', 'max_retries_per_run']) {
404
+ const value = runtimeCfg.quotas[key];
405
+ if (value != null && (typeof value !== 'number' || Number.isNaN(value))) {
406
+ typeErrors.push(`runtime.quotas.${key} deve ser número ou null`);
407
+ }
408
+ }
409
+ }
410
+ }
411
+ }
412
+ }
386
413
  return { unknownKeys, typeErrors };
387
414
  }
388
415
 
@@ -615,6 +642,442 @@ function readJsonFileSafe(filePath) {
615
642
  }
616
643
  }
617
644
 
645
+ function readExecutionGates(target, activeSession) {
646
+ const gatesPath = activeSession
647
+ ? path.join(target, '.oxe', ...String(activeSession).split('/'), 'execution', 'GATES.json')
648
+ : path.join(target, '.oxe', 'execution', 'GATES.json');
649
+ const raw = readJsonFileSafe(gatesPath);
650
+ const gates = raw.ok && Array.isArray(raw.data) ? raw.data : [];
651
+ const pending = gates.filter((gate) => gate && gate.status === 'pending');
652
+ const stalePending = pending.filter((gate) => {
653
+ const requestedAt = Date.parse(String(gate.requested_at || ''));
654
+ return Number.isFinite(requestedAt) && Date.now() - requestedAt > 24 * 60 * 60 * 1000;
655
+ });
656
+ return {
657
+ path: gatesPath,
658
+ gateSlaHours: 24,
659
+ total: gates.length,
660
+ pending,
661
+ stalePending,
662
+ staleGateCount: stalePending.length,
663
+ };
664
+ }
665
+
666
+ function parseAuditTrailFile(filePath) {
667
+ if (!fs.existsSync(filePath)) return [];
668
+ try {
669
+ return fs.readFileSync(filePath, 'utf8')
670
+ .split(/\r?\n/)
671
+ .filter(Boolean)
672
+ .map((line) => JSON.parse(line))
673
+ .filter((entry) => entry && typeof entry === 'object');
674
+ } catch {
675
+ return [];
676
+ }
677
+ }
678
+
679
+ function toNullableNumber(value) {
680
+ if (value == null || value === '') return null;
681
+ const parsed = Number(value);
682
+ return Number.isFinite(parsed) ? parsed : null;
683
+ }
684
+
685
+ function summarizeAuditTrail(target, runId) {
686
+ const auditPath = path.join(target, '.oxe', 'AUDIT-TRAIL.ndjson');
687
+ const entries = parseAuditTrailFile(auditPath);
688
+ const scoped = runId ? entries.filter((entry) => entry.run_id === runId) : entries;
689
+ const actions = {};
690
+ const actors = new Set();
691
+ let oldest = null;
692
+ let newest = null;
693
+ let warn = 0;
694
+ let critical = 0;
695
+ for (const entry of scoped) {
696
+ const action = String(entry.action || 'unknown');
697
+ actions[action] = (actions[action] || 0) + 1;
698
+ if (entry.actor) actors.add(String(entry.actor));
699
+ if (entry.severity === 'warn') warn += 1;
700
+ if (entry.severity === 'critical') critical += 1;
701
+ if (!oldest || String(entry.timestamp || '') < oldest) oldest = String(entry.timestamp || '');
702
+ if (!newest || String(entry.timestamp || '') > newest) newest = String(entry.timestamp || '');
703
+ }
704
+ return {
705
+ path: auditPath,
706
+ totalEntries: entries.length,
707
+ runEntries: scoped.length,
708
+ warn,
709
+ critical,
710
+ oldest,
711
+ newest,
712
+ actors: Array.from(actors),
713
+ actions,
714
+ };
715
+ }
716
+
717
+ function countRetryConsumption(attemptEntries) {
718
+ if (!attemptEntries || typeof attemptEntries !== 'object') return 0;
719
+ if (Array.isArray(attemptEntries)) {
720
+ const grouped = new Map();
721
+ for (const attempt of attemptEntries) {
722
+ const key = attempt && typeof attempt === 'object'
723
+ ? String(attempt.work_item_id || attempt.workItemId || attempt.node_id || attempt.nodeId || 'unknown')
724
+ : 'unknown';
725
+ grouped.set(key, (grouped.get(key) || 0) + 1);
726
+ }
727
+ return Array.from(grouped.values()).reduce((sum, count) => sum + Math.max(0, count - 1), 0);
728
+ }
729
+ return Object.values(attemptEntries).reduce((sum, attempts) => {
730
+ if (Array.isArray(attempts)) return sum + Math.max(0, attempts.length - 1);
731
+ return sum;
732
+ }, 0);
733
+ }
734
+
735
+ function summarizeQuota(config, activeRun, graphNodes, attemptEntries) {
736
+ const runtimeConfig = config && config.runtime && typeof config.runtime === 'object' ? config.runtime : {};
737
+ const quotaConfig = runtimeConfig.quotas && typeof runtimeConfig.quotas === 'object' ? runtimeConfig.quotas : {};
738
+ const workItems = Array.isArray(activeRun && activeRun.canonical_state && activeRun.canonical_state.workItems)
739
+ ? activeRun.canonical_state.workItems
740
+ : [];
741
+ const workItemsConsumed = graphNodes.length > 0 ? graphNodes.length : workItems.length;
742
+ const mutationsConsumed = graphNodes.length > 0
743
+ ? graphNodes.filter((node) => Array.isArray(node.mutation_scope) && node.mutation_scope.length > 0).length
744
+ : workItems.filter((item) => Array.isArray(item.mutation_scope) && item.mutation_scope.length > 0).length;
745
+ const retriesConsumed = countRetryConsumption(attemptEntries);
746
+ const limits = {
747
+ maxWorkItemsPerRun: toNullableNumber(quotaConfig.max_work_items_per_run),
748
+ maxMutationsPerRun: toNullableNumber(quotaConfig.max_mutations_per_run),
749
+ maxRetriesPerRun: toNullableNumber(quotaConfig.max_retries_per_run),
750
+ };
751
+ const violations = [];
752
+ if (limits.maxWorkItemsPerRun != null && workItemsConsumed > limits.maxWorkItemsPerRun) {
753
+ violations.push(`work_items ${workItemsConsumed}/${limits.maxWorkItemsPerRun}`);
754
+ }
755
+ if (limits.maxMutationsPerRun != null && mutationsConsumed > limits.maxMutationsPerRun) {
756
+ violations.push(`mutations ${mutationsConsumed}/${limits.maxMutationsPerRun}`);
757
+ }
758
+ if (limits.maxRetriesPerRun != null && retriesConsumed > limits.maxRetriesPerRun) {
759
+ violations.push(`retries ${retriesConsumed}/${limits.maxRetriesPerRun}`);
760
+ }
761
+ return {
762
+ limits,
763
+ consumed: {
764
+ workItems: workItemsConsumed,
765
+ mutations: mutationsConsumed,
766
+ retries: retriesConsumed,
767
+ },
768
+ violations,
769
+ exceeded: violations.length > 0,
770
+ };
771
+ }
772
+
773
+ function summarizePromotion(runDir, activeRun) {
774
+ const record = activeRun && activeRun.delivery && activeRun.delivery.promotion_record
775
+ ? activeRun.delivery.promotion_record
776
+ : readJsonFileSafe(path.join(runDir, 'promotion-record.json')).data;
777
+ if (!record) return null;
778
+ return {
779
+ status: record.status || null,
780
+ targetKind: record.target_kind || null,
781
+ remote: record.remote || null,
782
+ targetRef: record.target_ref || null,
783
+ prUrl: record.pr_url || null,
784
+ prNumber: record.pr_number != null ? Number(record.pr_number) : null,
785
+ coveragePercent: record.coverage_percent != null ? Number(record.coverage_percent) : null,
786
+ reasons: Array.isArray(record.reasons) ? record.reasons : [],
787
+ path: path.join(runDir, 'promotion-record.json'),
788
+ };
789
+ }
790
+
791
+ function summarizePolicyCoverage(runDir, mutationNodes, policyDecisions) {
792
+ const coveredMutationIds = new Set(
793
+ policyDecisions
794
+ .map((decision) => decision && decision.work_item_id ? String(decision.work_item_id) : null)
795
+ .filter(Boolean)
796
+ );
797
+ const mutationIds = mutationNodes
798
+ .map((node) => node && node.id ? String(node.id) : null)
799
+ .filter(Boolean);
800
+ const uncoveredMutationIds = mutationIds.filter((id) => !coveredMutationIds.has(id));
801
+ const coveragePercent = mutationIds.length > 0
802
+ ? Math.round(((mutationIds.length - uncoveredMutationIds.length) / mutationIds.length) * 100)
803
+ : 100;
804
+ return {
805
+ path: path.join(runDir, 'policy-decisions.json'),
806
+ totalDecisions: policyDecisions.length,
807
+ mutationNodes: mutationIds.length,
808
+ coveredMutations: mutationIds.length - uncoveredMutationIds.length,
809
+ uncoveredMutations: uncoveredMutationIds.length,
810
+ uncoveredMutationIds,
811
+ coveragePercent,
812
+ };
813
+ }
814
+
815
+ function summarizePromotionReadiness(verificationSummary, residualRiskSummary, evidenceCoverage, pendingGates, policyCoverage, promotionSummary, quotaSummary) {
816
+ const blockers = [];
817
+ const minimumCoverage = 100;
818
+ if (!verificationSummary) {
819
+ blockers.push('verification_manifest ausente');
820
+ } else if (!verificationSummary.allPassed || verificationSummary.fail > 0 || verificationSummary.error > 0) {
821
+ blockers.push('verify_failed');
822
+ }
823
+ if (pendingGates && Array.isArray(pendingGates.pending) && pendingGates.pending.length > 0) {
824
+ blockers.push('pending_gates');
825
+ }
826
+ if (residualRiskSummary && residualRiskSummary.highOrCritical > 0) {
827
+ blockers.push('high_or_critical_risks');
828
+ }
829
+ if (evidenceCoverage && Number(evidenceCoverage.coverage_percent || 0) < minimumCoverage) {
830
+ blockers.push('coverage_below_threshold');
831
+ }
832
+ if (policyCoverage && policyCoverage.uncoveredMutations > 0) {
833
+ blockers.push('policy_uncovered_mutations');
834
+ }
835
+ if (quotaSummary && quotaSummary.exceeded) {
836
+ blockers.push('quota_exceeded');
837
+ }
838
+ return {
839
+ status: blockers.length > 0 ? 'blocked' : 'ready',
840
+ blockers,
841
+ targetKind: promotionSummary && promotionSummary.targetKind ? promotionSummary.targetKind : 'pr_draft',
842
+ minimumCoverage,
843
+ coveragePercent: evidenceCoverage && evidenceCoverage.coverage_percent != null ? Number(evidenceCoverage.coverage_percent) : null,
844
+ pendingGateCount: pendingGates && Array.isArray(pendingGates.pending) ? pendingGates.pending.length : 0,
845
+ highOrCriticalRisks: residualRiskSummary ? residualRiskSummary.highOrCritical : 0,
846
+ uncoveredMutationCount: policyCoverage ? policyCoverage.uncoveredMutations : 0,
847
+ quotaExceeded: Boolean(quotaSummary && quotaSummary.exceeded),
848
+ };
849
+ }
850
+
851
+ function summarizeRecoveryState(target, activeSession, activeRun, verificationArtifacts) {
852
+ if (!activeRun || !activeRun.run_id) {
853
+ return {
854
+ status: 'not_started',
855
+ recoverCount: 0,
856
+ journalState: null,
857
+ markdownPath: null,
858
+ issues: [],
859
+ };
860
+ }
861
+ const journalPath = path.join(target, '.oxe', 'runs', activeRun.run_id, 'journal.json');
862
+ const journal = readJsonFileSafe(journalPath).data;
863
+ const consistency = operational.buildRecoveryConsistency(
864
+ target,
865
+ activeSession,
866
+ activeRun,
867
+ journal,
868
+ verificationArtifacts
869
+ );
870
+ const recoverySummary = activeRun.recovery_summary && typeof activeRun.recovery_summary === 'object'
871
+ ? activeRun.recovery_summary
872
+ : null;
873
+ const issues = Array.isArray(consistency.issues) ? consistency.issues : [];
874
+ const recoverCount = Number(activeRun.metrics && activeRun.metrics.recover_count || 0);
875
+ const summaryPath = recoverySummary && recoverySummary.markdown_ref
876
+ ? path.join(target, recoverySummary.markdown_ref)
877
+ : (activeSession
878
+ ? path.join(target, '.oxe', ...String(activeSession).split('/'), 'execution', 'RECOVERY-SUMMARY.md')
879
+ : path.join(target, '.oxe', 'RECOVERY-SUMMARY.md'));
880
+ const status = issues.length > 0
881
+ ? 'warning'
882
+ : activeRun.status === 'paused'
883
+ ? 'recoverable'
884
+ : recoverySummary
885
+ ? 'recovered'
886
+ : 'clean';
887
+ return {
888
+ status,
889
+ recoverCount,
890
+ recoveredAt: recoverySummary ? recoverySummary.recovered_at || null : null,
891
+ journalState: recoverySummary ? recoverySummary.journal_state || null : (journal && journal.scheduler_state ? journal.scheduler_state : null),
892
+ markdownPath: summaryPath,
893
+ orphanWorkItems: recoverySummary && Array.isArray(recoverySummary.orphan_work_items) ? recoverySummary.orphan_work_items : [],
894
+ pendingGatesRehydrated: consistency.pending_gates_rehydrated,
895
+ policyDecisionsRehydrated: consistency.policy_decisions_rehydrated,
896
+ evidenceRefsTracked: consistency.evidence_refs_tracked,
897
+ consistency,
898
+ issues,
899
+ };
900
+ }
901
+
902
+ function summarizeEnterpriseRuntime(target, activeRun, activeSession, config) {
903
+ const pendingGates = readExecutionGates(target, activeSession);
904
+ const auditSummary = summarizeAuditTrail(target, activeRun && activeRun.run_id ? activeRun.run_id : null);
905
+ const runtimeMode = operational.buildRuntimeModeStatus(activeRun);
906
+ const providerCatalog = operational.buildRuntimeProviderCatalog(target);
907
+ if (!activeRun || !activeRun.run_id) {
908
+ return {
909
+ runtimeMode,
910
+ fallbackMode: runtimeMode.fallback_mode,
911
+ verificationSummary: null,
912
+ residualRiskSummary: null,
913
+ evidenceCoverage: null,
914
+ pendingGates,
915
+ gateQueue: pendingGates,
916
+ policyDecisionSummary: { total: 0, denied: 0, gated: 0, overridesWithoutRationale: 0 },
917
+ policyCoverage: {
918
+ path: null,
919
+ totalDecisions: 0,
920
+ mutationNodes: 0,
921
+ coveredMutations: 0,
922
+ uncoveredMutations: 0,
923
+ uncoveredMutationIds: [],
924
+ coveragePercent: 100,
925
+ },
926
+ quotaSummary: summarizeQuota(config, null, [], {}),
927
+ auditSummary,
928
+ promotionSummary: null,
929
+ promotionReadiness: {
930
+ status: 'blocked',
931
+ blockers: ['verification_manifest ausente'],
932
+ targetKind: 'pr_draft',
933
+ minimumCoverage: 100,
934
+ coveragePercent: null,
935
+ pendingGateCount: pendingGates.pending.length,
936
+ highOrCriticalRisks: 0,
937
+ uncoveredMutationCount: 0,
938
+ },
939
+ recoveryState: {
940
+ status: 'not_started',
941
+ recoverCount: 0,
942
+ journalState: null,
943
+ markdownPath: null,
944
+ issues: [],
945
+ },
946
+ multiAgent: null,
947
+ providerCatalog,
948
+ enterpriseWarnings: providerCatalog.load_errors ? [...providerCatalog.load_errors] : [],
949
+ };
950
+ }
951
+
952
+ const runDir = path.join(target, '.oxe', 'runs', activeRun.run_id);
953
+ const manifest = activeRun.verification_manifest || readJsonFileSafe(path.join(runDir, 'verification-manifest.json')).data;
954
+ const risks = activeRun.residual_risks || readJsonFileSafe(path.join(runDir, 'residual-risk-ledger.json')).data || readJsonFileSafe(path.join(runDir, 'residual-risks.json')).data;
955
+ const evidenceCoverage = activeRun.verification_evidence_coverage || readJsonFileSafe(path.join(runDir, 'evidence-coverage.json')).data || (
956
+ manifest && manifest.summary
957
+ ? {
958
+ total_checks: Array.isArray(manifest.checks) ? manifest.checks.length : Number(manifest.summary.total || 0),
959
+ checks_with_evidence: Array.isArray(manifest.checks) ? manifest.checks.filter((check) => Array.isArray(check.evidence_refs) && check.evidence_refs.length > 0).length : 0,
960
+ total_evidence_refs: Array.isArray(manifest.checks) ? manifest.checks.reduce((sum, check) => sum + (Array.isArray(check.evidence_refs) ? check.evidence_refs.length : 0), 0) : 0,
961
+ coverage_percent: Array.isArray(manifest.checks) && manifest.checks.length > 0
962
+ ? Math.round((manifest.checks.filter((check) => Array.isArray(check.evidence_refs) && check.evidence_refs.length > 0).length / manifest.checks.length) * 100)
963
+ : 100,
964
+ }
965
+ : null
966
+ );
967
+ const policyDecisionsRaw = readJsonFileSafe(path.join(runDir, 'policy-decisions.json'));
968
+ const policyDecisions = Array.isArray(policyDecisionsRaw.data) ? policyDecisionsRaw.data : [];
969
+ const allGraphNodes = activeRun.compiled_graph && activeRun.compiled_graph.nodes && typeof activeRun.compiled_graph.nodes === 'object'
970
+ ? Object.values(activeRun.compiled_graph.nodes)
971
+ : [];
972
+ const mutationNodes = allGraphNodes.filter((node) => Array.isArray(node.mutation_scope) && node.mutation_scope.length > 0);
973
+ const attemptEntries = activeRun.canonical_state && activeRun.canonical_state.attempts && typeof activeRun.canonical_state.attempts === 'object'
974
+ ? activeRun.canonical_state.attempts
975
+ : {};
976
+ const retryExceeded = mutationNodes
977
+ .map((node) => {
978
+ const attempts = Array.isArray(attemptEntries[node.id]) ? attemptEntries[node.id].length : 0;
979
+ const maxRetries = node.policy && typeof node.policy.max_retries === 'number' ? node.policy.max_retries : null;
980
+ return maxRetries != null && attempts > maxRetries + 1 ? { nodeId: node.id, attempts, maxRetries } : null;
981
+ })
982
+ .filter(Boolean);
983
+ const quotaSummary = summarizeQuota(config, activeRun, allGraphNodes, attemptEntries);
984
+ const promotionSummary = summarizePromotion(runDir, activeRun);
985
+ const multiAgent = operational.readRuntimeMultiAgentStatus
986
+ ? operational.readRuntimeMultiAgentStatus(target, activeSession, { runId: activeRun.run_id })
987
+ : null;
988
+ const policyCoverage = summarizePolicyCoverage(runDir, mutationNodes, policyDecisions);
989
+ const residualRiskSummary = risks
990
+ ? {
991
+ total: Array.isArray(risks.risks) ? risks.risks.length : 0,
992
+ highOrCritical: Array.isArray(risks.risks)
993
+ ? risks.risks.filter((risk) => risk.severity === 'high' || risk.severity === 'critical').length
994
+ : 0,
995
+ ledgerPath: path.join(runDir, 'residual-risk-ledger.json'),
996
+ }
997
+ : null;
998
+ const verificationSummary = manifest
999
+ ? {
1000
+ total: Number(manifest.summary && manifest.summary.total || 0),
1001
+ pass: Number(manifest.summary && manifest.summary.pass || 0),
1002
+ fail: Number(manifest.summary && manifest.summary.fail || 0),
1003
+ skip: Number(manifest.summary && manifest.summary.skip || 0),
1004
+ error: Number(manifest.summary && manifest.summary.error || 0),
1005
+ allPassed: Boolean(manifest.summary && manifest.summary.all_passed),
1006
+ profile: manifest.profile || null,
1007
+ manifestPath: path.join(runDir, 'verification-manifest.json'),
1008
+ }
1009
+ : null;
1010
+ const promotionReadiness = summarizePromotionReadiness(
1011
+ verificationSummary,
1012
+ residualRiskSummary,
1013
+ evidenceCoverage,
1014
+ pendingGates,
1015
+ policyCoverage,
1016
+ promotionSummary,
1017
+ quotaSummary
1018
+ );
1019
+ const recoveryState = summarizeRecoveryState(
1020
+ target,
1021
+ activeSession,
1022
+ activeRun,
1023
+ { manifest, residualRisks: risks, evidenceCoverage }
1024
+ );
1025
+ const enterpriseWarnings = [];
1026
+ if (pendingGates.stalePending.length > 0) {
1027
+ enterpriseWarnings.push(`${pendingGates.stalePending.length} gate(s) pendente(s) há mais de 24h.`);
1028
+ }
1029
+ if (mutationNodes.length > 0 && policyDecisions.length === 0) {
1030
+ enterpriseWarnings.push('Há mutações no grafo compilado sem decisões de policy persistidas para a run ativa.');
1031
+ }
1032
+ const overridesWithoutRationale = policyDecisions.filter((decision) => decision.override && !(decision.rationale || decision.reason));
1033
+ if (overridesWithoutRationale.length > 0) {
1034
+ enterpriseWarnings.push(`${overridesWithoutRationale.length} override(s) de policy sem justificativa persistida.`);
1035
+ }
1036
+ if (retryExceeded.length > 0) {
1037
+ enterpriseWarnings.push(`${retryExceeded.length} work item(s) ultrapassaram o budget de retry configurado.`);
1038
+ }
1039
+ if (quotaSummary.exceeded) {
1040
+ enterpriseWarnings.push(`Runtime enterprise excedeu quotas configuradas: ${quotaSummary.violations.join(', ')}.`);
1041
+ }
1042
+ if (auditSummary.critical > 0) {
1043
+ enterpriseWarnings.push(`${auditSummary.critical} entrada(s) críticas no audit trail da run ativa.`);
1044
+ }
1045
+ if (policyCoverage.uncoveredMutations > 0) {
1046
+ enterpriseWarnings.push(`${policyCoverage.uncoveredMutations} mutation scope(s) ainda sem coverage de policy persistida.`);
1047
+ }
1048
+ if (providerCatalog.load_errors && providerCatalog.load_errors.length > 0) {
1049
+ enterpriseWarnings.push(...providerCatalog.load_errors);
1050
+ }
1051
+ if (recoveryState.issues.length > 0) {
1052
+ enterpriseWarnings.push(...recoveryState.issues);
1053
+ }
1054
+
1055
+ return {
1056
+ runtimeMode,
1057
+ fallbackMode: runtimeMode.fallback_mode,
1058
+ verificationSummary,
1059
+ residualRiskSummary,
1060
+ evidenceCoverage,
1061
+ pendingGates,
1062
+ gateQueue: pendingGates,
1063
+ policyDecisionSummary: {
1064
+ total: policyDecisions.length,
1065
+ denied: policyDecisions.filter((decision) => decision.allowed === false).length,
1066
+ gated: policyDecisions.filter((decision) => decision.gate_required === true).length,
1067
+ overridesWithoutRationale: overridesWithoutRationale.length,
1068
+ },
1069
+ policyCoverage,
1070
+ quotaSummary,
1071
+ auditSummary,
1072
+ promotionSummary,
1073
+ promotionReadiness,
1074
+ recoveryState,
1075
+ multiAgent,
1076
+ providerCatalog,
1077
+ enterpriseWarnings,
1078
+ };
1079
+ }
1080
+
618
1081
  /**
619
1082
  * @param {string} dir
620
1083
  * @returns {string[]}
@@ -649,7 +1112,7 @@ function hasOtherManagedInstructionBlocks(filePath) {
649
1112
  .replace(/<!-- oxe-cc:install-begin -->[\s\S]*?<!-- oxe-cc:install-end -->/g, '')
650
1113
  .trim();
651
1114
  if (!withoutOxe) return false;
652
- return /<!--[^>]*(managed|configuration|install-begin|install-end)[^>]*-->/i.test(withoutOxe);
1115
+ return /<!--[^>]*(managed|configuration|install-begin|install-end)[^>]*-->/i.test(withoutOxe);
653
1116
  }
654
1117
 
655
1118
  /**
@@ -1429,6 +1892,7 @@ function buildHealthReport(target) {
1429
1892
  const activeRun = operational.readRunState(target, activeSession);
1430
1893
  const eventsSummary = operational.summarizeEvents(operational.readEvents(target, activeSession));
1431
1894
  const memoryLayers = operational.buildMemoryLayers(target, activeSession);
1895
+ const enterpriseRuntime = summarizeEnterpriseRuntime(target, activeRun, activeSession, config);
1432
1896
  const azureActive = azure.isAzureContextEnabled(target, config);
1433
1897
  const azureReport = azureActive
1434
1898
  ? azure.azureDoctor(target, config, {
@@ -1599,6 +2063,7 @@ function buildHealthReport(target) {
1599
2063
  phaseWarn.length +
1600
2064
  runtimeWarn.length +
1601
2065
  reviewWarn.length +
2066
+ enterpriseRuntime.enterpriseWarnings.length +
1602
2067
  specWarn.length +
1603
2068
  planWarn.length +
1604
2069
  capabilityWarn.length +
@@ -1644,6 +2109,23 @@ function buildHealthReport(target) {
1644
2109
  activeRun,
1645
2110
  eventsSummary,
1646
2111
  memoryLayers,
2112
+ runtimeMode: enterpriseRuntime.runtimeMode,
2113
+ fallbackMode: enterpriseRuntime.fallbackMode,
2114
+ verificationSummary: enterpriseRuntime.verificationSummary,
2115
+ residualRiskSummary: enterpriseRuntime.residualRiskSummary,
2116
+ evidenceCoverage: enterpriseRuntime.evidenceCoverage,
2117
+ pendingGates: enterpriseRuntime.pendingGates,
2118
+ gateQueue: enterpriseRuntime.gateQueue,
2119
+ policyDecisionSummary: enterpriseRuntime.policyDecisionSummary,
2120
+ policyCoverage: enterpriseRuntime.policyCoverage,
2121
+ quotaSummary: enterpriseRuntime.quotaSummary,
2122
+ auditSummary: enterpriseRuntime.auditSummary,
2123
+ promotionSummary: enterpriseRuntime.promotionSummary,
2124
+ promotionReadiness: enterpriseRuntime.promotionReadiness,
2125
+ recoveryState: enterpriseRuntime.recoveryState,
2126
+ multiAgent: enterpriseRuntime.multiAgent,
2127
+ providerCatalog: enterpriseRuntime.providerCatalog,
2128
+ enterpriseWarn: enterpriseRuntime.enterpriseWarnings,
1647
2129
  azureActive,
1648
2130
  azure: azureReport
1649
2131
  ? {
@@ -119,6 +119,9 @@ function getWorkflowContract(slug) {
119
119
  blocking_conditions: uniqueStrings(workflow.blocking_conditions || modeDefaults.blocking_conditions || []),
120
120
  output_sections: uniqueStrings(workflow.output_sections || modeDefaults.output_sections || []),
121
121
  extraction_intent: String(workflow.extraction_intent || 'status_read'),
122
+ runtime_default_commands: Array.isArray(workflow.runtime_default_commands) ? workflow.runtime_default_commands.slice() : [],
123
+ runtime_primary_artifacts: Array.isArray(workflow.runtime_primary_artifacts) ? workflow.runtime_primary_artifacts.slice() : [],
124
+ runtime_fallback_note: workflow.runtime_fallback_note ? String(workflow.runtime_fallback_note) : '',
122
125
  auditor_artifacts: Array.isArray(workflow.auditor_artifacts) ? workflow.auditor_artifacts.slice() : [],
123
126
  auditor_excluded: Array.isArray(workflow.auditor_excluded) ? workflow.auditor_excluded.slice() : [],
124
127
  reference: MODE_REFERENCES[mode] || null,
@@ -291,6 +294,15 @@ function buildReasoningContractBlock(meta, options = {}) {
291
294
  if (blocking.length) {
292
295
  lines.push(`- **Bloqueios formais:** ${blocking.join(' · ')}`);
293
296
  }
297
+ if (Array.isArray(contract.runtime_default_commands) && contract.runtime_default_commands.length) {
298
+ lines.push(`- **Caminho runtime padrão:** ${contract.runtime_default_commands.map((cmd) => `\`${cmd}\``).join(' → ')}`);
299
+ }
300
+ if (Array.isArray(contract.runtime_primary_artifacts) && contract.runtime_primary_artifacts.length) {
301
+ lines.push(`- **Artefatos canónicos primários:** ${contract.runtime_primary_artifacts.map((artifact) => `\`${artifact}\``).join(' · ')}`);
302
+ }
303
+ if (contract.runtime_fallback_note) {
304
+ lines.push(`- **Fallback runtime:** ${contract.runtime_fallback_note}`);
305
+ }
294
306
  if (includeReference && MODE_REFERENCES[mode]) {
295
307
  lines.push(`- **Referência canónica:** \`${MODE_REFERENCES[mode]}\``);
296
308
  }