oxe-cc 1.5.1 → 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 (38) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +27 -0
  3. package/README.md +16 -14
  4. package/bin/lib/oxe-dashboard.cjs +21 -5
  5. package/bin/lib/oxe-project-health.cjs +120 -42
  6. package/bin/lib/oxe-release.cjs +76 -4
  7. package/bin/oxe-cc.js +68 -39
  8. package/docs/RELEASE-READINESS.md +8 -0
  9. package/docs/RUNTIME-SMOKE-MATRIX.md +9 -2
  10. package/lib/sdk/index.cjs +10 -5
  11. package/lib/sdk/index.d.ts +21 -10
  12. package/oxe/templates/CONFIG.md +3 -3
  13. package/oxe/templates/EXECUTION-RUNTIME.template.md +1 -1
  14. package/oxe/templates/FIXTURE-PACK.template.json +34 -34
  15. package/oxe/templates/FIXTURE-PACK.template.md +21 -21
  16. package/oxe/templates/IMPLEMENTATION-PACK.template.json +52 -52
  17. package/oxe/templates/IMPLEMENTATION-PACK.template.md +36 -36
  18. package/oxe/templates/INVESTIGATION.template.md +38 -38
  19. package/oxe/templates/PLAN.template.md +46 -46
  20. package/oxe/templates/REFERENCE-ANCHORS.template.md +24 -24
  21. package/oxe/templates/RESEARCH.template.md +11 -11
  22. package/oxe/templates/SPEC.template.md +6 -6
  23. package/oxe/templates/SUMMARY.template.md +20 -20
  24. package/oxe/templates/config.template.json +1 -1
  25. package/oxe/workflows/execute.md +36 -36
  26. package/oxe/workflows/milestone.md +12 -12
  27. package/oxe/workflows/next.md +1 -1
  28. package/oxe/workflows/plan.md +132 -132
  29. package/oxe/workflows/references/adaptive-discovery.md +27 -27
  30. package/oxe/workflows/references/flow-robustness-contract.md +80 -80
  31. package/oxe/workflows/references/session-path-resolution.md +71 -71
  32. package/oxe/workflows/references/workflow-runtime-contracts.json +127 -127
  33. package/oxe/workflows/verify.md +4 -4
  34. package/oxe/workflows/workstream.md +16 -16
  35. package/package.json +1 -1
  36. package/packages/runtime/package.json +1 -1
  37. package/vscode-extension/oxe-agents-1.6.0.vsix +0 -0
  38. package/vscode-extension/package.json +1 -1
