@orkastery/cli 0.5.1 → 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 (110) hide show
  1. package/adapters/claude-code/agents/ork-master.md +2 -0
  2. package/adapters/claude-code/commands/master.md +8 -0
  3. package/adapters/claude-code/commands/ork.md +9 -0
  4. package/adapters/codex/skills/ork/SKILL.md +18 -3
  5. package/adapters/hermes/README.md +1 -0
  6. package/adapters/hermes/bin/ork-network-status.sh +10 -0
  7. package/adapters/hermes/hermes.plugin.json +1 -0
  8. package/adapters/hermes/hitl-ingress/__init__.py +2 -2
  9. package/adapters/hermes/skills/orkastery-devmaster/SKILL.md +6 -5
  10. package/adapters/openclaw/README.md +24 -4
  11. package/adapters/openclaw/dist/hitl-ingress.js +2 -2
  12. package/adapters/openclaw/dist/index.js +46 -11
  13. package/adapters/openclaw/openclaw.plugin.json +5 -1
  14. package/adapters/openclaw/package.json +2 -2
  15. package/adapters/openclaw/src/hitl-ingress.ts +2 -2
  16. package/adapters/openclaw/src/index.ts +49 -11
  17. package/assets/orkmind_bridge.py +117 -9
  18. package/dist/adapters/claude-bg.js +39 -5
  19. package/dist/adapters/codex.js +4 -3
  20. package/dist/agents-md.js +1 -0
  21. package/dist/auditrun.js +5 -1
  22. package/dist/board.js +16 -14
  23. package/dist/branch-de-estado.js +66 -10
  24. package/dist/busca-semantica.js +52 -12
  25. package/dist/canarios-rm057.js +149 -0
  26. package/dist/canarios.js +2 -0
  27. package/dist/ci.js +8 -1
  28. package/dist/claims.js +39 -1
  29. package/dist/company-brain-context.js +158 -22
  30. package/dist/docs.js +48 -9
  31. package/dist/doctor-pulse-cron.js +194 -0
  32. package/dist/doctor-rede.js +149 -0
  33. package/dist/doctor.js +89 -9
  34. package/dist/entrega-pr.js +20 -8
  35. package/dist/esgotamentos.js +181 -0
  36. package/dist/experiencia.js +12 -2
  37. package/dist/fabrica-estado.js +14 -6
  38. package/dist/fabrica-publicar.js +6 -0
  39. package/dist/fechamento.js +20 -66
  40. package/dist/forja.js +31 -7
  41. package/dist/gates.js +5 -1
  42. package/dist/hitl-contract.js +59 -4
  43. package/dist/hitl-curto.js +11 -6
  44. package/dist/hitl-gates.js +5 -0
  45. package/dist/hitl-lote.js +5 -2
  46. package/dist/hitl-resumo.js +9 -0
  47. package/dist/hitl-tempo-parado.js +201 -0
  48. package/dist/hitl-texto-livre.js +5 -5
  49. package/dist/horario.js +27 -0
  50. package/dist/hosts.js +52 -10
  51. package/dist/impedimento.js +205 -0
  52. package/dist/index.js +391 -74
  53. package/dist/indice-vetorial.js +87 -10
  54. package/dist/indice.js +3 -1
  55. package/dist/init.js +17 -0
  56. package/dist/integracoes-locais.js +2 -0
  57. package/dist/intelligence-graph-cli.js +91 -8
  58. package/dist/intelligence-graph-contexto.js +215 -0
  59. package/dist/intelligence-graph-parsers.js +6 -4
  60. package/dist/leases.js +664 -87
  61. package/dist/ledger-stats.js +15 -1
  62. package/dist/ledger.js +3 -0
  63. package/dist/licoes.js +1 -0
  64. package/dist/maestro-runtime.js +4 -1
  65. package/dist/manifest.js +20 -1
  66. package/dist/master-nota.js +14 -5
  67. package/dist/master.js +129 -12
  68. package/dist/mcp-artifacts.js +34 -3
  69. package/dist/mcp-git.js +20 -3
  70. package/dist/mcp-grafo-worker.js +5 -3
  71. package/dist/mcp-grafo.js +50 -4
  72. package/dist/mcp-server.js +14 -1
  73. package/dist/memoria.js +48 -6
  74. package/dist/network-roadmap.js +191 -16
  75. package/dist/ocupacao.js +11 -3
  76. package/dist/orkmind.js +90 -5
  77. package/dist/orquestracao.js +12 -10
  78. package/dist/parado-no-condutor.js +188 -32
  79. package/dist/phase.js +68 -15
  80. package/dist/policies.js +52 -1
  81. package/dist/portfolio.js +4 -1
  82. package/dist/projeto-alvo.js +5 -1
  83. package/dist/prompts.js +36 -1
  84. package/dist/prova-ativacao.js +75 -6
  85. package/dist/pulse-delivery.js +34 -2
  86. package/dist/pulse.js +49 -3
  87. package/dist/ratelimit.js +20 -4
  88. package/dist/recall.js +3 -1
  89. package/dist/rede-adesao.js +399 -0
  90. package/dist/rede-forja.js +289 -0
  91. package/dist/rede-projetos.js +239 -0
  92. package/dist/rede-status.js +336 -0
  93. package/dist/rede.js +980 -0
  94. package/dist/retry.js +69 -2
  95. package/dist/roadmap-status.js +23 -3
  96. package/dist/runtime-profiles.js +22 -1
  97. package/dist/saida-segura.js +58 -0
  98. package/dist/session-watcher-claude.js +113 -23
  99. package/dist/sessoes-contas.js +187 -0
  100. package/dist/sessoes-inventario.js +6 -18
  101. package/dist/sessoes.js +83 -2
  102. package/dist/ship.js +85 -19
  103. package/dist/thread.js +192 -13
  104. package/dist/worktree.js +5 -2
  105. package/eval/fixtures/fx-pedido-colado/caso.json +20 -0
  106. package/package.json +7 -3
  107. package/references/definition-of-done.md +1 -1
  108. package/skills/README.md +1 -1
  109. package/skills/core/orkastery-bootstrap/SKILL.md +2 -2
  110. package/skills/phases/master-metrics/SKILL.md +8 -3
