@orkastery/cli 0.5.0 → 0.5.3

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 (131) hide show
  1. package/adapters/claude-code/README.md +11 -2
  2. package/adapters/claude-code/agents/ork-master.md +2 -0
  3. package/adapters/claude-code/commands/master.md +8 -0
  4. package/adapters/claude-code/commands/onboarding.md +3 -2
  5. package/adapters/claude-code/commands/ork.md +9 -0
  6. package/adapters/codex/skills/ork/SKILL.md +18 -3
  7. package/adapters/hermes/README.md +1 -0
  8. package/adapters/hermes/bin/ork-network-status.sh +10 -0
  9. package/adapters/hermes/hermes.plugin.json +1 -0
  10. package/adapters/hermes/hitl-ingress/__init__.py +2 -2
  11. package/adapters/hermes/skills/orkastery-devmaster/SKILL.md +6 -5
  12. package/adapters/openclaw/README.md +35 -4
  13. package/adapters/openclaw/dist/hitl-ingress.js +2 -2
  14. package/adapters/openclaw/dist/index.js +47 -12
  15. package/adapters/openclaw/openclaw.plugin.json +5 -1
  16. package/adapters/openclaw/package.json +2 -2
  17. package/adapters/openclaw/src/hitl-ingress.ts +2 -2
  18. package/adapters/openclaw/src/index.ts +50 -12
  19. package/assets/orkmind_bridge.py +117 -9
  20. package/dist/adapters/claude-bg.js +59 -11
  21. package/dist/adapters/codex.js +4 -3
  22. package/dist/agents-md.js +1 -0
  23. package/dist/auditrun.js +6 -2
  24. package/dist/board.js +16 -14
  25. package/dist/branch-de-estado.js +66 -10
  26. package/dist/busca-semantica.js +52 -12
  27. package/dist/canarios-rm057.js +149 -0
  28. package/dist/canarios.js +2 -0
  29. package/dist/ci.js +8 -1
  30. package/dist/claim-lint.js +3 -1
  31. package/dist/claims.js +39 -1
  32. package/dist/company-brain-context.js +158 -22
  33. package/dist/docs.js +110 -3
  34. package/dist/doctor-pulse-cron.js +194 -0
  35. package/dist/doctor-rede.js +149 -0
  36. package/dist/doctor.js +294 -37
  37. package/dist/entrega-pr.js +27 -8
  38. package/dist/esgotamentos.js +181 -0
  39. package/dist/experiencia.js +12 -2
  40. package/dist/fabrica-estado.js +35 -9
  41. package/dist/fabrica-publicar.js +6 -0
  42. package/dist/fechamento.js +20 -66
  43. package/dist/fix.js +3 -3
  44. package/dist/forja.js +31 -7
  45. package/dist/gates.js +5 -1
  46. package/dist/hitl-contract.js +59 -4
  47. package/dist/hitl-curto.js +11 -6
  48. package/dist/hitl-gates.js +5 -0
  49. package/dist/hitl-lote.js +5 -2
  50. package/dist/hitl-resumo.js +33 -1
  51. package/dist/hitl-tempo-parado.js +201 -0
  52. package/dist/hitl-texto-livre.js +5 -5
  53. package/dist/horario.js +27 -0
  54. package/dist/hosts.js +59 -13
  55. package/dist/impedimento.js +205 -0
  56. package/dist/index.js +433 -88
  57. package/dist/indice-vetorial.js +87 -10
  58. package/dist/indice.js +3 -1
  59. package/dist/init.js +28 -5
  60. package/dist/integracoes-locais.js +4 -0
  61. package/dist/intelligence-graph-cli.js +267 -30
  62. package/dist/intelligence-graph-contexto.js +215 -0
  63. package/dist/intelligence-graph-contract.js +71 -12
  64. package/dist/intelligence-graph-extract-md.js +41 -23
  65. package/dist/intelligence-graph-extract-ts.js +181 -11
  66. package/dist/intelligence-graph-extract.js +186 -9
  67. package/dist/intelligence-graph-index.js +205 -24
  68. package/dist/intelligence-graph-parsers.js +6 -4
  69. package/dist/intelligence-graph-repo.js +10 -0
  70. package/dist/leases.js +680 -87
  71. package/dist/ledger-stats.js +15 -1
  72. package/dist/ledger.js +12 -0
  73. package/dist/licoes.js +6 -4
  74. package/dist/liveness.js +2 -1
  75. package/dist/maestro-runtime.js +7 -1
  76. package/dist/manifest.js +44 -1
  77. package/dist/master-nota.js +14 -5
  78. package/dist/master.js +129 -12
  79. package/dist/mcp-artifacts.js +36 -3
  80. package/dist/mcp-git.js +80 -15
  81. package/dist/mcp-grafo-worker.js +93 -0
  82. package/dist/mcp-grafo.js +279 -0
  83. package/dist/mcp-install.js +2 -1
  84. package/dist/mcp-server.js +23 -1
  85. package/dist/mcp-ship.js +2 -0
  86. package/dist/mcp-verify.js +3 -1
  87. package/dist/memoria.js +48 -6
  88. package/dist/network-roadmap.js +195 -20
  89. package/dist/ocupacao.js +17 -5
  90. package/dist/onboarding.js +9 -4
  91. package/dist/orkmind.js +90 -5
  92. package/dist/orquestracao.js +14 -11
  93. package/dist/parado-no-condutor.js +1089 -0
  94. package/dist/phase.js +68 -15
  95. package/dist/policies.js +79 -3
  96. package/dist/portfolio.js +4 -1
  97. package/dist/projeto-alvo.js +5 -1
  98. package/dist/prompts.js +36 -1
  99. package/dist/prova-ativacao.js +414 -0
  100. package/dist/pulse-delivery.js +60 -7
  101. package/dist/pulse.js +99 -3
  102. package/dist/ratelimit.js +20 -4
  103. package/dist/recall.js +3 -1
  104. package/dist/rede-adesao.js +399 -0
  105. package/dist/rede-forja.js +289 -0
  106. package/dist/rede-projetos.js +239 -0
  107. package/dist/rede-status.js +336 -0
  108. package/dist/rede.js +980 -0
  109. package/dist/retry.js +69 -2
  110. package/dist/roadmap-status.js +162 -7
  111. package/dist/runtime-context.js +5 -1
  112. package/dist/runtime-profiles.js +22 -1
  113. package/dist/saida-segura.js +58 -0
  114. package/dist/session-watcher-claude.js +113 -23
  115. package/dist/sessoes-contas.js +187 -0
  116. package/dist/sessoes-inventario.js +6 -12
  117. package/dist/sessoes.js +87 -3
  118. package/dist/ship.js +110 -20
  119. package/dist/thread.js +212 -14
  120. package/dist/util.js +71 -4
  121. package/dist/verify.js +2 -2
  122. package/dist/worktree.js +34 -8
  123. package/eval/casos/onboarding.json +1 -1
  124. package/eval/fixtures/fx-pedido-colado/caso.json +20 -0
  125. package/package.json +7 -3
  126. package/references/definition-of-done.md +1 -1
  127. package/skills/README.md +1 -1
  128. package/skills/core/onboarding/SKILL.md +3 -2
  129. package/skills/core/orkastery-bootstrap/SKILL.md +2 -2
  130. package/skills/phases/master-metrics/SKILL.md +8 -3
  131. package/skills/phases/ship-release/SKILL.md +6 -3
