@orkastery/cli 0.6.0 → 0.6.2

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 (118) hide show
  1. package/adapters/claude-code/agents/ork-check.md +1 -1
  2. package/adapters/claude-code/agents/ork-go.md +1 -1
  3. package/adapters/claude-code/agents/ork-goal.md +1 -1
  4. package/adapters/claude-code/agents/ork-master.md +1 -1
  5. package/adapters/claude-code/agents/ork-plan.md +1 -1
  6. package/adapters/claude-code/agents/ork-ship.md +1 -1
  7. package/adapters/claude-code/commands/ork.md +1 -1
  8. package/adapters/claude-code/mods/ork-torre/hooks/frases.ts +2 -0
  9. package/adapters/codex/skills/ork/SKILL.md +1 -1
  10. package/adapters/openclaw/openclaw.plugin.json +1 -1
  11. package/adapters/openclaw/package.json +1 -1
  12. package/assets/adapters-en/claude-code/agents/ork-check.md +1 -1
  13. package/assets/adapters-en/claude-code/agents/ork-go.md +1 -1
  14. package/assets/adapters-en/claude-code/agents/ork-goal.md +1 -1
  15. package/assets/adapters-en/claude-code/agents/ork-master.md +1 -1
  16. package/assets/adapters-en/claude-code/agents/ork-plan.md +1 -1
  17. package/assets/adapters-en/claude-code/agents/ork-ship.md +1 -1
  18. package/assets/adapters-en/claude-code/commands/ork.md +1 -1
  19. package/assets/adapters-en/codex/skills/ork/SKILL.md +1 -1
  20. package/assets/orkmind_fixture.py +4 -1
  21. package/assets/skills-en/core/orkastery-bootstrap/SKILL.md +1 -1
  22. package/assets/skills-en/phases/check-quality/SKILL.md +1 -1
  23. package/assets/skills-en/phases/go-implementation/SKILL.md +1 -1
  24. package/dist/adapters/claude-bg.js +9 -7
  25. package/dist/adapters/grok-events.js +227 -0
  26. package/dist/adapters/grok-mcp-servidor.js +622 -0
  27. package/dist/adapters/grok-mcp.js +178 -0
  28. package/dist/adapters/grok.js +686 -0
  29. package/dist/agendador-cpu.js +92 -0
  30. package/dist/branch-de-estado.js +7 -2
  31. package/dist/changelog-check.js +217 -0
  32. package/dist/check-agil.js +289 -25
  33. package/dist/check-paralelo.js +73 -18
  34. package/dist/ci.js +3 -2
  35. package/dist/claim-lint.js +29 -0
  36. package/dist/claims.js +4 -0
  37. package/dist/conducao.js +5 -0
  38. package/dist/docs.js +16 -16
  39. package/dist/doctor-forja.js +1 -3
  40. package/dist/doctor.js +1 -1
  41. package/dist/entrega-pr.js +10 -10
  42. package/dist/fabrica-estado.js +20 -11
  43. package/dist/fila-pesada.js +121 -11
  44. package/dist/hitl-classificacao.js +1 -0
  45. package/dist/horario.js +4 -1
  46. package/dist/index.js +63 -35
  47. package/dist/integracoes-locais.js +2 -0
  48. package/dist/intelligence-graph-cli.js +27 -9
  49. package/dist/locale-accounts.js +11 -3
  50. package/dist/locale-ajuda.js +10 -6
  51. package/dist/locale-claims.js +16 -2
  52. package/dist/locale-forja.js +2 -4
  53. package/dist/locale-grafo.js +56 -1
  54. package/dist/locale-maestro.js +38 -0
  55. package/dist/locale-network.js +8 -6
  56. package/dist/locale-onboarding.js +14 -0
  57. package/dist/locale-operacao-restante.js +2 -2
  58. package/dist/locale-preflight.js +80 -0
  59. package/dist/locale-projetos.js +6 -0
  60. package/dist/locale-prompts.js +8 -8
  61. package/dist/locale-pulse.js +4 -0
  62. package/dist/locale-retry.js +56 -2
  63. package/dist/locale-security-scan.js +35 -1
  64. package/dist/locale-ship.js +10 -2
  65. package/dist/locale-verify.js +15 -0
  66. package/dist/locale.js +24 -7
  67. package/dist/maestro-cli.js +12 -17
  68. package/dist/manifest.js +6 -1
  69. package/dist/mcp-install.js +24 -13
  70. package/dist/mcp-server.js +62 -33
  71. package/dist/mcp-verify-async.js +353 -0
  72. package/dist/mcp-verify-worker.js +17 -0
  73. package/dist/mcp-verify.js +10 -1
  74. package/dist/merge-da-entrega.js +85 -0
  75. package/dist/network-roadmap.js +18 -14
  76. package/dist/noctum-aceite.js +154 -14
  77. package/dist/noctum-cli.js +69 -12
  78. package/dist/noctum-entrega.js +55 -7
  79. package/dist/noctum-envelope.js +1 -1
  80. package/dist/noctum-execucao.js +5 -1
  81. package/dist/noctum-fechamento.js +18 -7
  82. package/dist/noctum-fila.js +6 -1
  83. package/dist/noctum-guarda.js +81 -4
  84. package/dist/noctum-maquinas.js +5 -1
  85. package/dist/noctum-modo.js +35 -7
  86. package/dist/noctum-reconciliacao.js +4 -1
  87. package/dist/noctum-rede.js +102 -0
  88. package/dist/noctum-relatorio.js +44 -10
  89. package/dist/noctum-retomada-rede.js +71 -0
  90. package/dist/noctum-vigia.js +49 -9
  91. package/dist/parado-no-condutor.js +14 -15
  92. package/dist/pedido-conducao.js +51 -0
  93. package/dist/phase.js +43 -15
  94. package/dist/postura-local.js +7 -3
  95. package/dist/preflight.js +27 -27
  96. package/dist/prompts.js +4 -2
  97. package/dist/pulse.js +4 -2
  98. package/dist/ratelimit.js +1 -0
  99. package/dist/rede.js +24 -12
  100. package/dist/retry.js +115 -10
  101. package/dist/runtime-context.js +165 -45
  102. package/dist/runtime-escada.js +7 -1
  103. package/dist/runtime-profiles.js +27 -17
  104. package/dist/runtimes.js +42 -5
  105. package/dist/security-scan/agil.js +18 -7
  106. package/dist/security-scan/cli.js +2 -2
  107. package/dist/security-scan/executor.js +8 -0
  108. package/dist/security-scan/report.js +1 -1
  109. package/dist/session-watcher-grok.js +425 -0
  110. package/dist/session-watcher.js +52 -11
  111. package/dist/sessoes-contas.js +33 -5
  112. package/dist/setup.js +11 -11
  113. package/dist/varredura-acoes.js +8 -15
  114. package/dist/verify.js +191 -43
  115. package/package.json +2 -2
  116. package/skills/core/orkastery-bootstrap/SKILL.md +1 -1
  117. package/skills/phases/check-quality/SKILL.md +1 -1
  118. package/skills/phases/go-implementation/SKILL.md +1 -1
@@ -65,9 +65,11 @@ const branch_de_estado_1 = require("./branch-de-estado");
65
65
  const ci_1 = require("./ci");
66
66
  const horario_1 = require("./horario");
67
67
  const ledger_1 = require("./ledger");
68
+ const maquina_1 = require("./maquina");
68
69
  const master_1 = require("./master");
69
70
  const noctum_arco_1 = require("./noctum-arco");
70
71
  const noctum_envelope_1 = require("./noctum-envelope");
72
+ const noctum_coordenacao_1 = require("./noctum-coordenacao");
71
73
  const noctum_fila_1 = require("./noctum-fila");