@@ -58,6 +58,7 @@ const hitl_contract_1 = require("./hitl-contract");
58
58
  const hitl_presentation_1 = require("./hitl-presentation");
59
59
  const roadmap_status_1 = require("./roadmap-status");
60
60
  const network_roadmap_1 = require("./network-roadmap");
61
+ const rede_status_1 = require("./rede-status");
61
62
  const hitl_sessions_1 = require("./hitl-sessions");
62
63
  const hitl_local_1 = require("./hitl-local");
63
64
  const mcp_artifacts_1 = require("./mcp-artifacts");
@@ -107,7 +108,9 @@ const nova = zod_1.z.object({ nome: zod_1.z.string().trim().min(1).max(200),
107
108
  const fase = daThread.extend({ fase: zod_1.z.enum(['GOAL', 'PLAN', 'GO', 'CHECK', 'SHIP', 'MASTER']),
108
109
  prompt: zod_1.z.string().min(1).max(65536), runtime: zod_1.z.enum(['claude-bg', 'codex']).optional(),
109
110
  model: zod_1.z.string().trim().min(1).max(200).optional(),
110
- effort: zod_1.z.enum(['low', 'medium', 'high', 'xhigh', 'max']).optional(), dryRun: zod_1.z.boolean().optional() }).strict();
111
+ effort: zod_1.z.enum(['low', 'medium', 'high', 'xhigh', 'max']).optional(),
112
+ // RM-056 (D1): o perfil pedido, validado contra o store pelo mesmo caminho do CLI.
113
+ perfil: zod_1.z.string().trim().regex(/^[a-z0-9][a-z0-9._-]{0,62}$/i).optional(), dryRun: zod_1.z.boolean().optional() }).strict();
111
114
  // Limite de tipos do conversor externo: validacao continua no schema Zod strict.
112
115
  const converterSchema = zod_to_json_schema_1.zodToJsonSchema;
113
116
  const resposta = (valor) => ({ content: [{ type: 'text', text: JSON.stringify(valor) }] });
@@ -502,6 +505,16 @@ function criarServidorMcp(opcoes) {
502
505
  const panorama = (0, network_roadmap_1.montarPanoramaDaRede)({ fixado: raiz });
503
506
  return resposta({ texto: (0, network_roadmap_1.textoDoPanoramaDaRede)(panorama), panorama });
504
507
  });