package/dist/memoria.js CHANGED
@@ -172,7 +172,7 @@ function abrirMemoria(carregado, opcoes = {}) {
172
172
  const { estado, driver } = (0, orkmind_1.resolverRegime)(carregado.manifesto, opcoes.driver ?? null);
173
173
  const ativo = estado.efetivo === 'orkmind' && driver !== null;
174
174
  const config = (0, orkmind_1.configDoManifesto)(carregado.manifesto);
175
- const embeddings = (universo) => estadoDeEmbeddings(carregado.manifesto, ativo ? driver?.disponivel().saude ?? null : null, carregado.raiz, estado.tenant, config.dsn, { universo });
175
+ const embeddings = (leitura = {}) => estadoDeEmbeddings(carregado.manifesto, ativo ? driver?.disponivel().saude ?? null : null, carregado.raiz, estado.tenant, config.dsn, leitura);
176
176
  if (estado.pedido === 'orkmind')
177
177
  estado.embeddings = embeddings();
178
178
  const consultarRestrito = (collection, tags) => {
@@ -250,10 +250,30 @@ function abrirMemoria(carregado, opcoes = {}) {
250
250
  return null;
251
251
  return driver.exportar(colecao).find((e) => e.id === id) ?? null;
252
252
  },
253
+ universo(tenant) {
254
+ // A fronteira do manifesto vale antes de qualquer leitura: tenant e so o configurado.
255
+ if (tenant !== estado.tenant)
256
+ throw new Error('memory.tenant.mismatch');
257
+ if (leituraRestrita)
258
+ throw new Error('memory.query.invalid');
259
+ if (!ativo || !driver)
260
+ throw new Error('memory.universo.indisponivel');
261
+ if (typeof driver.universo !== 'function')
262
+ throw new Error('memory.universo.unsupported');
263
+ return driver.universo(tenant);
264
+ },
253
265
  };
254
- // A cobertura precisa do universo do tenant (um export por colecao): so quando pedida.
266
+ // RM-038: a cobertura precisa do universo da busca (uma leitura pela ponte): so quando pedida. Falha
267
+ // de leitura (janela saturada, fronteira violada) vira motivo tipado no estado, nunca cobertura inventada.
255
268
  if (opcoes.embeddings === 'detalhado' && ativo && !leituraRestrita) {
256
- estado.embeddings = embeddings((0, indice_vetorial_1.universoDoTenant)(memoria, estado.tenant));
269
+ let leitura;
270
+ try {
271
+ leitura = { universo: (0, indice_vetorial_1.universoDaBusca)(memoria, estado.tenant) };
272
+ }
273
+ catch (erro) {
274
+ leitura = { falhaDoUniverso: (0, indice_vetorial_1.codigoDaFalha)(erro) };
275
+ }
276
+ estado.embeddings = embeddings(leitura);
257
277
  }
258
278
  return memoria;
259
279
  }