package/AGENTS.md CHANGED
@@ -99,4 +99,4 @@ Os wrappers por runtime podem carregar metadata cognitiva (`oxe_reasoning_mode`,
99
99
 
100
100
  Para `execute` e `verify`, o comportamento atual esperado é **runtime-first**: se `oxe-cc runtime` estiver disponível, preferir `runtime compile/project/verify/gates` e tratar markdown como projeção derivada; se o runtime não puder ser executado, declarar `fallback legado` explicitamente.
101
101
 
102
- Para publicação, o gate local esperado passa por `oxe-cc doctor --release --write-manifest`. Essa verificação deve falhar se houver drift de versão, topo inválido no `CHANGELOG`, wrapper dirty após sync, runtime não compilado ou ausência dos relatórios `.oxe/release/*.json` exigidos pela release.
102
+ Para publicação, o gate local esperado passa por `oxe-cc doctor --release --write-manifest`. Essa verificação deve falhar se houver drift de versão, topo inválido no `CHANGELOG`, ausência da árvore canónica `oxe/`, `workflow-runtime-contracts.json` inválido, wrapper dirty após sync, runtime não compilado ou ausência dos relatórios `.oxe/release/*.json` exigidos pela release.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,33 @@ Todas as versões seguem [Semantic Versioning](https://semver.org/). As mudança
4
4
 
5
5
  ---
6
6
 
7
+ ## [1.6.0] — 2026-04-23
8
+
9
+ ### Product Reconciliation
10
+
11
+ - a árvore canónica `oxe/` foi restaurada como source of truth da release, incluindo `oxe/workflows/`, `oxe/workflows/references/`, `oxe/templates/` e `commands/oxe/` como superfície derivada sincronizada
12
+ - `workflow-runtime-contracts.json` voltou a ser contrato obrigatório da release; `sync-runtime-metadata` agora falha fechado quando a fonte canónica ou o registry semântico estão ausentes/inválidos
13
+ - `doctor --release` passou a bloquear explicitamente por topologia canónica ausente, contrato semântico inválido e drift entre workflows e wrappers
14
+
15
+ ### Package-vs-Workspace Health
16
+
17
+ - `status`, `status --full`, dashboard e SDK agora distinguem `workspaceMode: product_package` de `workspaceMode: oxe_project`
18
+ - no repositório do pacote, o próximo passo deixa de cair em `plan/replan` por ruído de manutenção e passa a apontar para `doctor --release --write-manifest`
19
+ - `status --json` passou a expor `workspaceMode` e `releaseReadiness` como contrato estável da linha `1.6.0`
20
+
21
+ ### Multi-runtime Edge Hardening
22
+
23
+ - a smoke matrix de release passou a validar a instalação do Codex de forma completa: prompts em `.codex/prompts/` e skills em `.agents/skills/oxe/`
24
+ - documentação de instalação foi ajustada para separar `--local` (layout do repo) de `--ide-local` (escopo da integração), eliminando a ambiguidade operacional em runtimes como Codex
25
+
26
+ ### Validation
27
+
28
+ - `node --test tests/oxe-project-health.test.cjs`
29
+ - `node --test tests/oxe-cli-edge.test.cjs`
30
+ - `npm test`
31
+ - `npm run scan:assets`
32
+ - `npm run release:doctor`
33
+
7
34
  ## [1.5.1] — 2026-04-22
8
35
 
9
36
  ### Rational Execution Readiness
package/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
  [![npm](https://img.shields.io/npm/v/oxe-cc.svg?style=flat-square)](https://www.npmjs.com/package/oxe-cc)
8
8
  [![license](https://img.shields.io/npm/l/oxe-cc.svg?style=flat-square)](LICENSE)
9
9
 
10
- **Versão:** `1.5.1` · [package.json](package.json)
10
+ **Versão:** `1.6.0` · [package.json](package.json)
11
11
 
12
12
  **Framework OXE — Orchestrated eXperience Engineering**
13
13
 
@@ -52,7 +52,7 @@ O OXE agora distingue cinco famílias de raciocínio:
52
52
  - `review` — findings primeiro, severidade, evidência e risco residual
53
53
  - `status` — leitura curta do estado, recomendação única e motivo
54
54
 
55
- Essas regras vivem no núcleo canónico em `oxe/workflows/references/reasoning-*.md`, sobem para os workflows em `oxe/workflows/` e são renderizadas para cada runtime em `.github/prompts/`, `.cursor/commands/`, `commands/oxe/`, `.codex/prompts/` e skills multiagente.
55
+ Essas regras vivem no núcleo canónico em `oxe/workflows/references/reasoning-*.md`, sobem para os workflows em `oxe/workflows/` e são renderizadas para cada runtime em `.github/prompts/`, `.cursor/commands/`, `commands/oxe/`, `.codex/prompts/` e skills multiagente. Nesta linha, `oxe/workflows/**` e `workflow-runtime-contracts.json` voltam a ser o contrato obrigatório da release; superfícies geradas permanecem derivadas e sincronizadas.
56
56
 
57
57
  ---
58
58
 
@@ -522,14 +522,16 @@ npx oxe-cc@latest
522
522
  | Flag | Efeito |
523
523
  |------|--------|
524
524
  | `--cursor` / `--copilot` | Só uma das stacks da IDE |
525
- | `--copilot-cli` | Skills globais do Copilot CLI em `~/.copilot/skills/` |
526
- | `--all-agents` | Cursor + Copilot + Claude + OpenCode + Gemini + Codex + Windsurf + Antigravity |
527
- | `--global` | Layout clássico: `oxe/` na raiz + `.oxe/` |
528
- | `--local` | Layout mínimo: só `.oxe/` (padrão) |
529
- | `--force` / `-f` | Sobrescreve arquivos existentes (use para atualizar) |
530
- | `--dry-run` | Lista ações sem escrever |
531
- | `--oxe-only` | workflows em `.oxe/`, sem integrações IDE |
532
- | `--no-global-cli` / `-l` | Não instala `oxe-cc` globalmente (útil em CI) |
525
+ | `--copilot-cli` | Skills globais do Copilot CLI em `~/.copilot/skills/` |
526
+ | `--all-agents` | Cursor + Copilot + Claude + OpenCode + Gemini + Codex + Windsurf + Antigravity |
527
+ | `--global` | Layout clássico: `oxe/` na raiz + `.oxe/` |
528
+ | `--local` | Layout do repositório: mínimo, só `.oxe/` (padrão). Não controla onde a integração da IDE é instalada. |
529
+ | `--ide-local` | Instala a integração no próprio repositório (`.cursor/`, `.github/`, `.claude/`, `.codex/` etc.) |
530
+ | `--ide-global` | Instala a integração no HOME do utilizador quando o runtime suportar esse escopo |
531
+ | `--force` / `-f` | Sobrescreve arquivos existentes (use para atualizar) |
532
+ | `--dry-run` | Lista ações sem escrever |
533
+ | `--oxe-only` | Só workflows em `.oxe/`, sem integrações IDE |
534
+ | `--no-global-cli` / `-l` | Não instala `oxe-cc` globalmente (útil em CI) |
533
535
  | `OXE_NO_PROMPT=1` | Modo não-interativo (CI) |
534
536
 
535
537
  </details>
@@ -568,10 +570,10 @@ node bin/oxe-cc.js --help
568
570
  |---------|-----------|
569
571
  | `oxe-cc` / `oxe-cc install` | Instala workflows e integrações |
570
572
  | `oxe-cc doctor` | Diagnóstico completo: Node, workflows, config, bootstrap `.oxe/`, sessão ativa, autoavaliação do plano, saúde lógica (`healthy` \| `warning` \| `broken`), drift semântico multi-runtime e workflows sem contrato no registry |
571
- | `oxe-cc doctor --release --write-manifest` | Gate de publicação: valida versões, topo do `CHANGELOG`, runtime compilado, wrapper sync e relatórios obrigatórios; persiste `release-manifest.json` |
572
- | `oxe-cc status` | Próximo passo sugerido + saúde lógica do fluxo |
573
- | `oxe-cc status --full` | Coverage matrix + readiness gate + active run no terminal (ANSI) |
574
- | `oxe-cc status --json` | Mesmo, em JSON (schema v5), com `healthStatus`, `activeSession`, `planSelfEvaluation`, `contextPacks`, `contextQuality`, `semanticsDrift`, `verificationSummary`, `residualRiskSummary`, `evidenceCoverage`, `pendingGates`, `policyDecisionSummary`, `quotaSummary`, `auditSummary`, `promotionSummary`, `runtimeMode`, `fallbackMode`, `gateQueue`, `policyCoverage`, `promotionReadiness`, `recoveryState`, `multiAgent` e `providerCatalog` |
573
+ | `oxe-cc doctor --release --write-manifest` | Gate de publicação: valida árvore canónica `oxe/`, `workflow-runtime-contracts.json`, versões, topo do `CHANGELOG`, runtime compilado, wrapper sync e relatórios obrigatórios; persiste `release-manifest.json` |
574
+ | `oxe-cc status` | Próximo passo sugerido + saúde lógica do fluxo |
575
+ | `oxe-cc status --full` | Coverage matrix + readiness gate + active run no terminal (ANSI); em repositório do pacote, troca para release readiness em vez de plan readiness |
576
+ | `oxe-cc status --json` | Mesmo, em JSON (schema v5), com `workspaceMode`, `releaseReadiness`, `healthStatus`, `activeSession`, `planSelfEvaluation`, `contextPacks`, `contextQuality`, `semanticsDrift`, `verificationSummary`, `residualRiskSummary`, `evidenceCoverage`, `pendingGates`, `policyDecisionSummary`, `quotaSummary`, `auditSummary`, `promotionSummary`, `runtimeMode`, `fallbackMode`, `gateQueue`, `policyCoverage`, `promotionReadiness`, `recoveryState`, `multiAgent` e `providerCatalog` |
575
577
  | `oxe-cc context build [--workflow <slug>] [--tier <minimal\|standard\|full>]` | Gera context pack(s) em `.oxe/context/packs/` — seleção determinística de artefatos por contrato de workflow |
576
578
  | `oxe-cc context inspect [--workflow <slug>]` | Inspeciona um context pack existente ou resolve sob demanda (sem escrita); útil para diagnóstico antes de iniciar um passo |
577
579
  | `oxe-cc update` | Atualiza workflows para a versão mais recente |
@@ -162,6 +162,20 @@ function confidenceBand(confidence, threshold) {
162
162
  }
163
163
 
164
164
  function computeReadiness(ctx, threshold) {
165
+ if (ctx.workspaceMode === 'product_package' && ctx.releaseReadiness) {
166
+ const blockers = Array.isArray(ctx.releaseReadiness.blockers) ? ctx.releaseReadiness.blockers.slice() : [];
167
+ const warnings = Array.isArray(ctx.releaseReadiness.warnings) ? ctx.releaseReadiness.warnings.slice() : [];
168
+ return {
169
+ go: Boolean(ctx.releaseReadiness.ok),
170
+ decision: ctx.releaseReadiness.ok ? 'go' : 'no-go',
171
+ threshold: health.normalizePlanConfidenceThreshold(threshold),
172
+ confidence: ctx.plan && ctx.plan.selfEvaluation ? ctx.plan.selfEvaluation.confidence : null,
173
+ confidenceBand: confidenceBand(ctx.plan && ctx.plan.selfEvaluation ? ctx.plan.selfEvaluation.confidence : null, threshold),
174
+ checkpointPending: false,
175
+ blockers,
176
+ warnings,
177
+ };
178
+ }
165
179
  const normalizedThreshold = health.normalizePlanConfidenceThreshold(threshold);
166
180
  const blockers = [];
167
181
  const warnings = [...ctx.diagnostics.reviewWarnings, ...ctx.diagnostics.runtimeWarnings, ...ctx.diagnostics.planWarnings];
@@ -349,7 +363,7 @@ function loadDashboardContext(projectRoot, opts = {}) {
349
363
  const activeSession = opts.activeSession === undefined ? health.parseActiveSession(stateText) : opts.activeSession;
350
364
  const p = reviewPaths(projectRoot, activeSession || null);
351
365
  const rootScoped = reviewPaths(projectRoot, null);
352
- const report = health.buildHealthReport(projectRoot);
366
+ const report = health.buildHealthReport(projectRoot);
353
367
  const specText = readScopedText(p.spec, rootScoped.spec);
354
368
  const planText = readScopedText(p.plan, rootScoped.plan);
355
369
  const verifyText = readScopedText(p.verify, rootScoped.verify);
@@ -373,9 +387,11 @@ function loadDashboardContext(projectRoot, opts = {}) {
373
387
  const sessions = parseSessionsIndex(sessionsRaw);
374
388
  const sessionPath = activeSession ? path.join(projectRoot, '.oxe', activeSession, 'SESSION.md') : null;
375
389
  const sessionRaw = sessionPath ? readTextIfExists(sessionPath) || '' : '';
376
- const ctx = {
377
- projectRoot: path.resolve(projectRoot),
378
- activeSession: activeSession || null,
390
+ const ctx = {
391
+ projectRoot: path.resolve(projectRoot),
392
+ workspaceMode: report.workspaceMode || 'oxe_project',
393
+ releaseReadiness: report.releaseReadiness || null,
394
+ activeSession: activeSession || null,
379
395
  phase: report.phase || health.parseStatePhase(stateText),
380
396
  healthStatus: report.healthStatus,
381
397
  nextStep: report.next,
@@ -388,7 +404,7 @@ function loadDashboardContext(projectRoot, opts = {}) {
388
404
  activeRun: activeRunState,
389
405
  runtimeCanonical: activeRunState && activeRunState.canonical_state ? activeRunState.canonical_state : null,
390
406
  compiledGraph: activeRunState && activeRunState.compiled_graph ? activeRunState.compiled_graph : null,
391
- enterprise: {
407
+ enterprise: {
392
408
  runtimeMode: report.runtimeMode || null,
393
409
  fallbackMode: report.fallbackMode || null,
394
410
  verificationSummary: report.verificationSummary || null,
@@ -746,6 +746,50 @@ function resolvedReadableOxePaths(target, activeSession) {
746
746
  summary: preferScoped('summary'),
747
747
  };
748
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
+ }
749
793
 
750
794
  /**
751
795
  * @param {string} target
@@ -1841,16 +1885,19 @@ function planReviewWarnings(stateText, p) {
1841
1885
  * @param {{ discuss_before_plan?: boolean }} cfg
1842
1886
  */
1843
1887
  function suggestNextStep(target, cfg = {}) {
1844
- const base = oxePaths(target);
1845
- const stateText = fs.existsSync(base.state) ? fs.readFileSync(base.state, 'utf8') : '';
1846
- const p = resolvedReadableOxePaths(target, parseActiveSession(stateText));
1847
- const discussBefore = Boolean(cfg.discuss_before_plan);
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);
1848
1893
  const threshold = normalizePlanConfidenceThreshold(cfg.plan_confidence_threshold);
1849
- const has = (/** @type {string} */ f) => fs.existsSync(f);
1850
- const mapsComplete = EXPECTED_CODEBASE_MAPS.every((f) => has(path.join(p.codebase, f)));
1851
- const azureActive = azure.isAzureContextEnabled(target, cfg);
1852
-
1853
- if (!has(p.oxe) || !has(p.state)) {
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)) {
1854
1901
  return {
1855
1902
  step: 'scan',
1856
1903
  cursorCmd: '/oxe-scan',
@@ -1858,10 +1905,27 @@ function suggestNextStep(target, cfg = {}) {
1858
1905
  artifacts: ['.oxe/'],
1859
1906
  };
1860
1907
  }
1861
-
1862
- const phase = parseStatePhase(stateText);
1863
-
1864
- 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)) {
1865
1929
  return {
1866
1930
  step: 'scan',
1867
1931
  cursorCmd: '/oxe-scan',
@@ -2003,9 +2067,7 @@ function suggestNextStep(target, cfg = {}) {
2003
2067
  artifacts: ['.oxe/PLAN.md', '.oxe/PLAN-REVIEW.md', '.oxe/STATE.md'],
2004
2068
  };
2005
2069
  }
2006
-
2007
- const activeRun = operational.readRunState(target, parseActiveSession(stateText));
2008
- if (activeRun && activeRun.status === 'waiting_approval') {
2070
+ if (activeRun && activeRun.status === 'waiting_approval') {
2009
2071
  return {
2010
2072
  step: 'dashboard',
2011
2073
  cursorCmd: '/oxe-dashboard',
@@ -2102,7 +2164,7 @@ function suggestNextStep(target, cfg = {}) {
2102
2164
  /**
2103
2165
  * @param {string} target
2104
2166
  */
2105
- function buildHealthReport(target) {
2167
+ function buildHealthReport(target) {
2106
2168
  const contextEngine = require('./oxe-context-engine.cjs');
2107
2169
  const runtimeSemantics = require('./oxe-runtime-semantics.cjs');
2108
2170
  const { config, path: cfgPath, parseError } = loadOxeConfigMerged(target);
@@ -2116,10 +2178,11 @@ function buildHealthReport(target) {
2116
2178
  stateText = '';
2117
2179
  }
2118
2180
  }
2119
- const activeSession = parseActiveSession(stateText);
2181
+ const activeSession = parseActiveSession(stateText);
2120
2182
  const p = resolvedReadableOxePaths(target, activeSession);
2121
- const phase = parseStatePhase(stateText);
2122
- const scanDate = parseLastScanDate(stateText);
2183
+ const phase = parseStatePhase(stateText);
2184
+ const workspaceInfo = detectWorkspaceMode(target);
2185
+ const scanDate = parseLastScanDate(stateText);
2123
2186
  const stale = isStaleScan(scanDate, Number(config.scan_max_age_days) || 0);
2124
2187
  const compactDate = parseLastCompactDate(stateText);
2125
2188
  const staleCompact = isStaleScan(compactDate, Number(config.compact_max_age_days) || 0);
@@ -2134,6 +2197,7 @@ function buildHealthReport(target) {
2134
2197
  const capabilityWarn = capabilityWarnings(p);
2135
2198
  const investigationWarn = investigationWarnings(p);
2136
2199
  const parsedPlanSelfEvaluation = parsePlanSelfEvaluation(p.plan);
2200
+ const activeRun = operational.readRunState(target, activeSession);
2137
2201
  const executionRationality = rationality.buildExecutionRationality({
2138
2202
  plan: p.plan,
2139
2203
  planAgents: p.planAgents,
@@ -2143,18 +2207,25 @@ function buildHealthReport(target) {
2143
2207
  fixturePackJson: p.fixturePackJson,
2144
2208
  fixturePackMd: p.fixturePackMd,
2145
2209
  });
2146
- const planWarn = [
2210
+ const suppressExecutionWorkspaceGates = shouldSuppressExecutionWorkspaceGates(
2211
+ workspaceInfo.workspaceMode,
2212
+ phase,
2213
+ activeSession,
2214
+ activeRun
2215
+ );
2216
+ const executionPlanWarn = [
2147
2217
  ...planWaveWarningsFixed(p.plan, Number(config.plan_max_tasks_per_wave) || 0),
2148
2218
  ...planTaskAceiteWarnings(p.plan),
2149
2219
  ...planSelfEvaluationWarningsFromInfo(parsedPlanSelfEvaluation, threshold),
2150
2220
  ...executionRationalityWarningsFromSummary(executionRationality),
2151
2221
  ...planAgentsWarnings(target),
2152
2222
  ];
2223
+ const planWarn = suppressExecutionWorkspaceGates ? [] : executionPlanWarn;
2153
2224
  const sessionWarn = sessionWarnings(target, activeSession);
2154
- const installWarn = installationCompletenessWarnings(target);
2155
- const copilot = copilotIntegrationReport(target);
2156
- const copilotWarn = copilot.warnings;
2157
- const reviewWarn = planReviewWarnings(stateText, p);
2225
+ const installWarn = installationCompletenessWarnings(target);
2226
+ const copilot = copilotIntegrationReport(target);
2227
+ const copilotWarn = copilot.warnings;
2228
+ const reviewWarn = suppressExecutionWorkspaceGates ? [] : planReviewWarnings(stateText, p);
2158
2229
  const planSelfEvaluation = {
2159
2230
  ...parsedPlanSelfEvaluation,
2160
2231
  best_plan_current: parsedPlanSelfEvaluation.bestPlan === 'sim'
@@ -2165,7 +2236,9 @@ function buildHealthReport(target) {
2165
2236
  threshold,
2166
2237
  executable: hasExecutablePlanSelfEvaluation(parsedPlanSelfEvaluation, threshold),
2167
2238
  };
2168
- const activeRun = operational.readRunState(target, activeSession);
2239
+ const releaseReadiness = workspaceInfo.workspaceMode === 'product_package'
2240
+ ? require('./oxe-release.cjs').inspectReleaseReadiness(target, { packageRoot: target })
2241
+ : null;
2169
2242
  const eventsSummary = operational.summarizeEvents(operational.readEvents(target, activeSession));
2170
2243
  const memoryLayers = operational.buildMemoryLayers(target, activeSession);
2171
2244
  const enterpriseRuntime = summarizeEnterpriseRuntime(target, activeRun, activeSession, config);
@@ -2312,7 +2385,7 @@ function buildHealthReport(target) {
2312
2385
  if (semanticsAudit.mismatches.length) {
2313
2386
  semanticsWarn.push(`${semanticsAudit.mismatches.length} wrapper(s) com drift semântico detectado.`);
2314
2387
  }
2315
- const semanticsDrift = {
2388
+ const semanticsDrift = {
2316
2389
  ok: semanticsWarn.length === 0 && semanticsAudit.ok,
2317
2390
  contractVersion: runtimeSemantics.CONTRACT_VERSION,
2318
2391
  manifestPath: base.runtimeSemanticsManifest,
@@ -2334,15 +2407,16 @@ function buildHealthReport(target) {
2334
2407
  ),
2335
2408
  },
2336
2409
  };
2337
- const hardFailure = Boolean(parseError) || sessionWarn.some((w) => /não existe|sem SESSION\.md/i.test(w));
2338
- const warningCount =
2339
- phaseWarn.length +
2340
- runtimeWarn.length +
2341
- reviewWarn.length +
2342
- enterpriseRuntime.enterpriseWarnings.length +
2343
- specWarn.length +
2344
- planWarn.length +
2345
- 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 +
2346
2420
  investigationWarn.length +
2347
2421
  sessionWarn.length +
2348
2422
  installWarn.length +
@@ -2353,8 +2427,9 @@ function buildHealthReport(target) {
2353
2427
  (sumWarn ? 1 : 0);
2354
2428
  const healthStatus = hardFailure ? 'broken' : warningCount > 0 ? 'warning' : 'healthy';
2355
2429
 
2356
- return {
2357
- configPath: cfgPath,
2430
+ return {
2431
+ workspaceMode: workspaceInfo.workspaceMode,
2432
+ configPath: cfgPath,
2358
2433
  configParseError: parseError,
2359
2434
  unknownConfigKeys: shape.unknownKeys,
2360
2435
  typeErrors: shape.typeErrors,
@@ -2427,8 +2502,9 @@ function buildHealthReport(target) {
2427
2502
  : null,
2428
2503
  contextPacks,
2429
2504
  contextQuality,
2430
- semanticsDrift,
2431
- packFreshness,
2505
+ semanticsDrift,
2506
+ releaseReadiness,
2507
+ packFreshness,
2432
2508
  activeSummaryRefs,
2433
2509
  healthStatus,
2434
2510
  next,
@@ -2474,8 +2550,10 @@ module.exports = {
2474
2550
  phaseCoherenceWarnings,
2475
2551
  verifyGapsWithoutSummaryWarning,
2476
2552
  specSectionWarnings,
2477
- planWaveWarningsFixed,
2478
- planTaskAceiteWarnings,
2553
+ planWaveWarningsFixed,
2554
+ planTaskAceiteWarnings,
2555
+ detectWorkspaceMode,
2556
+ shouldSuppressExecutionWorkspaceGates,
2479
2557
  suggestNextStep,
2480
2558
  buildHealthReport,
2481
2559
  buildExecutionRationality: rationality.buildExecutionRationality,
@@ -5,6 +5,7 @@ const path = require('path');
5
5
  const { spawnSync } = require('child_process');
6
6
 
7
7
  const oxeManifest = require('./oxe-manifest.cjs');
8
+ const runtimeSemantics = require('./oxe-runtime-semantics.cjs');
8
9
 
9
10
  const REQUIRED_RUNTIMES = [
10
11
  'cursor',
@@ -269,6 +270,7 @@ function loadRuntimeSmokeReport(projectRoot) {
269
270
  && item.oxe_present
270
271
  && item.workflow_resolution_ok
271
272
  && item.wrapper_drift_ok !== false
273
+ && item.extra_checks_ok !== false
272
274
  && item.uninstall_ok
273
275
  );
274
276
  });
@@ -301,11 +303,42 @@ function readVersionSnapshot(projectRoot) {
301
303
  };
302
304
  }
303
305
 
306
+ function inspectCanonicalSource(projectRoot) {
307
+ const workflowsDir = path.join(projectRoot, 'oxe', 'workflows');
308
+ const referencesDir = path.join(workflowsDir, 'references');
309
+ const contractsPath = path.join(referencesDir, 'workflow-runtime-contracts.json');
310
+ const commandsDir = path.join(projectRoot, 'commands', 'oxe');
311
+ const workflowFiles = fs.existsSync(workflowsDir)
312
+ ? fs.readdirSync(workflowsDir).filter((name) => name.endsWith('.md'))
313
+ : [];
314
+ const registryIssues = runtimeSemantics.validateWorkflowContractsRegistry();
315
+ return {
316
+ workflowsDir,
317
+ referencesDir,
318
+ contractsPath,
319
+ commandsDir,
320
+ workflowsPresent: fs.existsSync(workflowsDir),
321
+ referencesPresent: fs.existsSync(referencesDir),
322
+ commandsPresent: fs.existsSync(commandsDir),
323
+ contractsPresent: fs.existsSync(contractsPath),
324
+ workflowCount: workflowFiles.length,
325
+ contractVersion: runtimeSemantics.CONTRACT_VERSION,
326
+ registryIssues,
327
+ ok: fs.existsSync(workflowsDir)
328
+ && fs.existsSync(referencesDir)
329
+ && fs.existsSync(commandsDir)
330
+ && fs.existsSync(contractsPath)
331
+ && workflowFiles.length > 0
332
+ && registryIssues.length === 0,
333
+ };
334
+ }
335
+
304
336
  function buildReleaseManifest(projectRoot, options = {}) {
305
337
  const packageRoot = path.resolve(options.packageRoot || projectRoot);
306
338
  const paths = releasePaths(projectRoot);
307
339
  const versions = readVersionSnapshot(projectRoot);
308
340
  const runtimeEntry = path.join(packageRoot, 'lib', 'runtime', 'index.js');
341
+ const canonicalSource = inspectCanonicalSource(projectRoot);
309
342
  const wrapperSync = options.skipWrapperSync ? {
310
343
  before: collectWrapperHashes(projectRoot),
311
344
  after: collectWrapperHashes(projectRoot),
@@ -313,6 +346,7 @@ function buildReleaseManifest(projectRoot, options = {}) {
313
346
  mismatches: [],
314
347
  ok: true,
315
348
  } : syncWrappers(projectRoot, packageRoot);
349
+ const semanticsAudit = runtimeSemantics.auditRuntimeTargets(projectRoot);
316
350
  const smoke = loadRuntimeSmokeReport(projectRoot);
317
351
  const recovery = loadRecoveryFixtureReport(projectRoot);
318
352
  const multiAgent = loadMultiAgentSoakReport(projectRoot);
@@ -332,6 +366,12 @@ function buildReleaseManifest(projectRoot, options = {}) {
332
366
  path: runtimeEntry,
333
367
  ok: fs.existsSync(runtimeEntry),
334
368
  },
369
+ canonical_source: canonicalSource,
370
+ semantics: {
371
+ contractVersion: runtimeSemantics.CONTRACT_VERSION,
372
+ registryPath: runtimeSemantics.CONTRACTS_PATH,
373
+ audit: semanticsAudit,
374
+ },
335
375
  wrappers: {
336
376
  hash_before_sync: wrapperSync.before,
337
377
  hash_after_sync: wrapperSync.after,
@@ -354,8 +394,8 @@ function buildReleaseManifest(projectRoot, options = {}) {
354
394
  return manifest;
355
395
  }
356
396
 
357
- function checkReleaseConsistency(projectRoot, options = {}) {
358
- const manifest = buildReleaseManifest(projectRoot, options);
397
+ function evaluateReleaseManifest(manifest, options = {}) {
398
+ const enforceSync = options.enforceSync !== false;
359
399
  const blockers = [];
360
400
  const warnings = [];
361
401
  const versions = manifest.versions;
@@ -378,10 +418,22 @@ function checkReleaseConsistency(projectRoot, options = {}) {
378
418
  if (!versions.changelog.date) blockers.push('CHANGELOG.md topo sem data');
379
419
  if (!versions.changelog.hasHighlights) blockers.push('CHANGELOG.md topo sem highlights');
380
420
  }
421
+ const canonical = manifest.canonical_source || {};
422
+ if (!canonical.ok) {
423
+ blockers.push('árvore canónica ausente ou inválida');
424
+ }
425
+ if (!canonical.workflowsPresent) blockers.push('oxe/workflows ausente');
426
+ if (!canonical.referencesPresent) blockers.push('oxe/workflows/references ausente');
427
+ if (!canonical.commandsPresent) blockers.push('commands/oxe ausente');
428
+ if (!canonical.contractsPresent) blockers.push('workflow-runtime-contracts.json ausente');
429
+ if (canonical.workflowCount === 0) blockers.push('oxe/workflows sem workflows canónicos');
430
+ if (Array.isArray(canonical.registryIssues) && canonical.registryIssues.length) {
431
+ blockers.push('workflow-runtime-contracts.json inválido');
432
+ }
381
433
  if (!manifest.runtime_compiled.ok) {
382
434
  blockers.push('runtime não compilado (lib/runtime/index.js ausente)');
383
435
  }
384
- if (!manifest.wrappers.sync.ok) {
436
+ if (enforceSync && !manifest.wrappers.sync.ok) {
385
437
  if (manifest.wrappers.sync.scripts.some((entry) => !entry.ok)) {
386
438
  blockers.push('sync de wrappers falhou');
387
439
  }
@@ -389,6 +441,10 @@ function checkReleaseConsistency(projectRoot, options = {}) {
389
441
  blockers.push('wrappers ficam dirty após sync-runtime-metadata/sync:cursor');
390
442
  }
391
443
  }
444
+ const semanticsAudit = manifest.semantics && manifest.semantics.audit;
445
+ if (!semanticsAudit || semanticsAudit.ok !== true) {
446
+ blockers.push('drift semântico entre workflows e wrappers');
447
+ }
392
448
  if (!manifest.reports.runtime_smoke.present || !manifest.reports.runtime_smoke.ok) {
393
449
  blockers.push('runtime smoke matrix incompleta ou com falhas');
394
450
  }
@@ -406,10 +462,23 @@ function checkReleaseConsistency(projectRoot, options = {}) {
406
462
  blockers,
407
463
  warnings,
408
464
  manifest,
409
- manifestPath: releasePaths(projectRoot).manifest,
465
+ manifestPath: releasePaths(manifest.project_root).manifest,
410
466
  };
411
467
  }
412
468
 
469
+ function checkReleaseConsistency(projectRoot, options = {}) {
470
+ const manifest = buildReleaseManifest(projectRoot, options);
471
+ return evaluateReleaseManifest(manifest, { enforceSync: true });
472
+ }
473
+
474
+ function inspectReleaseReadiness(projectRoot, options = {}) {
475
+ const manifest = buildReleaseManifest(projectRoot, {
476
+ ...options,
477
+ skipWrapperSync: true,
478
+ });
479
+ return evaluateReleaseManifest(manifest, { enforceSync: false });
480
+ }
481
+
413
482
  module.exports = {
414
483
  REQUIRED_RUNTIMES,
415
484
  WRAPPER_TARGETS,
@@ -419,5 +488,8 @@ module.exports = {
419
488
  loadRecoveryFixtureReport,
420
489
  loadMultiAgentSoakReport,
421
490
  buildReleaseManifest,
491
+ inspectCanonicalSource,
492
+ evaluateReleaseManifest,
493
+ inspectReleaseReadiness,
422
494
  checkReleaseConsistency,
423
495
  };