508
+ /**
509
+ * RM-054 (fatia 3): as maquinas da pessoa (RM-053, `ork.rede-status/v1`), com a fonte, a batida e as
510
+ * lacunas. O retrato de cada maquina fica so com o projeto servido (D5 da RM-052).
511
+ */
512
+ registrarTool('ork_network_status', { description: 'Status da Orkastery Network, somente leitura: as maquinas da pessoa (casa privada na forja), com a batida de cada uma, a forja e o login, os runtimes e hosts com versao e o projeto servido em cada uma, mais a fonte e as lacunas. Transporte o texto como vem. Lacuna e "Nao consultado" sao o que nao foi lido: nunca conclua "nenhuma maquina" nem "rede vazia" a partir deles. Nao le roadmap nem threads: para isso, ork_network_roadmap.',
513
+ inputSchema: zod_1.z.object({}).strict(), annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: true } }, async () => {
514
+ carregar();
515
+ const status = (0, network_roadmap_1.redeDoProjetoFixado)(raiz);
516
+ return resposta({ texto: (0, rede_status_1.textoDaRede)(status), status: JSON.parse((0, rede_status_1.jsonDaRede)(status)) });
517
+ });
505
518
  registrarTool('ork_observe', { description: 'Observa uma vez o progresso canonico e pedidos da thread. Nao cria monitor ou despacho.',
506
519
  inputSchema: daThread, annotations: { readOnlyHint: true } }, async ({ threadId }) => {
507
520
  return resposta(observar(threadId));
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"));
@@ -78,6 +84,9 @@ const manifest_1 = require("./manifest");
78
84
  const maquina_1 = require("./maquina");
79
85
  const modos_1 = require("./modos");
80
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");
81
90
  const roadmap_reservas_1 = require("./roadmap-reservas");
82
91
  const roadmap_status_1 = require("./roadmap-status");
83
92
  const thread_1 = require("./thread");
@@ -118,9 +127,8 @@ const lacuna = (tipo, parte, alvo, detalhe, correcao) => ({ tipo, parte, ...(alv
118
127
  /**
119
128
  * Nome de remoto do git, como `origin`. O valor vem do manifesto versionado (`fabrica.remoto`), e um
120
129
  * valor que comece com `-` viraria opcao do `git fetch` (`--upload-pack=...`): so este formato chega
121
- * ao git (achado 4 do CHECK).
130
+ * ao git (achado 4 do CHECK). O validador e o mesmo da branch de estado (RM-047).
122
131
  */
123
- const REMOTO = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
124
132
  /**
125
133
  * Nome de branch base, como `main`. Tambem vem do manifesto (`worktree.base_branch`), e o `git log`
126
134
  * da base o recebia cru: `--output=<arquivo>` gravava um arquivo (rodada 2 do CHECK).
@@ -128,7 +136,7 @@ const REMOTO = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
128
136
  const baseValida = (base) => typeof base === 'string' && /^[A-Za-z0-9][A-Za-z0-9._/-]{0,99}$/.test(base) && !base.includes('..');
129
137
  function projetoDoClone(c, origem) {
130
138
  const remoto = c.manifesto.fabrica.remoto;
131
- 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;
132
140
  return { nome: c.manifesto.project.name, forja: url?.ok ? (0, forja_1.identidadeDaForja)(url.stdout.trim()) : null, raiz: c.raiz,
133
141
  base: c.manifesto.worktree.base_branch, remoto, origem };
134
142
  }
@@ -429,12 +437,12 @@ function maquinasDoPanorama(retratos, local, ctx, lacunas) {
429
437
  }
430
438
  saida.push({ maquina: m.maquina, por: m.por, publicadoEm: m.publicadoEm, idadeMin: Math.floor(idadeMs / 60000), semBatida,
431
439
  estaMaquina: m.maquina === ctx.maquina, origem: 'retrato', versaoOrk: m.versaoOrk ?? null,
432
- 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 });
433
441
  }
434
442
  if (local) {
435
443
  saida.push({ maquina: local.maquina, por: local.por, publicadoEm: local.publicadoEm, idadeMin: 0, semBatida: false, estaMaquina: true,
436
444
  origem: 'estado-local', versaoOrk: local.versaoOrk, ativas: local.threads.filter((t) => !t.entregue),
437
- entreguesSemMaster: local.threads.filter((t) => t.entregue).length });
445
+ entreguesSemMaster: local.threads.filter((t) => t.entregue).length, threadsLidas: true });
438
446
  }
439
447
  return saida.sort((a, b) => a.maquina.localeCompare(b.maquina));
440
448
  }
@@ -571,8 +579,8 @@ function lerProjetoDoClone(p, geral) {
571
579
  const carregado = (0, manifest_1.carregarManifesto)(raiz);
572
580
  // O fuso do dono deste projeto vale para o dia e os horarios dele (achado 5 do CHECK).
573
581
  const ctx = { ...geral, fuso: carregado?.manifesto.owner?.timezone ?? geral.fuso };
574
- if (!REMOTO.test(remoto)) {
575
- 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)'));
576
584
  return montarProjeto(p, { nome: p.nome, docs: null, reservas: null, retratos: null, local: null, commits: [] }, ctx, fontes, lacunas);
577
585
  }
578
586
  if (!baseValida(p.base ?? 'main') || (carregado && !baseValida(carregado.manifesto.worktree.base_branch))) {
@@ -784,6 +792,134 @@ function lerProjeto(p, ctx) {
784
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'));
785
793
  }
786
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
+ }
787
923
  /**
788
924
  * `ork network roadmap`: os projetos pedidos (ou todos os conhecidos), cada um com roadmap, reservas,
789
925
  * threads por maquina, fontes e lacunas. Recusa o `--projeto` ambiguo ou desconhecido.
@@ -793,11 +929,21 @@ function montarPanoramaDaRede(opcoes = {}) {
793
929
  const cwd = opcoes.cwd ?? process.cwd();
794
930
  const ctx = { quando, maquina: (0, maquina_1.nomeDaMaquina)(opcoes.maquina), semRemoto: opcoes.semRemoto === true, executor: opcoes.executor,
795
931
  fuso: (0, horario_1.fusoDoDono)().fuso };
796
- const naoConsultado = [
797
- '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',
798
- ];
932
+ const naoConsultado = [];
799
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
+ }
800
945
  let alvos;
946
+ let servido = null;
801
947
  if (opcoes.fixado !== undefined) {
802
948
  // D-G5 (fatia 2): o servidor MCP le so o projeto dele, em todas as maquinas; o registro nao e lido.
803
949
  const c = (0, manifest_1.carregarManifesto)(opcoes.fixado);
@@ -805,6 +951,7 @@ function montarPanoramaDaRede(opcoes = {}) {
805
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>)');
806
952
  }
807
953
  alvos = [projetoDoClone(c, 'instalacao')];
954
+ servido = { p: alvos[0], nome: alvos[0].nome };
808
955
  naoConsultado.push('outros projetos desta máquina: este servidor MCP lê só o projeto fixado na instalação; ' +
809
956
  'os outros vêm do CLI `ork network roadmap`');
810
957
  }
@@ -812,17 +959,19 @@ function montarPanoramaDaRede(opcoes = {}) {
812
959
  const conhecidos = projetosConhecidos({ cwd, registro: opcoes.registro, host: opcoes.host });
813
960
  naoConsultado.push(...conhecidos.naoConsultado);
814
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) : [])];
815
964
  if (opcoes.pedido !== undefined) {
816
965
  if (opcoes.host)
817
- exigirPedidoDoHost(opcoes.pedido, conhecidos.projetos);
818
- const alvo = resolverProjeto(opcoes.pedido, conhecidos.projetos, cwd);
966
+ exigirPedidoDoHost(opcoes.pedido, todos);
967
+ const alvo = resolverProjeto(opcoes.pedido, todos, cwd);
819
968
  alvos = [alvo];
820
- for (const p of conhecidos.projetos)
969
+ for (const p of todos)
821
970
  if (!mesmoProjeto(p, alvo))
822
971
  naoConsultado.push(`projeto ${p.nome}: fora do pedido (--projeto)`);
823
972
  }
824
973
  else {
825
- alvos = conhecidos.projetos;
974
+ alvos = todos;
826
975
  }
827
976
  }
828
977
  if (alvos.length === 0) {
@@ -831,9 +980,15 @@ function montarPanoramaDaRede(opcoes = {}) {
831
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>`'
832
981
  : 'peça o projeto: --projeto <caminho do clone> ou --projeto github:dono/repo'));
833
982
  }
834
- 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
+ });
835
989
  return { contrato: exports.CONTRATO_PANORAMA_DA_REDE, consultadoEm: quando, maquina: ctx.maquina, pedido: opcoes.pedido ?? null,
836
- limiarSemBatidaMin: fabrica_estado_1.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 };
837
992
  }
838
993
  // ---------------------------------------------------------------------------
839
994
  // O texto.
@@ -859,6 +1014,11 @@ function linhasDasMaquinas(x, fuso) {
859
1014
  return ['• nenhuma máquina publicou em ork/fabrica-estado'];
860
1015
  return x.maquinas.flatMap((m) => {
861
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
+ }
862
1022
  const batida = m.origem === 'estado-local' ? 'estado local lido agora'
863
1023
  : `retrato de ${(0, horario_1.formatarDataHora)(m.publicadoEm, { fuso })} (há ${(0, horario_1.duracaoCurta)(m.idadeMin)})${m.semBatida ? ', SEM BATIDA' : ''}`;
864
1024
  const cabeca = `• ${quem}: ${m.ativas.length} ativa(s)${m.entreguesSemMaster ? `, ${m.entreguesSemMaster} entregue(s) sem MASTER` : ''}, ${batida}`;
@@ -875,6 +1035,19 @@ function linhasDasReservas(x, fuso) {
875
1035
  return ['• nenhuma reserva em ork/roadmap-reservas'];
876
1036
  return x.reservas.map((r) => ` ${r.item} · ${r.maquina} · ${r.thread ?? 'sem thread'} · desde ${(0, horario_1.formatarDataHora)(r.desdeEm, { fuso })}`);
877
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
+ }
878
1051
  const linhaDaLacuna = (l) => `• ${l.tipo}${l.alvo ? ` (${l.alvo})` : ''}: ${l.detalhe}. O que fazer: ${l.correcao}.`;
879
1052
  /** O texto de `ork network roadmap`: o consultado e o nao consultado no alto, e cada parte com a fonte e a hora. */
880
1053
  function textoDoPanoramaDaRede(p) {
@@ -886,6 +1059,8 @@ function textoDoPanoramaDaRede(p) {
886
1059
  p.projetos.length ? `Consultado: ${p.projetos.map((x) => descreverProjeto(x.projeto)).join('; ')}` : 'Consultado: nenhum projeto.',
887
1060
  'Não consultado:', ...(p.naoConsultado.length ? p.naoConsultado.map((n) => `• ${n}`) : ['• nada fora do pedido']),
888
1061
  ];
1062
+ if (p.rede)
1063
+ linhas.push('', 'Rede por pessoa (RM-053)', ...linhasDaRede(p.rede, p.consultadoEm, p.fuso));
889
1064
  if (p.lacunas.length)
890
1065
  linhas.push('', 'Lacunas da consulta', ...p.lacunas.map(linhaDaLacuna));
891
1066
  for (const x of p.projetos) {
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';
@@ -126,7 +127,8 @@ function pausaHumanaAberta(thread, eventos) {
126
127
  for (let i = 0; i < eventos.length; i++) {
127
128
  const e = eventos[i];
128
129
  const prevista = ehPausaPrevista(e);
129
- 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);
130
132
  if (!prevista && !escalada)
131
133
  continue;
132
134
  const iFase = indiceDaFase(e.fase);
@@ -143,11 +145,16 @@ function pausaHumanaAberta(thread, eventos) {
143
145
  });
144
146
  if (resolvida)
145
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;
146
152
  return {
147
153
  desdeEm: e.ts,
148
154
  evidencia: prevista
149
155
  ? `ledger ${ledger_1.TIPOS_DE_EVENTO.pausaHumana} (${exports.PAUSA_PREVISTA}) em ${e.ts}`
150
- : `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 } : {}),
151
158
  };
152
159
  }
153
160
  return null;
@@ -162,7 +169,7 @@ function impedimentoAberto(eventos) {
162
169
  continue;
163
170
  const motivo = e.motivo;
164
171
  // `human.pending` ja e pausa humana; `claims.unverifiable` avisa mas nao bloqueia.
165
- if (motivo === 'human.pending' || motivo === 'claims.unverifiable')
172
+ if (motivo === 'human.pending' || motivo === 'claims.unverifiable' || (0, impedimento_1.ehImpedimentoDoDono)(motivo))
166
173
  continue;
167
174
  const resolvido = eventos
168
175
  .slice(i + 1)
@@ -248,6 +255,7 @@ function avaliarOcupacao(thread, doLedger, opcoes) {
248
255
  detalhe: `pausa humana aberta ha ${minutos(pausa.desdeEm, opcoes.agora)} min: ` +
249
256
  'esperar veredito humano nao ocupa vaga da maquina',
250
257
  evidencia: pausa.evidencia,
258
+ ...(pausa.correcao ? { correcao: pausa.correcao } : {}),
251
259
  };
252
260
  }
253
261
  if (registro)