@@ -300,6 +320,7 @@ function estadoDeEmbeddings(manifesto, saude, raiz, tenant, dsn, opcoes = {}) {
300
320
  const estado = {
301
321
  configurado, provider: config.provider, modelo: configurado ? config.model : null, dim: configurado ? config.dim : null,
302
322
  variavelDaChave: variavel, chavePresente, fallback, indices: [], entradas: null, cobertura: null,
323
+ universo: null, aviso: null, falhaDoUniverso: null,
303
324
  ativo: 'nenhum', sondado: saude !== null, motivo: null, detalhe: '', correcao: '',
304
325
  };
305
326
  if (!configurado) {
@@ -331,13 +352,24 @@ function estadoDeEmbeddings(manifesto, saude, raiz, tenant, dsn, opcoes = {}) {
331
352
  estado.correcao = `exporte ${variavel} com a chave dedicada ao Orkastery; o valor nunca vai ao manifesto`;
332
353
  }
333
354
  if (opcoes.universo) {
334
- const universo = opcoes.universo.filter(e => (e.tags.project ?? []).includes(tenant));
355
+ // RM-038: o universo vem de universoDaBusca; a fronteira vale de novo antes de contar.
356
+ if (opcoes.universo.tenant !== tenant)
357
+ throw new Error('memory.query.scope-violation');
358
+ (0, indice_vetorial_1.conferirUniverso)(opcoes.universo.entradas, tenant, opcoes.universo.lidoEm);
359
+ const universo = opcoes.universo.entradas;
335
360
  estado.indices = indicesDoTenant(raiz, tenant, base, universo);
336
361
  estado.entradas = universo.length;
362
+ estado.universo = { porColecao: { ...opcoes.universo.porColecao }, foraDaBusca: opcoes.universo.foraDaBusca,
363
+ ...(opcoes.universo.latenciaMs === undefined ? {} : { latenciaMs: opcoes.universo.latenciaMs }) };
337
364
  const referencia = estado.ativo === 'fallback' ? { modelo: fallback.modelo, dim: fallback.dim } : { modelo: config.model, dim: config.dim };
338
365
  const indice = estado.indices.find(i => i.modelo === referencia.modelo && i.dim === referencia.dim);
339
- estado.cobertura = universo.length === 0 ? 0 : Math.round(((indice?.coerentes ?? 0) / universo.length) * 10_000) / 10_000;
366
+ const coerentes = indice?.coerentes ?? 0;
367
+ estado.cobertura = universo.length === 0 ? 0 : Math.round((coerentes / universo.length) * 10_000) / 10_000;
368
+ // RM-038 (D7): o indice que a busca usa cobre menos do que ela enxerga: dito, nunca escondido.
369
+ estado.aviso = (0, indice_vetorial_1.avisoDeCobertura)(coerentes, universo, estado.ativo);
340
370
  }
371
+ else if (opcoes.falhaDoUniverso)
372
+ estado.falhaDoUniverso = opcoes.falhaDoUniverso;
341
373
  return estado;
342
374
  }
343
375
  /**
@@ -1098,12 +1130,22 @@ function textoDosEmbeddings(e) {
1098
1130
  `dependencias ${e.fallback.dependencias ? 'ok' : e.sondado ? 'ausentes' : 'nao sondadas'}` : '(nao declarado)'}`);
1099
1131
  linhas.push(` caminho ativo ${e.ativo}${e.sondado ? '' : ' (ponte nao sondada)'}`);
1100
1132
  }
1133
+ if (e.universo) {
1134
+ const colecoes = Object.entries(e.universo.porColecao).map(([c, n]) => `${c} ${n}`).join(', ');
1135
+ linhas.push(` universo da busca ${e.entradas} entrada(s) do tenant: ${colecoes}`);
1136
+ linhas.push(` fora da busca ${(0, indice_vetorial_1.textoForaDaBusca)(e.universo.foraDaBusca)}`);
1137
+ }
1138
+ else if (e.falhaDoUniverso) {
1139
+ linhas.push(` universo da busca nao lido (${e.falhaDoUniverso}); cobertura nao calculada`);
1140
+ }
1101
1141
  for (const i of e.indices) {
1102
1142
  linhas.push(` indice ${i.modelo} / ${i.dim} dim: ${i.vetores} vetor(es), ${i.coerentes ?? '?'} coerente(s), ${i.desatualizados ?? '?'} desatualizado(s)`);
1103
1143
  }
1104
1144
  if (e.entradas !== null) {
1105
- linhas.push(` cobertura ${e.cobertura === null ? '-' : `${Math.round(e.cobertura * 1000) / 10}%`} de ${e.entradas} entrada(s) do tenant`);
1145
+ linhas.push(` cobertura ${e.cobertura === null ? '-' : `${Math.round(e.cobertura * 1000) / 10}%`} de ${e.entradas} entrada(s) do universo da busca`);
1106
1146
  }
1147
+ if (e.aviso)
1148
+ linhas.push(` aviso ${e.aviso}`);
1107
1149
  if (e.sonda) {
1108
1150
  linhas.push(` sonda ${e.sonda.ok ? `${e.sonda.alvo} respondeu em ${e.sonda.latenciaMs} ms` : `sem resposta (${e.sonda.motivo ?? 'sem caminho ativo'})`}`);
1109
1151
  }
@@ -36,6 +36,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.ErroDoPedidoDeProjeto = exports.SAIDA_DO_PEDIDO = exports.LIMIAR_SEM_BATIDA_MS = exports.CONTRATO_REGISTRO_DE_PROJETOS = exports.CONTRATO_PANORAMA_DA_REDE = void 0;
37
37
  exports.projetosConhecidos = projetosConhecidos;
38
38
  exports.resolverProjeto = resolverProjeto;
39
+ exports.redeDoProjetoFixado = redeDoProjetoFixado;
39
40
  exports.montarPanoramaDaRede = montarPanoramaDaRede;
40
41
  exports.textoDoPanoramaDaRede = textoDoPanoramaDaRede;
41
42
  /**
@@ -62,6 +63,11 @@ exports.textoDoPanoramaDaRede = textoDoPanoramaDaRede;
62
63
  *
63
64
  * Na fatia 2, os hosts chegam aqui por dois modos: `host` (gateway sem cwd de projeto, a regra da
64
65
  * RM-052 no `--projeto` proprio do `network`) e `fixado` (o servidor MCP, que le so o projeto dele).
66
+ *
67
+ * Na fatia 3, a rede por pessoa da RM-053 (`ork.rede-status/v1`) entra como fonte: as maquinas da
68
+ * casa, com a batida de cada uma, os projetos que elas declaram (um projeto sem clone nem registro
69
+ * nesta maquina passa a ser conhecido pelo nome) e a maquina que esta na rede mas nao publicou na
70
+ * fabrica do projeto, dita como lacuna e nunca como "0 threads".
65
71
  */
66
72
  const node_child_process_1 = require("node:child_process");
67
73
  const fs = __importStar(require("node:fs"));
@@ -70,6 +76,7 @@ const path = __importStar(require("node:path"));
70
76
  const branch_de_estado_1 = require("./branch-de-estado");
71
77
  const docs_1 = require("./docs");
72
78
  const fabrica_estado_1 = require("./fabrica-estado");
79
+ Object.defineProperty(exports, "LIMIAR_SEM_BATIDA_MS", { enumerable: true, get: function () { return fabrica_estado_1.LIMIAR_SEM_BATIDA_MS; } });
73
80
  const forja_1 = require("./forja");
74
81
  const horario_1 = require("./horario");
75
82
  const estado_thread_1 = require("./estado-thread");
@@ -77,6 +84,9 @@ const manifest_1 = require("./manifest");
77
84
  const maquina_1 = require("./maquina");
78
85
  const modos_1 = require("./modos");
79
86
  const projeto_alvo_1 = require("./projeto-alvo");
87
+ const rede_1 = require("./rede");
88
+ const rede_projetos_1 = require("./rede-projetos");
89
+ const rede_status_1 = require("./rede-status");
80
90
  const roadmap_reservas_1 = require("./roadmap-reservas");
81
91
  const roadmap_status_1 = require("./roadmap-status");
82
92
  const thread_1 = require("./thread");
@@ -85,8 +95,6 @@ const yaml_1 = require("./yaml");
85
95
  exports.CONTRATO_PANORAMA_DA_REDE = 'ork.network-roadmap/v1';
86
96
  /** O registro de projetos da RM-052, lido como ela o grava. */
87
97
  exports.CONTRATO_REGISTRO_DE_PROJETOS = 'ork.projetos/v1';
88
- /** Maquina sem retrato novo ha mais disto esta sem batida: o mesmo limiar da rede (RM-053, `SEM_BATIDA_MS`). */
89
- exports.LIMIAR_SEM_BATIDA_MS = 3 * 60 * 60 * 1000;
90
98
  /** A recusa de projeto sai com o mesmo codigo da RM-052. */
91
99
  exports.SAIDA_DO_PEDIDO = 4;
92
100
  /** A recusa do `--projeto`, com os mesmos codigos da RM-052: o nucleo nunca chuta. */
@@ -119,9 +127,8 @@ const lacuna = (tipo, parte, alvo, detalhe, correcao) => ({ tipo, parte, ...(alv
119
127
  /**
120
128
  * Nome de remoto do git, como `origin`. O valor vem do manifesto versionado (`fabrica.remoto`), e um
121
129
  * valor que comece com `-` viraria opcao do `git fetch` (`--upload-pack=...`): so este formato chega
122
- * ao git (achado 4 do CHECK).
130
+ * ao git (achado 4 do CHECK). O validador e o mesmo da branch de estado (RM-047).
123
131
  */
124
- const REMOTO = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
125
132
  /**
126
133
  * Nome de branch base, como `main`. Tambem vem do manifesto (`worktree.base_branch`), e o `git log`
127
134
  * da base o recebia cru: `--output=<arquivo>` gravava um arquivo (rodada 2 do CHECK).
@@ -129,7 +136,7 @@ const REMOTO = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
129
136
  const baseValida = (base) => typeof base === 'string' && /^[A-Za-z0-9][A-Za-z0-9._/-]{0,99}$/.test(base) && !base.includes('..');
130
137
  function projetoDoClone(c, origem) {
131
138
  const remoto = c.manifesto.fabrica.remoto;
132
- const url = REMOTO.test(remoto) ? (0, branch_de_estado_1.git)(c.raiz, ['remote', 'get-url', remoto]) : null;
139
+ const url = (0, branch_de_estado_1.remotoValido)(remoto) ? (0, branch_de_estado_1.git)(c.raiz, ['remote', 'get-url', '--', remoto]) : null;
133
140
  return { nome: c.manifesto.project.name, forja: url?.ok ? (0, forja_1.identidadeDaForja)(url.stdout.trim()) : null, raiz: c.raiz,
134
141
  base: c.manifesto.worktree.base_branch, remoto, origem };
135
142
  }
@@ -423,19 +430,19 @@ function maquinasDoPanorama(retratos, local, ctx, lacunas) {
423
430
  if (local && m.maquina === local.maquina)
424
431
  continue;
425
432
  const idadeMs = Math.max(0, agora - Date.parse(m.publicadoEm));
426
- const semBatida = idadeMs > exports.LIMIAR_SEM_BATIDA_MS;
433
+ const semBatida = idadeMs > fabrica_estado_1.LIMIAR_SEM_BATIDA_MS;
427
434
  if (semBatida) {
428
435
  lacunas.push(lacuna('maquina.sem-batida', 'fabrica', m.maquina, `${m.maquina} sem retrato novo há ${(0, horario_1.duracaoCurta)(Math.floor(idadeMs / 60000))} (último em ${(0, horario_1.formatarDataHora)(m.publicadoEm, { fuso: ctx.fuso })}): ` +
429
436
  'as threads dela podem ter andado', `confira a ${m.maquina}: ela publica ao criar thread, despachar fase, entregar e a cada batida do pulse`));
430
437
  }
431
438
  saida.push({ maquina: m.maquina, por: m.por, publicadoEm: m.publicadoEm, idadeMin: Math.floor(idadeMs / 60000), semBatida,
432
439
  estaMaquina: m.maquina === ctx.maquina, origem: 'retrato', versaoOrk: m.versaoOrk ?? null,
433
- ativas: m.threads.filter((t) => !t.entregue), entreguesSemMaster: m.threads.filter((t) => t.entregue).length });
440
+ ativas: m.threads.filter((t) => !t.entregue), entreguesSemMaster: m.threads.filter((t) => t.entregue).length, threadsLidas: true });
434
441
  }
435
442
  if (local) {
436
443
  saida.push({ maquina: local.maquina, por: local.por, publicadoEm: local.publicadoEm, idadeMin: 0, semBatida: false, estaMaquina: true,
437
444
  origem: 'estado-local', versaoOrk: local.versaoOrk, ativas: local.threads.filter((t) => !t.entregue),
438
- entreguesSemMaster: local.threads.filter((t) => t.entregue).length });
445
+ entreguesSemMaster: local.threads.filter((t) => t.entregue).length, threadsLidas: true });
439
446
  }
440
447
  return saida.sort((a, b) => a.maquina.localeCompare(b.maquina));
441
448
  }
@@ -572,8 +579,8 @@ function lerProjetoDoClone(p, geral) {
572
579
  const carregado = (0, manifest_1.carregarManifesto)(raiz);
573
580
  // O fuso do dono deste projeto vale para o dia e os horarios dele (achado 5 do CHECK).
574
581
  const ctx = { ...geral, fuso: carregado?.manifesto.owner?.timezone ?? geral.fuso };
575
- if (!REMOTO.test(remoto)) {
576
- lacunas.push(lacuna('projeto.remoto-invalido', 'projeto', raizParaExibir(raiz), `fabrica.remoto do manifesto (${JSON.stringify(remoto).slice(0, 60)}) não é nome de remoto do git: nada foi lido pelo git`, 'corrija fabrica.remoto no orkastery.yaml (padrão: origin)'));
582
+ if (!(0, branch_de_estado_1.remotoValido)(remoto)) {
583
+ lacunas.push(lacuna('projeto.remoto-invalido', 'projeto', raizParaExibir(raiz), `fabrica.remoto do manifesto (${(0, branch_de_estado_1.remotoRedigido)(remoto)}) não é nome de remoto do git: nada foi lido pelo git`, 'corrija fabrica.remoto no orkastery.yaml (padrão: origin)'));
577
584
  return montarProjeto(p, { nome: p.nome, docs: null, reservas: null, retratos: null, local: null, commits: [] }, ctx, fontes, lacunas);
578
585
  }
579
586
  if (!baseValida(p.base ?? 'main') || (carregado && !baseValida(carregado.manifesto.worktree.base_branch))) {
@@ -785,6 +792,134 @@ function lerProjeto(p, ctx) {
785
792
  return vazio(lacuna('projeto.sem-leitura', 'projeto', p.raiz ? raizParaExibir(p.raiz) : p.nome, `a leitura de ${p.nome} parou: ${(0, forja_1.detalheSeguro)(e?.message ?? String(e))}`, 'confira o clone e o estado dele no próprio projeto; os outros projetos seguiram'));
786
793
  }
787
794
  }
795
+ // ---------------------------------------------------------------------------
796
+ // A rede por pessoa (fatia 3).
797
+ // ---------------------------------------------------------------------------
798
+ /** As lacunas do `ork.rede-status/v1` que sao da casa (forja, casa, retrato), e nao da fabrica de um projeto. */
799
+ const lacunaDaCasa = (tipo) => tipo.startsWith('forja.') || tipo.startsWith('rede.') || tipo === 'retrato.invalido' || tipo === 'maquina.nome-em-uso';
800
+ const CORRECAO_DA_CASA = {
801
+ 'forja.ausente': 'instale o gh (ou o glab) nesta máquina',
802
+ 'forja.sem-login': 'gh auth login (ou glab auth login) nesta máquina',
803
+ 'rede.sem-repositorio': 'ork network entrar numa máquina da pessoa',
804
+ 'maquina.nome-em-uso': 'ork network entrar --maquina NOME ou --forcar',
805
+ };
806
+ /** A rede lida para o panorama, ou o motivo de nao ter lido (vai ao `naoConsultado`). */
807
+ function lerRedeDoPanorama(opcoes, ctx, cwd) {
808
+ if (opcoes.rede === false)
809
+ return { status: null, motivo: 'não lida nesta chamada' };
810
+ if (opcoes.rede)
811
+ return { status: opcoes.rede, motivo: null };
812
+ if ((process.env.ORK_REDE_LER ?? '').trim() === '0')
813
+ return { status: null, motivo: 'leitura desligada (ORK_REDE_LER=0)' };
814
+ try {
815
+ // D-G6 da fatia 2: no host, o diretorio do gateway nao e projeto; a rede le so o registro e os retratos.
816
+ return { status: (0, rede_status_1.lerRede)({ semRemoto: ctx.semRemoto, maquina: opcoes.maquina, agora: ctx.quando, diretorio: opcoes.host ? null : cwd }),
817
+ motivo: null };
818
+ }
819
+ catch (e) {
820
+ return { status: null, motivo: `a leitura parou: ${(0, forja_1.detalheSeguro)(e?.message ?? String(e))}` };
821
+ }
822
+ }
823
+ /** O projeto que um retrato declara casa com o projeto do panorama: pela forja, ou pelo nome quando falta a forja. */
824
+ function declara(q, p, nome) {
825
+ const forja = q.remoto ? (0, forja_1.identidadeDaForja)(q.remoto) : null;
826
+ if (forja && p.forja)
827
+ return (0, forja_1.mesmaForja)(forja, p.forja);
828
+ return (0, rede_projetos_1.emUmaLinha)(q.nome) === nome;
829
+ }
830
+ /** Os projetos que as maquinas da rede declaram com remoto de forja e que esta maquina nao conhece. */
831
+ function projetosDaRede(status, conhecidos) {
832
+ const saida = [];
833
+ for (const m of status.membros) {
834
+ if (m.origem !== 'rede')
835
+ continue;
836
+ for (const q of m.projetos) {
837
+ const nome = (0, rede_projetos_1.emUmaLinha)(q.nome);
838
+ const forja = q.remoto ? (0, forja_1.identidadeDaForja)(q.remoto) : null;
839
+ if (!forja || !projeto_alvo_1.PADRAO_DO_NOME_DE_PROJETO.test(nome))
840
+ continue;
841
+ const p = { nome, forja, raiz: null, base: null, remoto: 'origin', origem: 'rede' };
842
+ if (![...conhecidos, ...saida].some((x) => mesmoProjeto(x, p)))
843
+ saida.push(p);
844
+ }
845
+ }
846
+ return saida;
847
+ }
848
+ /**
849
+ * Suspeitas da revisao de 03/10 (mesma raiz do defeito 3 da revisao): o retrato com o nome desta
850
+ * maquina e de outra instalacao quando o status diz `nomeEmUso`; entao nenhum retrato da rede e "esta
851
+ * maquina". `null` nesse caso; senao, o nome saneado desta maquina.
852
+ */
853
+ function euNaRede(status, ctx) {
854
+ const eu = (0, rede_1.nomeSeguro)(ctx.maquina);
855
+ return status.estaMaquina.maquina === eu && status.estaMaquina.nomeEmUso ? null : eu;
856
+ }
857
+ /** A rede no panorama; no MCP fixado, os projetos de cada maquina ficam so o servido (D5 da RM-052). */
858
+ function redeNoPanorama(status, ctx, servido) {
859
+ const agora = Date.parse(ctx.quando);
860
+ const fonte = status.fontes.find((f) => f.fonte === 'rede');
861
+ const eu = euNaRede(status, ctx);
862
+ const membros = status.membros.filter((m) => m.origem === 'rede').map((m) => {
863
+ const idadeMs = agora - Date.parse(m.publicadoEm);
864
+ const idadeMin = Number.isFinite(idadeMs) ? Math.floor(Math.max(0, idadeMs) / 60000) : null;
865
+ const projetos = m.projetos.filter((q) => !servido || declara(q, servido.p, servido.nome)).map((q) => (0, rede_projetos_1.emUmaLinha)(q.nome));
866
+ return { maquina: (0, rede_projetos_1.emUmaLinha)(m.maquina), estaMaquina: m.maquina === eu, publicadoEm: m.publicadoEm, idadeMin,
867
+ semBatida: idadeMin === null || idadeMin * 60000 > fabrica_estado_1.LIMIAR_SEM_BATIDA_MS, versaoOrk: m.versaoOrk ? (0, rede_projetos_1.emUmaLinha)(m.versaoOrk) : null,
868
+ projetos: [...new Set(projetos)] };
869
+ });
870
+ return { casa: status.casa ? (0, rede_1.refDaCasa)(status.casa) : null, ponta: fonte?.ponta ?? null, atualizado: fonte ? fonte.atualizado : null,
871
+ lidoEm: status.consultadoEm, membros };
872
+ }
873
+ /**
874
+ * A maquina da rede que declara o projeto e nao publicou na fabrica dele entra na lista com as
875
+ * threads NAO lidas e uma lacuna: antes ela sumia, e a lista parecia completa.
876
+ */
877
+ function completarComARede(x, p, status, ctx) {
878
+ if (x.maquinas === null)
879
+ return;
880
+ const agora = Date.parse(ctx.quando);
881
+ for (const m of status.membros) {
882
+ if (m.origem !== 'rede' || !m.projetos.some((q) => declara(q, p, x.projeto.nome)))
883
+ continue;
884
+ if (x.maquinas.some((y) => (0, rede_1.nomeSeguro)(y.maquina) === m.maquina))
885
+ continue;
886
+ const maquina = (0, rede_projetos_1.emUmaLinha)(m.maquina);
887
+ const idadeMs = agora - Date.parse(m.publicadoEm);
888
+ const legivel = Number.isFinite(idadeMs);
889
+ const idadeMin = legivel ? Math.floor(Math.max(0, idadeMs) / 60000) : 0;
890
+ const semBatida = !legivel || idadeMin * 60000 > fabrica_estado_1.LIMIAR_SEM_BATIDA_MS;
891
+ x.maquinas.push({ maquina, por: 'rede por pessoa', publicadoEm: m.publicadoEm, idadeMin, semBatida, estaMaquina: m.maquina === euNaRede(status, ctx),
892
+ origem: 'rede', versaoOrk: m.versaoOrk ? (0, rede_projetos_1.emUmaLinha)(m.versaoOrk) : null, ativas: [], entreguesSemMaster: 0, threadsLidas: false });
893
+ x.lacunas.push(lacuna('maquina.sem-fabrica', 'fabrica', maquina, `${maquina} está na rede por pessoa e declara ${x.projeto.nome}, mas não tem retrato em ${fabrica_estado_1.BRANCH_DA_FABRICA}: as threads dela neste projeto não foram lidas`, `na ${maquina}, rode ork fabrica entrar no clone de ${x.projeto.nome} (ou atualize o ork dela)`));
894
+ if (semBatida) {
895
+ x.lacunas.push(lacuna('maquina.sem-batida', 'rede', maquina, `${maquina} sem retrato novo na rede ${legivel
896
+ ? `há ${(0, horario_1.duracaoCurta)(idadeMin)}` : '(batida ilegível)'}`, `confira a ${maquina}: ela publica na batida do pulse`));
897
+ }
898
+ }
899
+ x.maquinas.sort((a, b) => a.maquina.localeCompare(b.maquina));
900
+ }
901
+ /**
902
+ * Fatia 3: o `ork network status` do servidor MCP fixado. A rede e da pessoa, mas o retrato de cada
903
+ * maquina lista todos os projetos dela: aqui fica so o projeto servido (D5 da RM-052), e o resto vai
904
+ * ao "nao consultado". Com `ORK_REDE_LER=0`, a casa e lida so da ultima copia desta maquina.
905
+ */
906
+ function redeDoProjetoFixado(raiz, opcoes = {}) {
907
+ const c = (0, manifest_1.carregarManifesto)(raiz);
908
+ if (!c) {
909
+ throw new ErroDoPedidoDeProjeto('projeto.sem-manifesto', `a raiz fixada não tem ${manifest_1.NOME_MANIFESTO}`, [], 'confira a raiz fixada na instalação do servidor MCP (ork mcp install --project <raiz>)');
910
+ }
911
+ const p = projetoDoClone(c, 'instalacao');
912
+ const s = opcoes.status ?? (0, rede_status_1.lerRede)({ semRemoto: opcoes.semRemoto === true || (process.env.ORK_REDE_LER ?? '').trim() === '0', diretorio: c.raiz,
913
+ soRaiz: c.raiz });
914
+ const membros = s.membros.map((m) => ({ ...m, projetos: m.projetos.filter((q) => declara(q, p, p.nome)) }))
915
+ .filter((m) => m.origem === 'rede' || m.projetos.length > 0);
916
+ // Revisao de 03/10 (D5): a lacuna de uma maquina que saiu dos membros (vista so em outro projeto) sai junto.
917
+ const nomes = new Set(membros.map((m) => m.maquina));
918
+ return { ...s, membros, fontes: s.fontes.filter((f) => !f.projeto || f.projeto === p.nome),
919
+ lacunas: s.lacunas.filter((l) => (!l.projeto || l.projeto === p.nome) &&
920
+ (!l.maquina || l.tipo === 'maquina.nome-em-uso' || nomes.has(l.maquina))),
921
+ naoConsultado: [...s.naoConsultado, `outros projetos das máquinas (este servidor MCP mostra só ${p.nome}; os outros vêm do CLI ork network status)`] };
922
+ }
788
923
  /**
789
924
  * `ork network roadmap`: os projetos pedidos (ou todos os conhecidos), cada um com roadmap, reservas,
790
925
  * threads por maquina, fontes e lacunas. Recusa o `--projeto` ambiguo ou desconhecido.
@@ -794,11 +929,21 @@ function montarPanoramaDaRede(opcoes = {}) {
794
929
  const cwd = opcoes.cwd ?? process.cwd();
795
930
  const ctx = { quando, maquina: (0, maquina_1.nomeDaMaquina)(opcoes.maquina), semRemoto: opcoes.semRemoto === true, executor: opcoes.executor,
796
931
  fuso: (0, horario_1.fusoDoDono)().fuso };
797
- const naoConsultado = [
798
- 'rede por pessoa (RM-053, ork.rede-status/v1): não lida nesta versão; as máquinas vêm da branch ork/fabrica-estado de cada projeto',
799
- ];
932
+ const naoConsultado = [];
800
933
  const lacunas = [];
934
+ const lida = lerRedeDoPanorama(opcoes, ctx, cwd);
935
+ const status = lida.status;
936
+ if (!status)
937
+ naoConsultado.push(`rede por pessoa (RM-053, ork.rede-status/v1): ${lida.motivo}; as máquinas vêm da branch ${fabrica_estado_1.BRANCH_DA_FABRICA} de cada projeto`);
938
+ else {
939
+ for (const l of status.lacunas) {
940
+ if (!lacunaDaCasa(l.tipo) || l.projeto)
941
+ continue;
942
+ lacunas.push(lacuna(l.tipo, 'rede', l.maquina, (0, forja_1.detalheSeguro)((0, rede_projetos_1.emUmaLinha)(l.detalhe)), CORRECAO_DA_CASA[l.tipo] ?? 'confira com ork network status'));
943
+ }
944
+ }
801
945
  let alvos;
946
+ let servido = null;
802
947
  if (opcoes.fixado !== undefined) {
803
948
  // D-G5 (fatia 2): o servidor MCP le so o projeto dele, em todas as maquinas; o registro nao e lido.
804
949
  const c = (0, manifest_1.carregarManifesto)(opcoes.fixado);
@@ -806,6 +951,7 @@ function montarPanoramaDaRede(opcoes = {}) {
806
951
  throw new ErroDoPedidoDeProjeto('projeto.sem-manifesto', `a raiz fixada não tem ${manifest_1.NOME_MANIFESTO}`, [], 'confira a raiz fixada na instalação do servidor MCP (ork mcp install --project <raiz>)');
807
952
  }
808
953
  alvos = [projetoDoClone(c, 'instalacao')];
954
+ servido = { p: alvos[0], nome: alvos[0].nome };
809
955
  naoConsultado.push('outros projetos desta máquina: este servidor MCP lê só o projeto fixado na instalação; ' +
810
956
  'os outros vêm do CLI `ork network roadmap`');
811
957
  }
@@ -813,17 +959,19 @@ function montarPanoramaDaRede(opcoes = {}) {
813
959
  const conhecidos = projetosConhecidos({ cwd, registro: opcoes.registro, host: opcoes.host });
814
960
  naoConsultado.push(...conhecidos.naoConsultado);
815
961
  lacunas.push(...conhecidos.lacunas);
962
+ // Fatia 3: o projeto que uma maquina da rede declara passa a ser conhecido, lido pela forja.
963
+ const todos = [...conhecidos.projetos, ...(status ? projetosDaRede(status, conhecidos.projetos) : [])];
816
964
  if (opcoes.pedido !== undefined) {
817
965
  if (opcoes.host)
818
- exigirPedidoDoHost(opcoes.pedido, conhecidos.projetos);
819
- const alvo = resolverProjeto(opcoes.pedido, conhecidos.projetos, cwd);
966
+ exigirPedidoDoHost(opcoes.pedido, todos);
967
+ const alvo = resolverProjeto(opcoes.pedido, todos, cwd);
820
968
  alvos = [alvo];
821
- for (const p of conhecidos.projetos)
969
+ for (const p of todos)
822
970
  if (!mesmoProjeto(p, alvo))
823
971
  naoConsultado.push(`projeto ${p.nome}: fora do pedido (--projeto)`);
824
972
  }
825
973
  else {
826
- alvos = conhecidos.projetos;
974
+ alvos = todos;
827
975
  }
828
976
  }
829
977
  if (alvos.length === 0) {
@@ -832,9 +980,15 @@ function montarPanoramaDaRede(opcoes = {}) {
832
980
  : 'nenhum projeto conhecido nesta máquina: sem manifesto no diretório atual e sem registro', opcoes.host ? 'peça o projeto pela forja (--projeto github:dono/repo) ou registre o clone com `ork projetos registrar <caminho>`'
833
981
  : 'peça o projeto: --projeto <caminho do clone> ou --projeto github:dono/repo'));
834
982
  }
835
- const projetos = alvos.map((p) => lerProjeto(p, ctx));
983
+ const projetos = alvos.map((p) => {
984
+ const x = lerProjeto(p, ctx);
985
+ if (status)
986
+ completarComARede(x, p, status, ctx);
987
+ return x;
988
+ });
836
989
  return { contrato: exports.CONTRATO_PANORAMA_DA_REDE, consultadoEm: quando, maquina: ctx.maquina, pedido: opcoes.pedido ?? null,
837
- limiarSemBatidaMin: exports.LIMIAR_SEM_BATIDA_MS / 60000, fuso: projetos[0]?.projeto.fuso ?? ctx.fuso, projetos, naoConsultado, lacunas };
990
+ limiarSemBatidaMin: fabrica_estado_1.LIMIAR_SEM_BATIDA_MS / 60000, fuso: projetos[0]?.projeto.fuso ?? ctx.fuso, projetos,
991
+ rede: status ? redeNoPanorama(status, ctx, servido) : null, naoConsultado, lacunas };
838
992
  }
839
993
  // ---------------------------------------------------------------------------
840
994
  // O texto.
@@ -860,6 +1014,11 @@ function linhasDasMaquinas(x, fuso) {
860
1014
  return ['• nenhuma máquina publicou em ork/fabrica-estado'];
861
1015
  return x.maquinas.flatMap((m) => {
862
1016
  const quem = `${m.maquina}${m.estaMaquina ? ' (esta máquina)' : ''}`;
1017
+ // Fatia 3: a maquina so da rede por pessoa; as threads dela neste projeto nao foram lidas.
1018
+ if (m.origem === 'rede') {
1019
+ return [`• ${quem}: threads não lidas, na rede por pessoa (retrato de ${(0, horario_1.formatarDataHora)(m.publicadoEm, { fuso })}, ` +
1020
+ `há ${(0, horario_1.duracaoCurta)(m.idadeMin)}${m.semBatida ? ', SEM BATIDA' : ''}) e sem retrato em ${fabrica_estado_1.BRANCH_DA_FABRICA}`];
1021
+ }
863
1022
  const batida = m.origem === 'estado-local' ? 'estado local lido agora'
864
1023
  : `retrato de ${(0, horario_1.formatarDataHora)(m.publicadoEm, { fuso })} (há ${(0, horario_1.duracaoCurta)(m.idadeMin)})${m.semBatida ? ', SEM BATIDA' : ''}`;
865
1024
  const cabeca = `• ${quem}: ${m.ativas.length} ativa(s)${m.entreguesSemMaster ? `, ${m.entreguesSemMaster} entregue(s) sem MASTER` : ''}, ${batida}`;
@@ -876,6 +1035,19 @@ function linhasDasReservas(x, fuso) {
876
1035
  return ['• nenhuma reserva em ork/roadmap-reservas'];
877
1036
  return x.reservas.map((r) => ` ${r.item} · ${r.maquina} · ${r.thread ?? 'sem thread'} · desde ${(0, horario_1.formatarDataHora)(r.desdeEm, { fuso })}`);
878
1037
  }
1038
+ /** Fatia 3: a casa da rede por pessoa e as maquinas dela, com a batida e os projetos que declaram. */
1039
+ function linhasDaRede(r, quando, fuso) {
1040
+ const lido = r.lidoEm === quando ? 'lida agora' : `lida ${(0, horario_1.formatarDataHora)(r.lidoEm, { fuso })}`;
1041
+ const casa = !r.casa ? '• casa: nenhuma achada (veja as lacunas)'
1042
+ : r.atualizado === null ? `• casa: ${r.casa}, não lida (veja as lacunas)`
1043
+ : `• casa: ${r.casa} @ ${r.ponta ? r.ponta.slice(0, 7) : 'sem commit'}, ${r.atualizado ? lido : 'última cópia desta máquina, sem leitura nova'}`;
1044
+ if (r.membros.length === 0)
1045
+ return [casa, '• nenhum retrato lido na casa; isso não quer dizer que não há máquinas: veja as lacunas'];
1046
+ return [casa, ...r.membros.map((m) => `• ${m.maquina}${m.estaMaquina ? ' (esta máquina)' : ''}: retrato de ` +
1047
+ `${(0, horario_1.formatarDataHora)(m.publicadoEm, { fuso })}${m.idadeMin === null ? ' (batida ilegível)' : ` (há ${(0, horario_1.duracaoCurta)(m.idadeMin)})`}` +
1048
+ `${m.semBatida ? ', SEM BATIDA' : ''}${m.versaoOrk ? `, ork ${m.versaoOrk}` : ''}, ` +
1049
+ (m.projetos.length ? `projetos: ${m.projetos.join(', ')}` : 'nenhum projeto declarado aqui'))];
1050
+ }
879
1051
  const linhaDaLacuna = (l) => `• ${l.tipo}${l.alvo ? ` (${l.alvo})` : ''}: ${l.detalhe}. O que fazer: ${l.correcao}.`;
880
1052
  /** O texto de `ork network roadmap`: o consultado e o nao consultado no alto, e cada parte com a fonte e a hora. */
881
1053
  function textoDoPanoramaDaRede(p) {
@@ -887,13 +1059,16 @@ function textoDoPanoramaDaRede(p) {
887
1059
  p.projetos.length ? `Consultado: ${p.projetos.map((x) => descreverProjeto(x.projeto)).join('; ')}` : 'Consultado: nenhum projeto.',
888
1060
  'Não consultado:', ...(p.naoConsultado.length ? p.naoConsultado.map((n) => `• ${n}`) : ['• nada fora do pedido']),
889
1061
  ];
1062
+ if (p.rede)
1063
+ linhas.push('', 'Rede por pessoa (RM-053)', ...linhasDaRede(p.rede, p.consultadoEm, p.fuso));
890
1064
  if (p.lacunas.length)
891
1065
  linhas.push('', 'Lacunas da consulta', ...p.lacunas.map(linhaDaLacuna));
892
1066
  for (const x of p.projetos) {
893
1067
  // Cada projeto no fuso do dono dele (achado 5 da rodada 2): a legenda do bloco diz quando difere.
894
1068
  const fuso = x.projeto.fuso;
895
1069
  linhas.push('', '────────', '');
896
- linhas.push(x.roadmap ? (0, roadmap_status_1.textoDoStatusDoRoadmap)(x.roadmap, fuso)
1070
+ // Fatia 2 do ensaio (P7): o fuso ja sai no fim do panorama (e no bloco, quando difere): sem a legenda do status.
1071
+ linhas.push(x.roadmap ? (0, roadmap_status_1.textoDoStatusDoRoadmap)(x.roadmap, fuso, { legenda: false })
897
1072
  : `Roadmap do ${x.projeto.nome}: não lido (${x.lacunas.map((l) => l.tipo).filter((t) => t !== 'projeto.sem-clone').join(', ') || 'sem fonte'}).`);
898
1073
  linhas.push('', 'Threads por máquina', ...linhasDasMaquinas(x, fuso));
899
1074
  linhas.push('', 'Reservas', ...linhasDasReservas(x, fuso));
package/dist/ocupacao.js CHANGED
@@ -28,6 +28,7 @@ exports.faseTemPausaPrevista = faseTemPausaPrevista;
28
28
  exports.sessaoLivreDaVaga = sessaoLivreDaVaga;
29
29
  exports.avaliarOcupacao = avaliarOcupacao;
30
30
  const ledger_1 = require("./ledger");
31
+ const impedimento_1 = require("./impedimento");
31
32
  const sessoes_adopt_1 = require("./sessoes-adopt");
32
33
  /** Estado que o `ork phase run` grava quando o bloco despachado pausa ao fim. */
33
34
  exports.PAUSA_PREVISTA = 'prevista ao fim do bloco';
@@ -91,9 +92,11 @@ function ultimaAtividadeEm(eventos) {
91
92
  * despacho e a sessao roda ate o fim do bloco. O impedimento de verify tambem nao: a sessao corrige e
92
93
  * verifica de novo. Somente leitura.
93
94
  */
94
- function sessaoLivreDaVaga(eventos, desde, agora, staleMin,
95
+ function sessaoLivreDaVaga(doLedger, desde, agora, staleMin,
95
96
  /** A-3 do CHECK 4: o bloqueio no runtime e por sessao; o de outra sessao (a sucedida) nao libera esta. */
96
97
  sessionId) {
98
+ // Fatia 2 do ensaio da 0.5.0 (P2): evento de `--dry-run` nao e atividade nem parada.
99
+ const eventos = doLedger.filter((e) => !(0, ledger_1.ehEnsaio)(e));
97
100
  const ultima = ultimaAtividadeEm(eventos);
98
101
  if (ultima !== null && minutos(ultima, agora) >= staleMin)
99
102
  return 'stale';
@@ -124,7 +127,8 @@ function pausaHumanaAberta(thread, eventos) {
124
127
  for (let i = 0; i < eventos.length; i++) {
125
128
  const e = eventos[i];
126
129
  const prevista = ehPausaPrevista(e);
127
- const escalada = e.tipo === ledger_1.TIPOS_DE_EVENTO.gateBloqueado && e.motivo === 'human.pending';
130
+ // RM-055: o impedimento do despacho que so o dono resolve tambem e pausa humana: nao ocupa vaga.
131
+ const escalada = (0, impedimento_1.ehEsperaDoDono)(e, ledger_1.TIPOS_DE_EVENTO.gateBloqueado);
128
132
  if (!prevista && !escalada)
129
133
  continue;
130
134
  const iFase = indiceDaFase(e.fase);
@@ -141,11 +145,16 @@ function pausaHumanaAberta(thread, eventos) {
141
145
  });
142
146
  if (resolvida)
143
147
  continue;
148
+ // Ensaio de 03/10/2026 (RM-049): a escalada traz a correcao gravada no gate (no impedimento do dono,
149
+ // o comando dele e o `ork retry run`); o board a mostra, em vez de um `ork gate request` que o
150
+ // modo sem pausa prevista recusa.
151
+ const correcao = escalada && typeof e.correcao === 'string' && e.correcao !== '' ? e.correcao : undefined;
144
152
  return {
145
153
  desdeEm: e.ts,
146
154
  evidencia: prevista
147
155
  ? `ledger ${ledger_1.TIPOS_DE_EVENTO.pausaHumana} (${exports.PAUSA_PREVISTA}) em ${e.ts}`
148
- : `ledger ${ledger_1.TIPOS_DE_EVENTO.gateBloqueado} (human.pending) em ${e.ts}`,
156
+ : `ledger ${ledger_1.TIPOS_DE_EVENTO.gateBloqueado} (${String(e.motivo)}) em ${e.ts}`,
157
+ ...(correcao ? { correcao } : {}),
149
158
  };
150
159
  }
151
160
  return null;
@@ -160,7 +169,7 @@ function impedimentoAberto(eventos) {
160
169
  continue;
161
170
  const motivo = e.motivo;
162
171
  // `human.pending` ja e pausa humana; `claims.unverifiable` avisa mas nao bloqueia.
163
- if (motivo === 'human.pending' || motivo === 'claims.unverifiable')
172
+ if (motivo === 'human.pending' || motivo === 'claims.unverifiable' || (0, impedimento_1.ehImpedimentoDoDono)(motivo))
164
173
  continue;
165
174
  const resolvido = eventos
166
175
  .slice(i + 1)
@@ -196,7 +205,9 @@ function minutos(desdeEm, ateEm) {
196
205
  * anterior terminada ou o runtime nao consultado), e entra por FIFO quando
197
206
  * houver vaga -- e ai conta contra o limite, como sempre contou.
198
207
  */
199
- function avaliarOcupacao(thread, eventos, opcoes) {
208
+ function avaliarOcupacao(thread, doLedger, opcoes) {
209
+ // Fatia 2 do ensaio da 0.5.0 (P2): o ensaio do ship (`dryRun: true`) nao pausa a thread no board.
210
+ const eventos = doLedger.filter((e) => !(0, ledger_1.ehEnsaio)(e));
200
211
  const sessionId = ultimaSessao(thread, eventos);
201
212
  const sessaoEstado = opcoes.estados === null || sessionId === null
202
213
  ? null
@@ -244,6 +255,7 @@ function avaliarOcupacao(thread, eventos, opcoes) {
244
255
  detalhe: `pausa humana aberta ha ${minutos(pausa.desdeEm, opcoes.agora)} min: ` +
245
256
  'esperar veredito humano nao ocupa vaga da maquina',
246
257
  evidencia: pausa.evidencia,
258
+ ...(pausa.correcao ? { correcao: pausa.correcao } : {}),
247
259
  };
248
260
  }
249
261
  if (registro)
@@ -33,7 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.ETAPAS_ONBOARDING = exports.PAUTA_ONBOARDING = exports.CONTRATO_ONBOARDING = void 0;
36
+ exports.ETAPAS_ONBOARDING = exports.PAUTA_ONBOARDING = exports.ONDE_FICAM_OS_SEGREDOS = exports.CONTRATO_ONBOARDING = void 0;
37
37
  exports.caminhoOnboarding = caminhoOnboarding;
38
38
  exports.validarConteudo = validarConteudo;
39
39
  exports.jsonCanonico = jsonCanonico;
@@ -54,10 +54,15 @@ const horario_1 = require("./horario");
54
54
  const experiencia_1 = require("./experiencia");
55
55
  const yaml_1 = require("./yaml");
56
56
  exports.CONTRATO_ONBOARDING = 'ork.onboarding/v1';
57
+ /**
58
+ * Fatia 2 do ensaio da 0.5.0 (P5): onde ficam os segredos, para quem usa o Hermes e para quem nao
59
+ * usa. Perguntas, erro e ajuda leem daqui; as docs e os hosts repetem a frase, conferida por teste.
60
+ */
61
+ exports.ONDE_FICAM_OS_SEGREDOS = 'no ambiente do processo ou no cofre do host (no Hermes, ~/.hermes/.env)';
57
62
  exports.PAUTA_ONBOARDING = [
58
63
  { etapa: 'maestro', pergunta: `Quem conduz o projeto, quais são seus objetivos e preferências, e em qual fuso horário o Orkastery deve mostrar horários? Informe o fuso como nome IANA, ex.: {"fuso":"${horario_1.FUSO_DE_BRASILIA}"}; ele orienta ${horario_1.CHAVE_DO_FUSO} no manifesto, sem editá-lo. Preferências em {"owner":{...}} gravam owner no manifesto.` },
59
- { etapa: 'credenciais', pergunta: 'Quais provedores públicos e nomes de variáveis em env serão usados? Segredos somente em ~/.hermes/.env; nunca informe valores.' },
60
- { etapa: 'bancos', pergunta: 'Quais bancos públicos e nomes de variáveis em env configuram as conexões? Nunca informe DSN ou senha; valores somente em ~/.hermes/.env.' },
64
+ { etapa: 'credenciais', pergunta: `Quais provedores públicos e nomes de variáveis em env serão usados? Segredos ficam ${exports.ONDE_FICAM_OS_SEGREDOS}; nunca informe valores.` },
65
+ { etapa: 'bancos', pergunta: `Quais bancos públicos e nomes de variáveis em env configuram as conexões? Nunca informe DSN ou senha; valores ficam ${exports.ONDE_FICAM_OS_SEGREDOS}.` },
61
66
  { etapa: 'memoria', pergunta: 'Deseja OrkMind? Informe {"modo":"orkmind"} ou {"modo":"files"}; a escolha orienta memory.mode no manifesto, sem editá-lo.' },
62
67
  { etapa: 'produtos', pergunta: 'Quais produtos e repositórios públicos pertencem ao projeto?' },
63
68
  { etapa: 'topologia', pergunta: 'Como se distribuem ambientes e serviços? Informe identificadores públicos.' },
@@ -74,7 +79,7 @@ function objeto(v) {
74
79
  [Object.prototype, null].includes(Object.getPrototypeOf(v));
75
80
  }
76
81
  /** Recusa antes de persistir. Diagnósticos nunca incluem a entrada recebida. */
77
- function invalido() { throw new Error('onboarding.input.invalid: use JSON público e referências de variáveis; segredos somente em ~/.hermes/.env'); }
82
+ function invalido() { throw new Error(`onboarding.input.invalid: use JSON público e referências de variáveis; segredos ficam ${exports.ONDE_FICAM_OS_SEGREDOS}`); }
78
83
  function textoSeguro(v) {
79
84
  return !(0, policies_1.procurarSegredos)(v).length &&
80
85
  !/[a-z][a-z0-9+.-]*:\/\/[^\s/]*@/i.test(v) &&