72
74
  const noctum_medicao_1 = require("./noctum-medicao");
73
75
  const noctum_registro_1 = require("./noctum-registro");
@@ -76,6 +78,8 @@ const noctum_supervisao_1 = require("./noctum-supervisao");
76
78
  const parado_no_condutor_1 = require("./parado-no-condutor");
77
79
  const estado_thread_1 = require("./estado-thread");
78
80
  const thread_1 = require("./thread");
81
+ const noctum_coordenacao_2 = require("./noctum-coordenacao");
82
+ const noctum_transporte_1 = require("./noctum-transporte");
79
83
  const util_1 = require("./util");
80
84
  const worktree_1 = require("./worktree");
81
85
  exports.CONTRATO_PILOTO = 'ork.noctum-piloto/v1';
@@ -128,8 +132,9 @@ function remotoLocal(url) {
128
132
  return /^file:/i.test(url) || /^(\/|\.{1,2}\/|[A-Za-z]:[\\/])/.test(url) || (!/^[a-z][a-z0-9+.-]*:\/\//i.test(url) && !/^[^@\s]+@[^:\s]+:/.test(url));
129
133
  }
130
134
  /** Os limiares da proposta, pela janela do piloto. */
131
- function limiaresDaProposta(horas) {
132
- return { m1MinimoHoras: arred(horas), m5MaximoMin: 15, entregasMinimas: horas < 12 ? 2 : 4, margemDoTeto: 0, atrasoDoRelatorioMin: noctum_fila_1.CADENCIA_MS / MIN };
135
+ function limiaresDaProposta(horas, multimaquina = false) {
136
+ return { m1MinimoHoras: arred(horas), m5MaximoMin: multimaquina ? 25 : 15, entregasMinimas: horas < 12 ? 2 : 4,
137
+ margemDoTeto: 0, atrasoDoRelatorioMin: noctum_fila_1.CADENCIA_MS / MIN };
133
138
  }
134
139
  /** As chaves dos limiares do dono, no contrato `ork.noctum-limiares/v1`. */
135
140
  exports.CHAVES_DOS_LIMIARES = ['m1MinimoHoras', 'm5MaximoMin', 'entregasMinimas', 'margemDoTeto', 'atrasoDoRelatorioMin'];
@@ -203,12 +208,29 @@ function aceiteDoPiloto(carregado, arco, opcoes = {}) {
203
208
  if (!estado?.conteudo || !estado.autorizadoPor)
204
209
  throw new RecusaDoAceite('noctum.aceite.sem-arco', `o arco ${arco} nunca foi autorizado pelo dono`);
205
210
  const conteudo = estado.conteudo;
211
+ const multimaquina = (0, noctum_envelope_1.envelopeMultimaquina)(conteudo);
212
+ const maquinaAtual = (0, maquina_1.nomeDaMaquina)(opcoes.maquina);
213
+ let rede = null;
214
+ let erroDaRede = null;
215
+ if (multimaquina) {
216
+ try {
217
+ if (opcoes.rede)
218
+ rede = opcoes.rede;
219
+ else {
220
+ const leitura = (0, noctum_transporte_1.lerRamo)(carregado, arco, { remoto: opcoes.remoto, maquina: maquinaAtual }, noctum_coordenacao_2.conferirCoordenacao);
221
+ rede = { registros: leitura.registros.map(r => r.dados), estado: (0, noctum_coordenacao_1.derivarCoordenacao)(leitura) };
222
+ }
223
+ }
224
+ catch (e) {
225
+ erroDaRede = `a coordenação não se lê (${e instanceof noctum_envelope_1.RecusaDoArco ? e.motivo : e.message.slice(0, 120)})`;
226
+ }
227
+ }
206
228
  const janela = conteudo.janela;
207
229
  // A condução começa no início da janela ou na primeira autorização, o que vier depois; as autorizações seguintes, dentro
208
230
  // da janela, são emendas do dono.
209
231
  let autorizacoes = [];
210
232
  try {
211
- autorizacoes = (0, noctum_arco_1.versoesAutorizadas)(raiz, arco).map(v => ({ versao: v.versao, autorizadoEm: v.autorizadoEm }));
233
+ autorizacoes = (0, noctum_arco_1.versoesAutorizadas)(raiz, arco).map(v => ({ versao: v.versao, autorizadoEm: v.autorizadoEm, envelopeSha256: v.envelopeSha256 }));
212
234
  }
213
235
  catch (e) {
214
236
  if (!(e instanceof noctum_envelope_1.RecusaDoArco))
@@ -221,6 +243,19 @@ function aceiteDoPiloto(carregado, arco, opcoes = {}) {
221
243
  const horasDaJanela = (Date.parse(janela.fim) - Date.parse(janela.inicio)) / HORA;
222
244
  const git = opcoes.git ?? gitDaRaiz(raiz);
223
245
  const remoto = opcoes.remoto ?? carregado.manifesto.fabrica?.remoto ?? 'origin';
246
+ const outras = [];
247
+ for (const outra of opcoes.outrasMaquinas ?? []) {
248
+ const r = outra.resultado;
249
+ const envelope = objeto(r) && objeto(r.envelope) ? r.envelope : null;
250
+ const maquina = objeto(r) && typeof r.maquina === 'string' ? r.maquina : null;
251
+ const valida = objeto(r) && r.contrato === exports.CONTRATO_PILOTO && r.arco === arco && envelope && typeof envelope.sha256 === 'string' &&
252
+ autorizacoes.some(v => v.envelopeSha256 === envelope.sha256) && maquina && multimaquina && conteudo.maquinas.some(m => m.nome === maquina) &&
253
+ Array.isArray(r.medidas) && Array.isArray(r.criterios) && typeof r.veredito === 'string';
254
+ if (!valida) {
255
+ throw new RecusaDoAceite('noctum.aceite.outra-maquina-invalida', `${outra.fonte}: o arquivo precisa ser um ${exports.CONTRATO_PILOTO} completo do arco ${arco}, de uma máquina e envelope autorizados aqui`);
256
+ }
257
+ outras.push({ ...outra, resultado: r });
258
+ }
224
259
  // As leituras: a supervisão, o relatório e os ledgers das threads do arco.
225
260
  let supervisao = null;
226
261
  let erroDaSupervisao = null;
@@ -300,6 +335,8 @@ function aceiteDoPiloto(carregado, arco, opcoes = {}) {
300
335
  catch { /* sem transporte configurado: o relatório diz a lacuna */ }
301
336
  const medidas = [];
302
337
  const medida = (m) => { medidas.push(m); return m; };
338
+ const nomesDasMaquinas = multimaquina ? conteudo.maquinas.map(m => m.nome) : [maquinaAtual];
339
+ const porMaquina = (valor) => Object.fromEntries(nomesDasMaquinas.map(maquina => [maquina, valor(maquina)]));
303
340
  /**
304
341
  * I1 do CHECK 2: a entrega do arco é o `ship_done` que a entrega dele gravou (o arco na evidência), e não a reconciliação
305
342
  * de um merge feito por fora (`incorporadaPor: 'outro-caminho'`) nem o ship do dono depois do arco.
@@ -443,7 +480,8 @@ function aceiteDoPiloto(carregado, arco, opcoes = {}) {
443
480
  const semLeitura = entregas.some(e => e.provada === null) || ilegiveis.length > 0;
444
481
  medida({ id: 'M2', nome: 'Entregas com prova', valor: semLeitura ? null : provadas.length, unidade: `entregas provadas de ${entregas.length} alegadas`,
445
482
  fonte: 'ship_done dos ledgers, ponta da base pelo git ls-remote e ancestralidade do merge', lacunas: lacunasDaEntrega,
446
- evidencia: { alegadas: entregas.length, entregas: entregas.map(e => ({ trabalho: e.trabalho, thread: e.thread, provada: e.provada, motivo: e.motivo })) } });
483
+ evidencia: { alegadas: entregas.length, entregas: entregas.map(e => ({ trabalho: e.trabalho, thread: e.thread, provada: e.provada, motivo: e.motivo })),
484
+ ...(multimaquina ? { porMaquina: porMaquina(maquina => (rede?.registros ?? []).filter(r => r.tipo === 'entrega' && r.por === maquina).length) } : {}) } });
447
485
  medida({ id: 'M3', nome: 'MASTER por omissão', valor: semLeitura ? null : provadas.filter(e => e.master).length, unidade: `entregas com MASTER de ${provadas.length} provadas`,
448
486
  fonte: 'aceite_por_omissao com o arco na evidência, antes do fim do arco, e a thread fechada', lacunas: semLeitura ? ['as entregas não se provaram todas'] : [],
449
487
  evidencia: { semMaster: provadas.filter(e => !e.master).map(e => e.thread) } });
@@ -456,6 +494,37 @@ function aceiteDoPiloto(carregado, arco, opcoes = {}) {
456
494
  .map(l => ({ em: l.em, fonte: `supervisão: ${l.evento.tipo}`, alvos: alvosDe(l.evento) }));
457
495
  const dosLedgers = (tipos) => eventosDasThreads.filter(e => tipos.includes(e.tipo))
458
496
  .map(e => ({ em: e.ts, fonte: `ledger da ${e.threadDoArco}: ${e.tipo}`, alvos: [e.threadDoArco, ...[...porTrabalho].filter(([, t]) => t === e.threadDoArco).map(([w]) => w)] }));
497
+ const ordensDaRede = new Map((rede?.registros ?? []).filter(r => r.tipo === 'ordem' && typeof r.ordem === 'string')
498
+ .map(r => [String(r.ordem), { maquina: String(r.maquina), trabalho: String(r.trabalho) }]));
499
+ const vezesDaRede = new Map((rede?.registros ?? []).filter(r => r.tipo === 'vez' && typeof r.chave === 'string' && typeof r.n === 'number')
500
+ .map(r => [`${String(r.chave)}/${String(r.n)}`, { maquina: String(r.por), trabalho: ordensDaRede.get(String(r.ordem))?.trabalho ?? '' }]));
501
+ const daRede = (i, falha) => {
502
+ if (!i.alvo)
503
+ return `${falha} exige o alvo (a máquina afetada) na agenda`;
504
+ if (erroDaRede)
505
+ return erroDaRede;
506
+ if (!rede)
507
+ return 'a coordenação da rede não foi lida';
508
+ if (falha === 'F10') {
509
+ return rede.registros.filter(r => r.por === i.alvo)
510
+ .map(r => ({ em: r.em, fonte: `coordenação: ${r.tipo} assinado por ${r.por}`, alvos: [i.alvo], maquina: r.por }));
511
+ }
512
+ const isoladas = rede.ordensIsoladas?.filter(o => o.maquina === i.alvo) ??
513
+ [...(rede.estado?.ordens.values() ?? [])].filter(o => o.maquina === i.alvo && o.estado === 'isolada');
514
+ if (isoladas.length)
515
+ return [];
516
+ return rede.registros.filter(r => {
517
+ if (r.por === i.alvo)
518
+ return false;
519
+ if (r.tipo === 'autoridade' && r.modo === 'forcada')
520
+ return objeto(r.anterior) && r.anterior.maquina === i.alvo;
521
+ if (r.tipo === 'cancelamento')
522
+ return ordensDaRede.get(String(r.ordem))?.maquina === i.alvo;
523
+ if (r.tipo === 'vez-fim' && r.desfecho === 'abandonada')
524
+ return vezesDaRede.get(`${String(r.chave)}/${String(r.n)}`)?.maquina === i.alvo;
525
+ return false;
526
+ }).map(r => ({ em: r.em, fonte: `coordenação: ${r.tipo} de ${i.alvo} por ${r.por}`, alvos: [i.alvo], maquina: r.por }));
527
+ };
459
528
  const SINAIS = {
460
529
  F1: () => erroDaSupervisao ?? daSupervisao(['retomada_autorizada']),
461
530
  F2: () => erroDaSupervisao ?? daSupervisao(['conducao_assumida']),
@@ -466,26 +535,33 @@ function aceiteDoPiloto(carregado, arco, opcoes = {}) {
466
535
  F7: () => erroDoRelatorio ?? confirmacoesAutomaticas.map(l => ({ em: l.em, fonte: 'relatório: envio_confirmado', alvos: [] })),
467
536
  F8: () => erroDaSupervisao ?? daSupervisao(['efeito_reconciliado']),
468
537
  F9: () => erroDaSupervisao ?? daSupervisao(['tique_concluido']),
469
- F10: () => 'a partição entre máquinas é medida com a coordenação da N3 (bloco B)',
470
- F11: () => 'a máquina perdida é medida com a coordenação da N3 (bloco B)',
538
+ F10: (i) => daRede(i, 'F10'),
539
+ F11: (i) => daRede(i, 'F11'),
471
540
  };
472
541
  const injecoes = opcoes.injecoes ?? null;
473
542
  const recuperacoes = (injecoes ?? []).map(i => {
474
- const fontes = SINAIS[i.falha]?.() ?? `falha desconhecida: ${i.falha}`;
543
+ const fontes = SINAIS[i.falha]?.(i) ?? `falha desconhecida: ${i.falha}`;
475
544
  if (typeof fontes === 'string')
476
545
  return { ...i, lacuna: fontes, sinal: null };
477
546
  const doAlvo = i.alvo ? fontes.filter(f => !f.alvos.length || f.alvos.includes(i.alvo)) : fontes;
478
547
  const sinal = sinalDe(doAlvo, Date.parse(i.instante));
479
- return { ...i, lacuna: null, sinal: sinal ? { em: sinal.em, fonte: sinal.fonte } : null };
548
+ return { ...i, lacuna: null, sinal: sinal ? { em: sinal.em, fonte: sinal.fonte, ...(sinal.maquina ? { maquina: sinal.maquina } : {}) } : null };
480
549
  });
481
550
  // A agenda vazia não prova recuperação nenhuma: é lacuna, como a agenda que falta.
482
551
  const lacunasDaAgenda = injecoes === null ? ['sem a agenda de injeções (--injecoes)']
483
552
  : !injecoes.length ? ['a agenda de injeções está vazia: nenhuma falha foi injetada']
484
553
  : recuperacoes.filter(r => r.lacuna).map(r => `${r.falha}: ${r.lacuna}`);
485
554
  const recuperadas = recuperacoes.filter(r => r.sinal);
555
+ const aguardamRecibo = (rede?.ordensIsoladas ?? [...(rede?.estado?.ordens.values() ?? [])]
556
+ .filter(o => o.estado === 'isolada').map(o => ({ maquina: o.maquina, trabalho: o.trabalho })))
557
+ .filter(o => (injecoes ?? []).some(i => i.falha === 'F11' && i.alvo === o.maquina));
558
+ const retomados = [...(rede?.estado?.trabalhos.values() ?? [])].filter(t => t.ordem && t.estado !== 'isolado' &&
559
+ (injecoes ?? []).some(i => i.falha === 'F11' && i.alvo && t.ordem.maquina !== i.alvo)).map(t => t.trabalho);
486
560
  medida({ id: 'M4', nome: 'Falhas recuperadas', valor: lacunasDaAgenda.length ? null : recuperadas.length, unidade: `recuperadas de ${recuperacoes.length} injetadas`,
487
561
  fonte: 'agenda de injeções, log da supervisão, registro do relatório e ledgers', lacunas: lacunasDaAgenda,
488
- evidencia: { injecoes: recuperacoes.map(r => ({ falha: r.falha, instante: r.instante, sinal: r.sinal, lacuna: r.lacuna })) } });
562
+ evidencia: { injecoes: recuperacoes.map(r => ({ falha: r.falha, instante: r.instante, sinal: r.sinal, lacuna: r.lacuna })),
563
+ ...(multimaquina ? { porMaquina: porMaquina(maquina => recuperadas.filter(r => r.sinal && 'maquina' in r.sinal && r.sinal.maquina === maquina).length),
564
+ retomados, aguardamRecibo } : {}) } });
489
565
  // M5 é das falhas que o plano mede pelo tempo (a sessão morta, o condutor morto e a máquina perdida); as outras, como o
490
566
  // tique parado de propósito por 20 min, contam só em M4.
491
567
  const tempos = recuperadas.filter(r => exports.FALHAS_DO_M5.includes(r.falha)).map(r => (Date.parse(r.sinal.em) - Date.parse(r.instante)) / MIN)
@@ -494,7 +570,12 @@ function aceiteDoPiloto(carregado, arco, opcoes = {}) {
494
570
  medida({ id: 'M5', nome: 'Tempo de recuperação', valor: lacunasDaAgenda.length || !tempos.length ? null : arred(tempos.at(-1)), unidade: 'min (máximo)',
495
571
  fonte: 'da injeção ao primeiro sinal de retomada no log', lacunas: lacunasDaAgenda.length ? lacunasDaAgenda : tempos.length ? []
496
572
  : [`nenhuma falha recuperada de ${exports.FALHAS_DO_M5.join(', ')} para medir`],
497
- evidencia: { mediana: mediana === null ? null : arred(mediana), maximo: tempos.length ? arred(tempos.at(-1)) : null } });
573
+ evidencia: { mediana: mediana === null ? null : arred(mediana), maximo: tempos.length ? arred(tempos.at(-1)) : null,
574
+ ...(multimaquina ? { porMaquina: porMaquina(maquina => {
575
+ const t = recuperadas.filter(r => exports.FALHAS_DO_M5.includes(r.falha) && r.sinal && 'maquina' in r.sinal && r.sinal.maquina === maquina)
576
+ .map(r => arred((Date.parse(r.sinal.em) - Date.parse(r.instante)) / MIN));
577
+ return t.length ? Math.max(...t) : null;
578
+ }) } : {}) } });
498
579
  // M6: violações do envelope.
499
580
  const contas = new Set([...conteudo.orcamento.porConta.map(c => c.conta), ...(comTrabalhos ? conteudo.capacidade.porConta.map(c => c.conta) : [])]);
500
581
  const bases = new Set(conteudo.destinos.map(d => d.base));
@@ -549,6 +630,22 @@ function aceiteDoPiloto(carregado, arco, opcoes = {}) {
549
630
  for (const [chave, n] of paginas)
550
631
  if (n > 1)
551
632
  duplicados.push(`${n} envios confirmados da ${chave}`);
633
+ const vagasSobrepostas = [];
634
+ const vagasDaRede = rede?.vagas ?? rede?.estado?.vagas ?? [];
635
+ for (let i = 0; i < vagasDaRede.length; i++) {
636
+ for (let j = i + 1; j < vagasDaRede.length; j++) {
637
+ const a = vagasDaRede[i], b = vagasDaRede[j];
638
+ if (a.trabalho !== b.trabalho || a.maquina === b.maquina)
639
+ continue;
640
+ const inicio = Math.max(Date.parse(a.desde), Date.parse(b.desde));
641
+ const fimA = a.solta ? Date.parse(a.solta.em) : Number.POSITIVE_INFINITY;
642
+ const fimB = b.solta ? Date.parse(b.solta.em) : Number.POSITIVE_INFINITY;
643
+ if (inicio < Math.min(fimA, fimB)) {
644
+ vagasSobrepostas.push({ trabalho: a.trabalho, maquinas: [a.maquina, b.maquina], vagas: [a.vaga, b.vaga] });
645
+ duplicados.push(`o trabalho ${a.trabalho} teve vagas ativas ao mesmo tempo em ${a.maquina} e ${b.maquina}`);
646
+ }
647
+ }
648
+ }
552
649
  const lacunasDaForja = [];
553
650
  const repositorio = urlDoRemoto && !remotoLocal(urlDoRemoto) ? (0, ci_1.repositorioGitHub)(urlDoRemoto) : null;
554
651
  if (repositorio) {
@@ -583,7 +680,8 @@ function aceiteDoPiloto(carregado, arco, opcoes = {}) {
583
680
  }
584
681
  const lacunasDoM7 = [...(erroDoRelatorio ? [erroDoRelatorio] : []), ...lacunasDaForja, ...ilegiveis];
585
682
  medida({ id: 'M7', nome: 'Efeitos duplicados', valor: lacunasDoM7.length && !duplicados.length ? null : duplicados.length, unidade: 'efeitos duplicados',
586
- fonte: 'ledgers (ship_done e ship_published), log do arco, registro do relatório e PRs da forja', lacunas: lacunasDoM7, evidencia: { duplicados } });
683
+ fonte: 'ledgers (ship_done e ship_published), log do arco, registro do relatório, coordenação e PRs da forja', lacunas: lacunasDoM7,
684
+ evidencia: { duplicados, ...(multimaquina ? { vagasSobrepostas, porMaquina: porMaquina(maquina => vagasSobrepostas.filter(v => v.maquinas.includes(maquina)).length) } : {}) } });
587
685
  // M8: evidência perdida.
588
686
  const perdidas = [...ilegiveis, ...(erroDaSupervisao ? [erroDaSupervisao] : []), ...(erroDoRelatorio ? [erroDoRelatorio] : [])];
589
687
  for (const e of entregas)
@@ -625,7 +723,9 @@ function aceiteDoPiloto(carregado, arco, opcoes = {}) {
625
723
  const lacunasDoTeto = tetos.filter(t => t.lacuna).map(t => `${t.escopo}: ${t.lacuna}`);
626
724
  const acima = tetos.filter(t => t.consumo !== null && t.consumo > t.teto);
627
725
  medida({ id: 'M9', nome: 'Consumo contra o teto', valor: lacunasDoTeto.length ? null : acima.length, unidade: 'escopos acima do teto',
628
- fonte: 'consumo do log do arco e a medição das unidades', lacunas: lacunasDoTeto, evidencia: { tetos } });
726
+ fonte: 'consumo do log do arco, da coordenação e a medição das unidades', lacunas: lacunasDoTeto,
727
+ evidencia: { tetos, ...(multimaquina ? { porMaquina: porMaquina(maquina => (rede?.vagas ?? rede?.estado?.vagas ?? [])
728
+ .filter(v => v.maquina === maquina && (!v.solta || v.solta.consumiu)).length) } : {}) } });
629
729
  // M10: o relatório da manhã.
630
730
  const lacunasDoRelatorio = erroDoRelatorio ? [erroDoRelatorio] : [];
631
731
  const vencimento = (0, noctum_relatorio_1.vencimentoDoRelatorio)(conteudo);
@@ -666,8 +766,39 @@ function aceiteDoPiloto(carregado, arco, opcoes = {}) {
666
766
  medida({ id: 'M10', nome: 'Relatório da manhã', valor: lacunasDoRelatorio.length ? null : falhasDoRelatorio.length ? 0 : 1, unidade: '1 no horário com recibo, 0 fora',
667
767
  fonte: 'registro do relatório do arco', lacunas: lacunasDoRelatorio,
668
768
  evidencia: { vencimento, versao1: v1 ? { compostoEm: v1.em, paginas: paginasDaV1, recibos: recibos.size } : null, falhas: falhasDoRelatorio } });
769
+ // No v3, cinco medidas são a composição dos aceites completos de todas as máquinas. A ausência de uma fonte é lacuna,
770
+ // nunca zero. As demais já trazem em `evidencia.porMaquina` a divisão derivada do ramo conferido.
771
+ const porFonte = new Map([[maquinaAtual, medidas]]);
772
+ for (const outra of outras) {
773
+ if (porFonte.has(outra.resultado.maquina)) {
774
+ throw new RecusaDoAceite('noctum.aceite.outra-maquina-invalida', `${outra.fonte}: a máquina ${outra.resultado.maquina} apareceu mais de uma vez`);
775
+ }
776
+ porFonte.set(outra.resultado.maquina, outra.resultado.medidas);
777
+ }
778
+ if (multimaquina) {
779
+ const ausentes = conteudo.maquinas.map(m => m.nome).filter(m => !porFonte.has(m));
780
+ const combinadas = new Set(['M1', 'M3', 'M6', 'M8', 'M10']);
781
+ for (const medidaLocal of medidas.filter(m => combinadas.has(m.id))) {
782
+ const valores = Object.fromEntries(conteudo.maquinas.map(({ nome }) => [nome,
783
+ porFonte.get(nome)?.find(m => m.id === medidaLocal.id)?.valor ?? null]));
784
+ medidaLocal.evidencia = { ...medidaLocal.evidencia, porMaquina: valores };
785
+ if (ausentes.length) {
786
+ medidaLocal.valor = null;
787
+ medidaLocal.lacunas.push(`sem o aceite completo da(s) máquina(s): ${ausentes.join(', ')}`);
788
+ }
789
+ else if (Object.values(valores).some(v => v === null)) {
790
+ medidaLocal.valor = null;
791
+ medidaLocal.lacunas.push('ao menos uma máquina deixou a medida como lacuna');
792
+ }
793
+ else {
794
+ const vs = Object.values(valores);
795
+ medidaLocal.valor = medidaLocal.id === 'M1' ? Math.min(...vs) : medidaLocal.id === 'M10' ? (vs.every(v => v === 1) ? 1 : 0)
796
+ : vs.reduce((a, b) => a + b, 0);
797
+ }
798
+ }
799
+ }
669
800
  // Os critérios.
670
- const proposta = limiaresDaProposta(horasDaJanela);
801
+ const proposta = limiaresDaProposta(horasDaJanela, multimaquina);
671
802
  const doDono = opcoes.limiares ?? {};
672
803
  const limiar = (k) => doDono[k] !== undefined ? { valor: doDono[k], origem: 'dono' } : { valor: proposta[k], origem: 'proposta' };
673
804
  const m = (id) => medidas.find(x => x.id === id);
@@ -700,11 +831,17 @@ function aceiteDoPiloto(carregado, arco, opcoes = {}) {
700
831
  : 'sem revogação');
701
832
  const veredito = marcas.length ? 'simulado' : criterios.some(c => c.veredito === 'reprovado') ? 'reprovado'
702
833
  : criterios.some(c => c.veredito === 'incompleto') ? 'incompleto' : 'aprovado';
834
+ const maquinasDoResultado = [
835
+ { maquina: maquinaAtual, fonte: 'estado local', sha256: sha256(JSON.stringify({ arco, maquina: maquinaAtual, medidas })), veredito },
836
+ ...outras.map(o => ({ maquina: o.resultado.maquina, fonte: o.fonte, sha256: o.sha256, veredito: o.resultado.veredito })),
837
+ ];
703
838
  return {
704
839
  contrato: exports.CONTRATO_PILOTO, arco,
705
840
  envelope: { contrato: conteudo.contrato ?? null, versao: estado.versao, sha256: estado.envelopeSha256 },
706
841
  janela: { inicio: janela.inicio, fim: janela.fim, fuso: janela.fuso, horas: arred(horasDaJanela) },
707
842
  encerramento: estado.encerramento ? { motivo: estado.encerramento.motivo, em: estado.encerramento.em } : null,
843
+ maquina: maquinaAtual,
844
+ maquinas: maquinasDoResultado,
708
845
  simulacao: { simulado: marcas.length > 0, marcas },
709
846
  medidas, criterios, veredito,
710
847
  lacunas: medidas.flatMap(x => x.lacunas.map(l => `${x.id}: ${l}`)),
@@ -756,7 +893,7 @@ function publicavel(r, sha256DoCompleto, sensiveis) {
756
893
  return x;
757
894
  };
758
895
  const andar = (v) => typeof v === 'string' ? limpar(v) : Array.isArray(v) ? v.map(andar)
759
- : objeto(v) ? Object.fromEntries(Object.entries(v).map(([k, x]) => [k, andar(x)])) : v;
896
+ : objeto(v) ? Object.fromEntries(Object.entries(v).map(([k, x]) => [limpar(k), andar(x)])) : v;
760
897
  return { ...andar(r), publicavel: true, sha256DoCompleto };
761
898
  }
762
899
  /** As contas, as máquinas e os repositórios que o arco conhece: o que o publicável troca por rótulos. */
@@ -768,6 +905,9 @@ function sensiveisDoArco(carregado, arco) {
768
905
  const repositorios = new Set();
769
906
  for (const c of conteudo?.orcamento.porConta ?? [])
770
907
  contas.add(c.conta);
908
+ if (conteudo && (0, noctum_envelope_1.envelopeMultimaquina)(conteudo))
909
+ for (const m of conteudo.maquinas)
910
+ maquinas.add(m.nome);
771
911
  for (const d of estado?.despachos ?? [])
772
912
  contas.add(d.conta);
773
913
  for (const d of conteudo?.destinos ?? []) {
@@ -41,6 +41,8 @@ exports.comandoNoctum = comandoNoctum;
41
41
  * RM-058, fatia N6 (D1, D12 e D13 da ork-rm058n6): o comando `ork noctum`, a porta do CLI para o arco.
42
42
  *
43
43
  * ork noctum propor --arquivo <envelope.json> [--json] o pedido de abertura vira proposta e pergunta ao dono; nada é autorizado
44
+ * ork noctum maquina [--json] a entrada desta máquina em `maquinas` do envelope `v3`: o nome, a
45
+ * chave pública e a impressão (RM-058, bloco B, D5)
44
46
  * ork noctum revogar <arco> [--json] a pergunta de revogação ao dono (RM-057); nada é revogado
45
47
  * ork noctum responder <arco> --resposta-stdin ... o envelope assinado do gateway (autorizar ou revogar)
46
48
  * ork noctum terminal --hook-stdin o ingresso do terminal, que o hook UserPromptSubmit chama (RM-057);
@@ -57,6 +59,7 @@ exports.comandoNoctum = comandoNoctum;
57
59
  * aqui pelo `responder`, ou o diálogo nativo do host pelo MCP). Recusa tipada sai na saída de erro, com código 2.
58
60
  */
59
61
  const fs = __importStar(require("node:fs"));
62
+ const node_crypto_1 = require("node:crypto");
60
63
  const fila_pesada_1 = require("./fila-pesada");
61
64
  const hitl_gates_1 = require("./hitl-gates");
62
65
  const hitl_public_receipt_1 = require("./hitl-public-receipt");
@@ -73,14 +76,17 @@ const noctum_modo_1 = require("./noctum-modo");
73
76
  const noctum_relatorio_1 = require("./noctum-relatorio");
74
77
  const noctum_registro_1 = require("./noctum-registro");
75
78
  const noctum_supervisao_1 = require("./noctum-supervisao");
79
+ const noctum_transporte_1 = require("./noctum-transporte");
76
80
  const noctum_vigia_1 = require("./noctum-vigia");
81
+ const maquina_1 = require("./maquina");
77
82
  const runtime_profiles_1 = require("./runtime-profiles");
78
83
  const noctum_terminal_1 = require("./noctum-terminal");
79
84
  const transporte_do_canal_1 = require("./transporte-do-canal");
80
- exports.USO_DO_NOCTUM = 'uso: ork noctum propor --arquivo <envelope.json> | revogar <arco> [--json] | responder <arco> --resposta-stdin ' +
85
+ exports.USO_DO_NOCTUM = 'uso: ork noctum propor --arquivo <envelope.json> | maquina [--json] | revogar <arco> [--json] | ' +
86
+ 'responder <arco> --resposta-stdin ' +
81
87
  '--origem O --canal C --por P --mensagem M [--conta X] | terminal --hook-stdin | status [<arco>] [--json] | tique <arco> [--json] | ' +
82
88
  'relatorio <arco> [--json] [--reenviar <canal>] | conduzir <arco> [--json] | aceite <arco> [--json] [--injecoes <arquivo>] ' +
83
- '[--limiares <arquivo>] [--publicar <arquivo>]';
89
+ '[--limiares <arquivo>] [--outra-maquina <arquivo>[,<arquivo>]] [--publicar <arquivo>]';
84
90
  /** A linha da entrega da pergunta ao canal do dono, sem caminho de máquina. */
85
91
  function linhaDaEntrega(e) {
86
92
  if (e.entregue)
@@ -115,13 +121,31 @@ function lerEntradaDoHookStdin() {
115
121
  const TETO_DO_ARQUIVO_DO_ENVELOPE = 64 * 1024;
116
122
  const texto = (v) => typeof v === 'string' ? v : undefined;
117
123
  /**
118
- * Pré-voo informativo da proposta: antecipa três causas que fariam o arco esperar, mas não autoriza nem bloqueia nada.
119
- * As dependências opcionais deixam os testes usar perfis e trust simulados sem ler contas reais.
124
+ * RM-058, bloco B (D22): no `v3`, esta máquina só entra no arco se está em `maquinas` com o nome e a chave desta raiz,
125
+ * a mesma conferência do `euNaVersaoVigente`. O aviso só antecipa a recusa: a proposta segue.
126
+ */
127
+ function avisosDaMaquina(maquinas, eu) {
128
+ const listada = maquinas.find(m => m.nome === eu.nome);
129
+ const correcao = 'rode ork noctum maquina --json nesta máquina e ponha a saída em maquinas do envelope';
130
+ if (!listada) {
131
+ return [{ motivo: 'noctum.proposta.maquina-ausente', correcao,
132
+ detalhe: `esta máquina (${eu.nome}) não está em maquinas do envelope v3: fica fora do arco, sem ordem, despacho nem entrega` }];
133
+ }
134
+ if (listada.chave === eu.chave)
135
+ return [];
136
+ return [{ motivo: 'noctum.proposta.maquina-outra-chave', correcao,
137
+ detalhe: `o envelope dá à máquina ${eu.nome} a chave de impressão ${(0, noctum_envelope_1.impressaoDaChave)(listada.chave)}, e a desta raiz é ` +
138
+ `${(0, noctum_envelope_1.impressaoDaChave)(eu.chave)}: a máquina fica fora do arco` }];
139
+ }
140
+ /**
141
+ * Pré-voo informativo da proposta: antecipa as causas que fariam o arco esperar, mas não autoriza nem bloqueia nada.
142
+ * As dependências opcionais deixam os testes usar perfis, trust e a identidade da máquina simulados sem ler contas reais.
120
143
  */
121
144
  function avisosDaProposta(carregado, conteudo, opcoes = {}) {
122
145
  if (!(0, noctum_envelope_1.envelopeComTrabalhos)(conteudo))
123
146
  return [];
124
- const avisos = [];
147
+ const avisos = (0, noctum_envelope_1.envelopeMultimaquina)(conteudo)
148
+ ? avisosDaMaquina(conteudo.maquinas, (opcoes.identidade ?? (() => (0, noctum_transporte_1.identidadeDaMaquina)(carregado.raiz)))()) : [];
125
149
  let store = null;
126
150
  try {
127
151
  store = opcoes.store ?? (0, runtime_profiles_1.lerPerfisComContas)(carregado.raiz);
@@ -130,7 +154,7 @@ function avisosDaProposta(carregado, conteudo, opcoes = {}) {
130
154
  avisos.push({ motivo: 'noctum.proposta.contas-sem-leitura', detalhe: 'as contas do envelope não puderam ser conferidas',
131
155
  correcao: 'rode ork accounts check e proponha de novo depois de corrigir a leitura' });
132
156
  }
133
- const confiar = opcoes.confianca ?? ((raiz, p) => (0, impedimento_1.impedimentoResolvido)('runtime.workspace-untrusted', { cwd: raiz, runtime: p.runtime, configDir: p.runtime === 'claude-bg' ? p.configDir : p.codexHome }));
157
+ const confiar = opcoes.confianca ?? ((raiz, p) => (0, impedimento_1.impedimentoResolvido)('runtime.workspace-untrusted', { cwd: raiz, runtime: p.runtime, configDir: (0, runtime_profiles_1.diretorioDoPerfil)(p) }));
134
158
  if (store) {
135
159
  for (const { conta } of conteudo.capacidade.porConta) {
136
160
  const p = store.perfis.find(x => x.id === conta);
@@ -267,9 +291,11 @@ function comandoNoctum(args) {
267
291
  const { raiz } = carregado;
268
292
  const quando = new Date().toISOString();
269
293
  const r = (0, noctum_arco_1.proporEnvelope)(raiz, pedido, quando, (0, pulse_resposta_1.codigosEmUso)(raiz));
270
- const conteudo = (0, noctum_arco_1.lerArco)(raiz, r.arco)?.conteudo;
271
- const fuso = conteudo?.janela.fuso ?? pedido?.janela?.fuso;
272
- const avisos = conteudo ? avisosDaProposta(carregado, conteudo) : [];
294
+ // Os avisos olham o envelope que o dono vai autorizar, já validado pelo proporEnvelope: o vigente (lerArco) só
295
+ // existe depois da autorização, e na primeira proposta os avisos sumiam (RM-058, bloco B).
296
+ const proposto = (0, noctum_envelope_1.validarPedidoDeAbertura)(pedido);
297
+ const fuso = proposto.janela.fuso;
298
+ const avisos = avisosDaProposta(carregado, proposto);
273
299
  // RM-057 (resposta curta, D4 e D9): a proposta é a pergunta ao dono, que sai também pelo canal dele quando ele tem um.
274
300
  const q = r.codigo ? (0, noctum_arco_1.perguntasDoArco)(raiz, r.arco, quando).find(x => x.codigo === r.codigo && x.situacao === 'pendente') : undefined;
275
301
  const entrega = q ? (0, noctum_pergunta_canal_1.entregarPerguntaDoArco)(raiz, q, { quando }) : null;
@@ -288,6 +314,32 @@ function comandoNoctum(args) {
288
314
  }
289
315
  return 0;
290
316
  }
317
+ if (sub === 'maquina') {
318
+ // RM-058, bloco B (B4, D5): o que o dono copia de cada máquina para `maquinas` do envelope `v3`. O nome vem antes da
319
+ // chave: o recusado não cria a chave local. A chave privada nunca sai desta raiz.
320
+ if (args.posicionais.length !== 2) {
321
+ console.error(exports.USO_DO_NOCTUM);
322
+ return 2;
323
+ }
324
+ const { raiz } = (0, manifest_1.exigirManifesto)();
325
+ const nome = (0, maquina_1.nomeDaMaquina)();
326
+ if (!noctum_transporte_1.NOME_DE_MAQUINA.test(nome)) {
327
+ console.error(`noctum.maquina.nome-invalido: o nome desta máquina ("${nome}") não serve no envelope v3: letras, dígitos, ` +
328
+ 'ponto, _ ou -, até 64, começando por letra ou dígito. Correção: grave um nome válido em ~/.orkastery/maquina.json ' +
329
+ '(o campo nome) ou defina ORK_MAQUINA em todo processo do ork desta máquina, o cron e os gateways inclusive');
330
+ return 2;
331
+ }
332
+ const { chave } = (0, noctum_transporte_1.identidadeDaMaquina)(raiz, nome);
333
+ const impressao = (0, noctum_envelope_1.impressaoDaChave)(chave);
334
+ if (json)
335
+ console.log(JSON.stringify({ nome, chave, impressao }, null, 2));
336
+ else {
337
+ console.log(['Esta máquina no envelope v3:', ` nome ${nome}`, ` chave ${chave}`, ` impressão ${impressao}`, '',
338
+ 'A entrada para maquinas do envelope:', JSON.stringify({ nome, chave }), '',
339
+ 'A chave privada não sai desta raiz; o dono confere a impressão no resumo da proposta.'].join('\n'));
340
+ }
341
+ return 0;
342
+ }
291
343
  if (sub === 'revogar') {
292
344
  // RM-057 (resposta curta, D10): abre a pergunta de revogação e a entrega; quem revoga é a resposta do dono.
293
345
  const arco = args.posicionais[2];
@@ -443,17 +495,22 @@ function comandoNoctum(args) {
443
495
  if (sub === 'aceite') {
444
496
  const arco = args.posicionais[2];
445
497
  const arquivo = (nome) => args.opcoes[nome] === undefined ? null : texto(args.opcoes[nome]) ?? false;
446
- const [injecoes, limiares, publicar] = [arquivo('injecoes'), arquivo('limiares'), arquivo('publicar')];
447
- if (!arco || args.posicionais.length !== 3 || injecoes === false || limiares === false || publicar === false) {
498
+ const [injecoes, limiares, outraMaquina, publicar] = [arquivo('injecoes'), arquivo('limiares'), arquivo('outra-maquina'), arquivo('publicar')];
499
+ if (!arco || args.posicionais.length !== 3 || injecoes === false || limiares === false || outraMaquina === false || publicar === false) {
448
500
  console.error(exports.USO_DO_NOCTUM);
449
501
  return 2;
450
502
  }
451
503
  let agenda = null, doDono = null;
504
+ const outrasMaquinas = [];
452
505
  try {
453
506
  if (injecoes)
454
507
  agenda = (0, noctum_aceite_1.lerInjecoes)(fs.readFileSync(injecoes, 'utf8'));
455
508
  if (limiares)
456
509
  doDono = (0, noctum_aceite_1.lerLimiares)(JSON.parse(fs.readFileSync(limiares, 'utf8')));
510
+ for (const fonte of (outraMaquina ? outraMaquina.split(',') : []).map(x => x.trim()).filter(Boolean)) {
511
+ const bruto = fs.readFileSync(fonte, 'utf8');
512
+ outrasMaquinas.push({ fonte, sha256: (0, node_crypto_1.createHash)('sha256').update(bruto).digest('hex'), resultado: JSON.parse(bruto) });
513
+ }
457
514
  }
458
515
  catch (e) {
459
516
  if (e instanceof noctum_aceite_1.RecusaDoAceite)
@@ -463,7 +520,7 @@ function comandoNoctum(args) {
463
520
  }
464
521
  const carregado = (0, manifest_1.exigirManifesto)();
465
522
  // RM-058, integração (T8, D12): o arquivo completo fica no estado; o publicável vai aonde o dono pedir.
466
- const r = (0, noctum_aceite_1.aceiteDoPiloto)(carregado, arco, { injecoes: agenda, limiares: doDono });
523
+ const r = (0, noctum_aceite_1.aceiteDoPiloto)(carregado, arco, { injecoes: agenda, limiares: doDono, outrasMaquinas });
467
524
  const gravado = (0, noctum_aceite_1.gravarAceite)(carregado.raiz, r);
468
525
  if (publicar)
469
526
  fs.writeFileSync(publicar, `${JSON.stringify((0, noctum_aceite_1.publicavel)(r, gravado.sha256, (0, noctum_aceite_1.sensiveisDoArco)(carregado, arco)), null, 2)}\n`);
@@ -97,6 +97,8 @@ const ship_publicar_1 = require("./ship-publicar");
97
97
  const thread_1 = require("./thread");
98
98
  const verify_1 = require("./verify");
99
99
  const worktree_1 = require("./worktree");
100
+ const noctum_integracao_1 = require("./noctum-integracao");
101
+ const noctum_rede_1 = require("./noctum-rede");
100
102
  exports.CONTRATO_DA_ENTREGA = 'ork.noctum-entrega/v1';
101
103
  /** D2: quantas vezes o arco despacha o CHECK independente de um trabalho antes de bloquear. */
102
104
  exports.LIMITE_DE_REVISOES = 4;
@@ -374,8 +376,15 @@ function passoDaEntrega(carregado, arco, opcoes = {}) {
374
376
  }
375
377
  if (sup.parada)
376
378
  return { acao: 'nada', motivo: 'arco-encerrado', detalhe: `a supervisão parou o arco (${sup.parada.motivo}): nenhum efeito novo` };
379
+ const multimaquina = (0, noctum_envelope_1.envelopeMultimaquina)(conteudo);
380
+ const rede = multimaquina ? opcoes.projecaoDaRede ?? (0, noctum_rede_1.lerRede)(carregado, arco, { quando, maquina: opcoes.maquina, remoto, rede: opcoes.redeDaCoordenacao }) : null;
381
+ if (rede?.lacuna) {
382
+ return { acao: 'aguardar', trabalho: null, thread: null, motivo: rede.lacuna.motivo, detalhe: rede.lacuna.detalhe,
383
+ ate: depoisDe(quando, exports.ESPERA_DO_CI_MS) };
384
+ }
385
+ const coordenacao = rede ? (0, noctum_rede_1.coordenacaoDaFila)(rede) : null;
377
386
  const fila = (0, noctum_fila_1.montarFila)((0, noctum_fila_1.fatosDaFila)(carregado, arco, { quando, maquina: opcoes.maquina, lerReservas: opcoes.lerReservas,
378
- supervisao: decisoesDaEntrega(sup) }));
387
+ ...(coordenacao ? { coordenacao } : {}), supervisao: decisoesDaEntrega(sup) }));
379
388
  const naEntrega = fila.trabalhos.filter(t => t.thread && (t.estado === 'aguardando-entrega' || t.estado === 'entregue'))
380
389
  .sort((a, b) => a.prioridade - b.prioridade || a.posicao - b.posicao);
381
390
  if (!naEntrega.length)
@@ -392,11 +401,14 @@ function passoDaEntrega(carregado, arco, opcoes = {}) {
392
401
  detalhe: `o trabalho ${trabalho.id} foi isolado pela supervisão (${isoladoDepois.motivo}): ${isoladoDepois.detalhe}` });
393
402
  continue;
394
403
  }
395
- // D9: com outro contrato (a v3 da N3), a integração é da vez entre máquinas; aqui nada se decide por ela.
396
- if (conteudo.contrato !== noctum_envelope_1.CONTRATO_ENVELOPE_V2) {
397
- bloqueios.push({ trabalho: trabalho.id, thread: trabalho.thread, motivo: 'noctum.entrega.exige-vez-da-n3',
398
- detalhe: `o envelope ${conteudo.contrato} integra pela vez entre máquinas (integrarComVez da N3), com integradorDaEntrega` });
399
- continue;
404
+ if (multimaquina && rede?.trabalhos.find(t => t.id === trabalho.id)?.estado === 'entregue') {
405
+ let entregueAqui = false;
406
+ try {
407
+ entregueAqui = (0, ledger_1.lerLedger)((0, thread_1.dirThread)(carregado.raiz, trabalho.thread)).some(e => e.tipo === ledger_1.TIPOS_DE_EVENTO.shipConcluido);
408
+ }
409
+ catch { /* a leitura normal abaixo dará a lacuna se este trabalho ainda couber aqui */ }
410
+ if (!entregueAqui)
411
+ continue;
400
412
  }
401
413
  let lido;
402
414
  try {
@@ -421,7 +433,41 @@ function passoDaEntrega(carregado, arco, opcoes = {}) {
421
433
  bloqueios.push({ ...ids, motivo: decisao.motivo, detalhe: decisao.porque });
422
434
  continue;
423
435
  }
424
- const { resultado, efeito } = executar(carregado, arco, estado, trabalho, lido, decisao.acao, opcoes, remoto, quando);
436
+ let executado;
437
+ if (multimaquina && decisao.acao === 'integrar') {
438
+ const ordem = coordenacao?.ordens?.get(trabalho.id);
439
+ const candidato = lido.entrada.provas ? candidatoDaEntrega(lido.entrada.provas) : null;
440
+ if (!ordem || !candidato || !lido.destino) {
441
+ bloqueios.push({ trabalho: trabalho.id, thread: trabalho.thread, motivo: 'noctum.coordenacao.ordem-vencida',
442
+ detalhe: `o trabalho ${trabalho.id} não tem ordem aceita desta máquina e candidato provado para a vez` });
443
+ continue;
444
+ }
445
+ try {
446
+ const feita = (opcoes.integrarPelaRede ?? noctum_integracao_1.integrarComVez)(carregado, arco, { ordem: ordem.ordem,
447
+ destino: { repositorio: lido.destino.repositorio, base: lido.destino.base }, candidato }, integradorDaEntrega(carregado, arco, lido.t.id, opcoes), { quando, maquina: opcoes.maquina, remoto, rede: opcoes.redeDaCoordenacao });
448
+ executado = { resultado: { acao: 'integrou', trabalho: trabalho.id, thread: lido.t.id, mergeSha: feita.merge,
449
+ pontaDaBase: feita.merge, docs: null, provas: candidato.provas }, efeito: true };
450
+ }
451
+ catch (e) {
452
+ if (!(e instanceof noctum_envelope_1.RecusaDoArco))
453
+ throw e;
454
+ const ids = { trabalho: trabalho.id, thread: lido.t.id };
455
+ if (['noctum.integracao.ja-entregue', 'noctum.coordenacao.ordem-vencida'].includes(e.motivo)) {
456
+ executado = { resultado: { acao: 'nada', motivo: 'sem-entrega', detalhe: e.message }, efeito: false };
457
+ }
458
+ else if (e.motivo === 'noctum.integracao.base-nao-provada') {
459
+ executado = { resultado: { acao: 'bloqueado', ...ids, motivo: e.motivo, detalhe: e.message, outros: [] }, efeito: false };
460
+ }
461
+ else {
462
+ executado = { resultado: { acao: 'aguardar', ...ids, motivo: e.motivo, detalhe: e.message,
463
+ ate: depoisDe(quando, exports.ESPERA_DO_CI_MS) }, efeito: false };
464
+ }
465
+ }
466
+ }
467
+ else {
468
+ executado = executar(carregado, arco, estado, trabalho, lido, decisao.acao, opcoes, remoto, quando);
469
+ }
470
+ const { resultado, efeito } = executado;
425
471
  // Um efeito por chamada (A1 da rodada 1): o efeito feito, ou de resultado incerto, encerra o passo; a recusa de antes de
426
472
  // qualquer efeito deixa o trabalho seguinte tentar, e o trabalho travado não prende a fila.
427
473
  if (efeito)
@@ -434,6 +480,8 @@ function passoDaEntrega(carregado, arco, opcoes = {}) {
434
480
  bloqueios.push({ trabalho: resultado.trabalho, thread: resultado.thread, motivo: resultado.motivo, detalhe: resultado.detalhe });
435
481
  continue;
436
482
  }
483
+ if (resultado.acao === 'nada')
484
+ continue;
437
485
  return resultado;
438
486
  }
439
487
  if (espera)
@@ -377,7 +377,7 @@ function lerOrcamento(v, p) {
377
377
  chavesConhecidas(v, ['arco', 'porConta', 'porRuntime'], 'orcamento', p);
378
378
  const arco = lerTeto(v.arco, 'orcamento.arco', p);
379
379
  const porConta = lerSubtetos(v.porConta, 'conta', 'orcamento.porConta', x => ID_DE_CONTA.test(x), 'id de perfil de conta já autenticado', p);
380
- const porRuntime = lerSubtetos(v.porRuntime, 'runtime', 'orcamento.porRuntime', x => runtime_profiles_1.RUNTIMES_COM_PERFIL.includes(x), `runtime de assinatura: ${runtime_profiles_1.RUNTIMES_COM_PERFIL.join(', ')}`, p);
380
+ const porRuntime = lerSubtetos(v.porRuntime, 'runtime', 'orcamento.porRuntime', x => runtime_profiles_1.RUNTIMES_COM_INVENTARIO.includes(x), `runtime de assinatura: ${runtime_profiles_1.RUNTIMES_COM_INVENTARIO.join(', ')}`, p);
381
381
  // Subteto acima do teto do arco, na mesma unidade, não limita nada: é teto do arco disfarçado.
382
382
  for (const [nome, teto] of [...(porConta ?? []).map(s => [`conta ${s.conta}`, s.teto]),
383
383
  ...(porRuntime ?? []).map(s => [`runtime ${s.runtime}`, s.teto])]) {
@@ -326,7 +326,11 @@ function despacharCandidato(carregado, arco, fila, c, opcoes) {
326
326
  if (!c.pedido)
327
327
  return { ...recusar('noctum.despacho.pedido-ausente', `o trabalho ${c.trabalho} não traz pedido para abrir a thread`, null), seguir: true };
328
328
  // RM-037 (CHECK 3 da ork-rm037defeit2): sob a trava de despacho, o preparo das dependências só fica pendente.
329
- thread = (opcoes.abrirThread ?? ((k, o) => (0, thread_1.novaThread)(k, o).thread))(carregado, { nome: `${c.item.toLowerCase()} ${c.trabalho}`,
329
+ const base = `${c.item.toLowerCase()} ${c.trabalho}`;
330
+ const numero = opcoes.coordenacao?.ordens?.get(c.trabalho)?.numero;
331
+ const sufixo = numero === undefined ? '' : ` o${numero}`;
332
+ const nome = sufixo ? `${base.slice(0, Math.max(1, 12 - sufixo.length)).trimEnd()}${sufixo}` : base;
333
+ thread = (opcoes.abrirThread ?? ((k, o) => (0, thread_1.novaThread)(k, o).thread))(carregado, { nome,
330
334
  modo: 'auto', roadmap: c.item, pedido: c.pedido, criarWorktree: true, adiarDependencias: true });
331
335
  }
332
336
  pedido = (0, thread_1.pedidoDaThread)(raiz, thread);