@orkastery/cli 0.6.1 → 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 (69) 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 +3 -1
  25. package/dist/adapters/grok-mcp-servidor.js +22 -12
  26. package/dist/adapters/grok.js +1 -1
  27. package/dist/changelog-check.js +217 -0
  28. package/dist/check-agil.js +241 -5
  29. package/dist/ci.js +3 -2
  30. package/dist/claim-lint.js +29 -0
  31. package/dist/claims.js +4 -0
  32. package/dist/docs.js +7 -0
  33. package/dist/fila-pesada.js +121 -11
  34. package/dist/hitl-classificacao.js +1 -0
  35. package/dist/index.js +43 -25
  36. package/dist/intelligence-graph-cli.js +27 -9
  37. package/dist/locale-claims.js +16 -2
  38. package/dist/locale-grafo.js +56 -1
  39. package/dist/locale-maestro.js +38 -0
  40. package/dist/locale-network.js +8 -6
  41. package/dist/locale-onboarding.js +14 -0
  42. package/dist/locale-preflight.js +80 -0
  43. package/dist/locale-projetos.js +6 -0
  44. package/dist/locale-prompts.js +8 -8
  45. package/dist/locale-pulse.js +4 -0
  46. package/dist/locale-retry.js +16 -0
  47. package/dist/locale-security-scan.js +35 -1
  48. package/dist/locale-verify.js +15 -0
  49. package/dist/locale.js +24 -7
  50. package/dist/maestro-cli.js +12 -17
  51. package/dist/mcp-install.js +2 -1
  52. package/dist/mcp-server.js +20 -17
  53. package/dist/mcp-verify-async.js +353 -0
  54. package/dist/mcp-verify-worker.js +17 -0
  55. package/dist/mcp-verify.js +10 -1
  56. package/dist/pedido-conducao.js +51 -0
  57. package/dist/preflight.js +25 -26
  58. package/dist/prompts.js +4 -2
  59. package/dist/pulse.js +4 -2
  60. package/dist/rede.js +24 -12
  61. package/dist/retry.js +12 -0
  62. package/dist/security-scan/cli.js +2 -2
  63. package/dist/security-scan/report.js +1 -1
  64. package/dist/session-watcher.js +33 -7
  65. package/dist/verify.js +166 -34
  66. package/package.json +1 -1
  67. package/skills/core/orkastery-bootstrap/SKILL.md +1 -1
  68. package/skills/phases/check-quality/SKILL.md +1 -1
  69. package/skills/phases/go-implementation/SKILL.md +1 -1
package/dist/rede.js CHANGED
@@ -73,7 +73,7 @@ exports.painelDaRede = painelDaRede;
73
73
  * fora de qualquer clone de projeto.
74
74
  *
75
75
  * D3: publicar exige o repositorio privado, conferido na forja antes de cada publicacao.
76
- * D5: o retrato e uma LISTA DE PERMISSAO (maquina, hostname, forjas, runtimes, hosts, projetos,
76
+ * D5: o retrato e uma LISTA DE PERMISSAO (maquina, alias legado, forjas, runtimes, hosts, projetos,
77
77
  * versao do `ork`, batida) e passa por uma varredura de segredo antes do push: um achado recusa a
78
78
  * publicacao inteira, e o erro diz o padrao e o campo, nunca o valor.
79
79
  * D6: cada maquina so escreve o proprio retrato; o `REDE.md` e indice derivado que quem publica
@@ -191,6 +191,17 @@ function hostnameSeguro() {
191
191
  const bruto = os.hostname().trim();
192
192
  return /^[A-Za-z0-9][A-Za-z0-9._-]{0,252}$/.test(bruto) ? bruto : bruto.replace(/[^A-Za-z0-9._-]+/g, '-').slice(0, 253) || 'desconhecido';
193
193
  }
194
+ /**
195
+ * RM-062 F3: o nome que sai da maquina precisa ter sido escolhido localmente. O fallback historico
196
+ * para `os.hostname()` transformava um detalhe privado em nome de arquivo e em JSON. A configuracao
197
+ * fica em `~/.orkastery/maquina.json`; `--maquina` e `ORK_MAQUINA` cobrem a primeira entrada.
198
+ */
199
+ function nomeGenericoDaRede(explicito) {
200
+ const nome = (explicito ?? '').trim() || (0, maquina_1.lerConfigDaMaquina)()?.nome || (process.env.ORK_MAQUINA ?? '').trim();
201
+ if (!nome)
202
+ throw new locale_1.ErroDoCatalogo((m) => m.network.nomeGenericoAusente);
203
+ return nomeSeguro(nome);
204
+ }
194
205
  /** O retrato desta maquina, montado so com os campos da lista de permissao (D5). */
195
206
  function retratoDaMaquina(opcoes = {}) {
196
207
  return retratoComDescartes(opcoes).retrato;
@@ -199,7 +210,7 @@ function retratoDaMaquina(opcoes = {}) {
199
210
  * O retrato e os projetos que ficaram fora dele. Projeto vem de fora do nucleo (o registro da
200
211
  * RM-052, o cwd, o ultimo retrato): um valor dele com cara de segredo tira SO aquele projeto, com
201
212
  * aviso, em vez de travar a publicacao da maquina para sempre (A1 do CHECK 1). O que o nucleo monta
202
- * (maquina, hostname, forjas, runtimes, hosts) continua falhando fechado em `exigirRetratoSeguro`.
213
+ * (maquina, alias legado, forjas, runtimes, hosts) continua falhando fechado em `exigirRetratoSeguro`.
203
214
  */
204
215
  function retratoComDescartes(opcoes = {}) {
205
216
  const amb = opcoes.amb ?? {};
@@ -248,11 +259,12 @@ function retratoComDescartes(opcoes = {}) {
248
259
  descartados.push({ campo: `projetos[${exports.MAXIMO_DE_ITENS}..]`, padrao: `limite de ${exports.MAXIMO_DE_ITENS} projetos` });
249
260
  limpos.length = exports.MAXIMO_DE_ITENS;
250
261
  }
262
+ const maquina = nomeGenericoDaRede(opcoes.maquina);
251
263
  const retrato = {
252
264
  contrato: exports.CONTRATO_DO_RETRATO,
253
- maquina: nomeSeguro((0, maquina_1.nomeDaMaquina)(opcoes.maquina)),
265
+ maquina,
254
266
  ...(opcoes.id ? { id: opcoes.id } : {}),
255
- hostname: hostnameSeguro(),
267
+ hostname: maquina,
256
268
  adesao: opcoes.adesao ?? (0, rede_adesao_1.adesaoDaRede)().adesao ?? 'rede',
257
269
  forjas: identidades.map((i) => ({ forja: i.forja, host: i.host, cli: i.cli, versao: i.versao, usuario: i.usuario })),
258
270
  runtimes,
@@ -785,10 +797,9 @@ function gravarNaCasa(conferida, retrato, descartados, opcoes) {
785
797
  const idDoOutro = atual ? atual.id ?? null : idNoArquivo(cache, ponta, arquivoDoRetrato(maquina));
786
798
  const alheio = !!idDoOutro && idDoOutro !== retrato.id;
787
799
  if (alheio && !opcoes.tomarNome) {
788
- // U7 da revisao 3: o hostname igual sugere a propria maquina, mas duas VMs podem se chamar `ubuntu`.
789
- const mesmoHostname = atual?.hostname === hostnameSeguro();
800
+ // O campo legado `hostname` agora repete o alias generico e nao distingue instalacoes.
790
801
  const outro = atual ? { hostname: atual.hostname, publicadoEm: atual.publicadoEm } : null;
791
- throw new locale_1.ErroDoCatalogo((m) => m.network.nomeEmUso(maquina, outro ? m.network.quemHostname(String(outro.hostname), (0, horario_1.formatarDataHora)(outro.publicadoEm)) : m.network.quemIlegivel, mesmoHostname ? m.network.dicaMesmoHostname : m.network.dicaOutroNome));
802
+ throw new locale_1.ErroDoCatalogo((m) => m.network.nomeEmUso(maquina, outro ? m.network.quemHostname(String(outro.hostname), (0, horario_1.formatarDataHora)(outro.publicadoEm)) : m.network.quemIlegivel, m.network.dicaNomeEmUso));
792
803
  }
793
804
  const mudancas = [
794
805
  { caminho: arquivoDoRetrato(maquina), conteudo: JSON.stringify(retrato, null, 2) + '\n' },
@@ -819,7 +830,7 @@ function publicarRede(opcoes = {}) {
819
830
  if (!adesao.membro)
820
831
  throw new locale_1.ErroDoCatalogo((m) => m.network.foraDaRede);
821
832
  const amb = opcoes.amb ?? {};
822
- const maquina = nomeSeguro((0, maquina_1.nomeDaMaquina)(opcoes.maquina));
833
+ const maquina = nomeGenericoDaRede(opcoes.maquina);
823
834
  const id = (0, rede_adesao_1.idDaMaquina)();
824
835
  const agora = opcoes.agora ?? new Date().toISOString();
825
836
  const marca = lerMarcaDaRede();
@@ -860,12 +871,13 @@ const NOME_DE_MAQUINA = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
860
871
  * `ork network entrar`. M2 do CHECK 1: atomico do ponto de vista da maquina. Confere (e cria,
861
872
  * privada) a casa, publica o primeiro retrato e SO ENTAO grava o nome e a adesao; se a publicacao
862
873
  * falha, nada muda aqui. M3: o nome que vale e gravado, como no `ork fabrica entrar`: `--maquina`,
863
- * senao o ja gravado, senao `ORK_MAQUINA` ou o hostname; assim o cron publica com o mesmo nome.
874
+ * senao o ja gravado, senao `ORK_MAQUINA`; assim o cron publica com o mesmo nome. RM-062 F3:
875
+ * hostname nunca e fallback, porque nome de arquivo e JSON sao evidencia publicada.
864
876
  */
865
877
  function entrarNaRede(opcoes = {}) {
866
- // O nome explicito tem de ser valido; o que vem do arquivo, de ORK_MAQUINA ou do hostname e saneado.
878
+ // O nome explicito tem de ser valido; o que vem do arquivo ou de ORK_MAQUINA e saneado.
867
879
  const explicito = (opcoes.maquina ?? '').trim();
868
- const maquina = explicito || nomeSeguro((0, maquina_1.lerConfigDaMaquina)()?.nome || (0, maquina_1.nomeDaMaquina)());
880
+ const maquina = explicito || nomeGenericoDaRede();
869
881
  if (!NOME_DE_MAQUINA.test(maquina)) {
870
882
  throw new locale_1.ErroDoCatalogo((m) => m.network.nomeInvalido(maquina));
871
883
  }
@@ -896,7 +908,7 @@ function entrarNaRede(opcoes = {}) {
896
908
  function sairDaRede(opcoes = {}) {
897
909
  // U3 da revisao 3: a saida local vem primeiro; nada depois dela pode deixar a maquina membro.
898
910
  (0, rede_adesao_1.gravarConfigDaRede)({ membro: false });
899
- const maquina = nomeSeguro((0, maquina_1.nomeDaMaquina)(opcoes.maquina));
911
+ const maquina = nomeGenericoDaRede(opcoes.maquina);
900
912
  const r = resolverCasa(opcoes);
901
913
  if (r.casa)
902
914
  (0, rede_adesao_1.gravarConfigDaRede)({ forja: r.casa.forja, host: r.casa.host, dono: r.casa.dono, repositorio: r.casa.repositorio });
package/dist/retry.js CHANGED
@@ -288,6 +288,12 @@ exports.POLITICA_DE_RETRY = {
288
288
  automatica: true,
289
289
  ...TEXTOS_DA_POLITICA['lease.busy'],
290
290
  },
291
+ 'concurrency.lease-indeterminado': {
292
+ motivo: 'concurrency.lease-indeterminado',
293
+ acao: 'esperar-janela',
294
+ automatica: false,
295
+ ...TEXTOS_DA_POLITICA['concurrency.lease-indeterminado'],
296
+ },
291
297
  // I-36 (T6): a vez chega quando a conducao atual terminar. Redespachar agora so recebe a mesma recusa,
292
298
  // e passar na frente de quem conduz e exatamente o incidente de 19/09/2026.
293
299
  'conducao.em-andamento': {
@@ -296,6 +302,12 @@ exports.POLITICA_DE_RETRY = {
296
302
  automatica: true,
297
303
  ...TEXTOS_DA_POLITICA['conducao.em-andamento'],
298
304
  },
305
+ 'conducao.acao-necessaria': {
306
+ motivo: 'conducao.acao-necessaria',
307
+ acao: 'esperar-janela',
308
+ automatica: false,
309
+ ...TEXTOS_DA_POLITICA['conducao.acao-necessaria'],
310
+ },
299
311
  // I-41 (D10): formato e defeito de quem escreveu o pedido, e a spec dirigida conserta.
300
312
  // Escalar isto para o dono seria pedir que ele revise a sintaxe do robo.
301
313
  'hitl.formato': {
@@ -55,7 +55,7 @@ function executarSecurityScan(argv, ctx) {
55
55
  const message = e.message;
56
56
  if (!/^security-scan\.(config-invalid|dismissal-invalid)/.test(message))
57
57
  throw e;
58
- console.error(message);
58
+ console.error(json ? message : m.error(message));
59
59
  return 1;
60
60
  }
61
61
  const files = (0, report_1.writeReport)(ctx.estado, report);
@@ -68,7 +68,7 @@ function executarSecurityScan(argv, ctx) {
68
68
  if (notRun.length) {
69
69
  ctx.escrever(m.notRunHeading);
70
70
  for (const r of notRun)
71
- ctx.escrever(m.notRun(r.name, m.states[r.state] ?? r.state, r.reason ?? ''));
71
+ ctx.escrever(m.notRun(r.name, m.states[r.state] ?? r.state, m.reason(r.reason ?? '')));
72
72
  }
73
73
  ctx.escrever(files.md);
74
74
  }
@@ -143,7 +143,7 @@ function renderMarkdown(report) {
143
143
  else {
144
144
  out.push(`| ${m.tableTool} | ${m.tableExecutor} | ${m.tableState} | ${m.tableReason} |`, '| --- | --- | --- | --- |');
145
145
  for (const r of report.tools) {
146
- out.push(`| ${cell(r.name)} | ${r.executor ?? m.noExecutor} | ${m.states[r.state] ?? r.state} | ${cell(r.reason ?? '')} |`);
146
+ out.push(`| ${cell(r.name)} | ${r.executor ?? m.noExecutor} | ${m.states[r.state] ?? r.state} | ${cell(m.reason(r.reason ?? ''))} |`);
147
147
  }
148
148
  }
149
149
  const news = report.findings.filter(f => f.status === 'new');
@@ -61,6 +61,17 @@ const session_watcher_claude_1 = require("./session-watcher-claude");
61
61
  const session_watcher_grok_1 = require("./session-watcher-grok");
62
62
  const conta_esgotada_1 = require("./conta-esgotada");
63
63
  const trabalho_thread_1 = require("./trabalho-thread");
64
+ const pedido_conducao_1 = require("./pedido-conducao");
65
+ /** O gate estreito da própria sessão vence diagnósticos genéricos do watcher. */
66
+ function pedidoDaConducao(eventos, sessionId, despachoEm) {
67
+ const evento = eventos.filter(e => e.tipo === 'gate_blocked' && e.origem === 'mcp.request_conduction' &&
68
+ e.sessionId === sessionId && e.despachoEm === despachoEm && typeof e.motivo === 'string' &&
69
+ pedido_conducao_1.MOTIVOS_DE_PEDIDO_A_CONDUCAO.includes(e.motivo) &&
70
+ typeof e.comando === 'string' && typeof e.despacho === 'string').at(-1);
71
+ return evento ? { motivo: evento.motivo, comando: evento.comando,
72
+ detalhe: typeof evento.detalhe === 'string' ? evento.detalhe : 'sessão solicitou ação da condução',
73
+ despacho: evento.despacho } : null;
74
+ }
64
75
  /** Falhas operacionais e bloqueios sem retry automático não alegam conclusão do produto. */
65
76
  function bloqueioPreservado(terminal) {
66
77
  return terminal.classificacao === 'gate_blocked' && terminal.motivo !== null &&
@@ -474,18 +485,25 @@ function reconciliarSessaoSemWatcher(carregado, threadId, agoraMs = Date.now())
474
485
  if (atuais.some(e => e.tipo === 'phase_result' && e.sessionId === sessao.sessionId &&
475
486
  e.despachoEm === sessao.despachadaEm))
476
487
  return;
488
+ const pedido = pedidoDaConducao(atuais, sessao.sessionId, sessao.despachadaEm);
477
489
  const dados = { fase: sessao.fase, sessionId: sessao.sessionId, despachoEm: sessao.despachadaEm,
478
- sensorResultId, classificacao: 'gate_blocked', motivo: 'runtime.unavailable', runtime: sessao.runtime,
479
- fonte: 'conducao.batida: sessão verificada sem watcher e sem evento útil', fonteVencedora: null,
490
+ sensorResultId, classificacao: 'gate_blocked', motivo: pedido?.motivo ?? 'runtime.unavailable', runtime: sessao.runtime,
491
+ fonte: pedido ? 'mcp.request_conduction: parada intencional tipada pela sessão'
492
+ : 'conducao.batida: sessão verificada sem watcher e sem evento útil', fonteVencedora: null,
480
493
  ok: false, conclusaoNativa: false, conclusaoNativaAusente: 'watcher ausente e fonte indisponível além do limite',
481
494
  estado: 'bloqueada', observadoEm: new Date(agoraMs).toISOString(), gate: 'phase.dispatch',
482
- origem: 'conducao.batida', morteLatenciaMs: agoraMs - ultimoUtil };
483
- (0, ledger_1.registrar)(dir, threadId, 'sessao_morta', dados);
495
+ origem: 'conducao.batida', morteLatenciaMs: agoraMs - ultimoUtil,
496
+ ...(pedido ? { detalhe: pedido.detalhe, correcao: pedido.comando, comando: pedido.comando,
497
+ pedidoConducao: { contrato: 'ork.pedido-conducao/v1', despacho: pedido.despacho, estado: 'aberto' } } : {}) };
498
+ if (!pedido)
499
+ (0, ledger_1.registrar)(dir, threadId, 'sessao_morta', dados);
484
500
  (0, ledger_1.registrar)(dir, threadId, 'gate_blocked', dados);
485
501
  (0, ledger_1.registrar)(dir, threadId, 'phase_result', dados);
486
502
  });
487
503
  return { thread: threadId, sessionId: sessao.sessionId, estado: 'parada',
488
- detalhe: 'sessão verificada sem watcher e sem evento útil além do limite' };
504
+ detalhe: pedidoDaConducao((0, ledger_1.lerLedger)(dir), sessao.sessionId, sessao.despachadaEm)
505
+ ? 'sessão parou com pedido tipado para a condução'
506
+ : 'sessão verificada sem watcher e sem evento útil além do limite' };
489
507
  }
490
508
  }
491
509
  function observarSessaoResolvida(carregado, sessionId, { thread, sessao: registrada }, opcoes) {
@@ -714,6 +732,7 @@ function observarSessaoResolvida(carregado, sessionId, { thread, sessao: registr
714
732
  }
715
733
  if (!podeTerminar)
716
734
  return { ...base, bytesLidos };
735
+ const pedido = pedidoDaConducao(atuais, sessionId, sessao.despachadaEm);
717
736
  const semTerminal = !terminal;
718
737
  // Fato registrado para a autoridade de revisão: `null` só com terminal nativo correlacionado.
719
738
  const conclusaoNativa = incompleto || invalido || aberto || descorrelacionado
@@ -726,6 +745,11 @@ function observarSessaoResolvida(carregado, sessionId, { thread, sessao: registr
726
745
  terminal = { classificacao: 'gate_blocked', motivo: 'runtime.silencio',
727
746
  timestamp: new Date(now).toISOString(), turnId: null, fonte: 'processo ausente e arquivos estáveis',
728
747
  tokens: new codex_events_1.ParserCodex().estado.tokens, duracaoMs: null };
748
+ if (pedido && (terminal.classificacao === 'fase_concluida' ||
749
+ terminal.classificacao === 'gate_blocked' && ['runtime.unavailable', 'runtime.silencio'].includes(terminal.motivo ?? ''))) {
750
+ terminal = { ...terminal, classificacao: 'gate_blocked', motivo: pedido.motivo,
751
+ fonte: `mcp.request_conduction: ${pedido.detalhe}` };
752
+ }
729
753
  if (fechamento && (fechamento.exitCode !== 0 || fechamento.signal || fechamento.erro) &&
730
754
  terminal.classificacao !== 'human.pending' && !bloqueioPreservado(terminal)) {
731
755
  terminal = { ...terminal, classificacao: 'gate_blocked', motivo: 'runtime.unavailable' };
@@ -739,7 +763,7 @@ function observarSessaoResolvida(carregado, sessionId, { thread, sessao: registr
739
763
  ? (0, trabalho_thread_1.gravarRespostaFinalDaSessao)(carregado.raiz, thread.id, sessionId, terminal.respostaFinal) : null;
740
764
  const dados = { fase: sessao.fase, sessionId, despachoEm: sessao.despachadaEm, sensorResultId: resultadoId,
741
765
  classificacao: terminal.classificacao, motivo: terminal.motivo, runtime: 'codex', fonte: terminal.fonte,
742
- fonteVencedora: incompleto || invalido || aberto || descorrelacionado ? null : terminais[0]?.nome ?? null,
766
+ fonteVencedora: pedido || incompleto || invalido || aberto || descorrelacionado ? null : terminais[0]?.nome ?? null,
743
767
  turnId: terminal.turnId, numeroTurno: terminal.numeroTurno ?? null,
744
768
  tokens: terminal.tokens, exitCode: fechamento?.exitCode ?? null, exitCodeFonte: fechamento?.fonte ?? 'unavailable',
745
769
  signal: fechamento?.signal ?? null, duracaoMs: fechamento?.duracaoMs ?? terminal.duracaoMs,
@@ -750,11 +774,13 @@ function observarSessaoResolvida(carregado, sessionId, { thread, sessao: registr
750
774
  estado: terminal.classificacao === 'fase_concluida' ? 'concluida' : 'bloqueada',
751
775
  observadoEm: terminal.timestamp, gate: 'phase.dispatch', origem: 'sessions.watch',
752
776
  morteLatenciaMs: morte ? now - cursor.progressoEm : null, ...(perfil ? { perfil } : {}),
777
+ ...(pedido ? { detalhe: pedido.detalhe, correcao: pedido.comando, comando: pedido.comando,
778
+ pedidoConducao: { contrato: 'ork.pedido-conducao/v1', despacho: pedido.despacho, estado: 'aberto' } } : {}),
753
779
  ...(respostaFinal ? { respostaFinal: { arquivo: (0, trabalho_thread_1.caminhoDoTrabalho)(thread.id) + respostaFinal.arquivo,
754
780
  sha256: respostaFinal.sha256, bytes: respostaFinal.bytes, truncada: respostaFinal.truncada } } : {}),
755
781
  ...(conta ? { falhaDeConta: { motivo: conta.falha.motivo, resetEm: conta.falha.resetEm, fonte: conta.falha.fonte,
756
782
  trecho: conta.falha.trecho } } : {}) };
757
- const tipo = semTerminal && morte && !incompleto && !conta ? 'sessao_morta' : terminal.classificacao;
783
+ const tipo = semTerminal && morte && !incompleto && !conta && !pedido ? 'sessao_morta' : terminal.classificacao;
758
784
  // I-33 (D12): o perfil sai do rodizio na primeira gravacao do resultado, onde a evidencia esta.
759
785
  if (conta && perfil && !atuais.some(e => e.tipo === 'phase_result' && e.sensorResultId === resultadoId)) {
760
786
  try {
package/dist/verify.js CHANGED
@@ -78,6 +78,7 @@ const thread_1 = require("./thread");
78
78
  const estado_thread_1 = require("./estado-thread");
79
79
  const dependencias_da_worktree_1 = require("./dependencias-da-worktree");
80
80
  const fs = __importStar(require("node:fs"));
81
+ const os = __importStar(require("node:os"));
81
82
  const node_fs_1 = require("node:fs");
82
83
  const path = __importStar(require("node:path"));
83
84
  const node_path_1 = require("node:path");
@@ -98,6 +99,7 @@ const check_modo_1 = require("./check-modo");
98
99
  const check_agil_1 = require("./check-agil");
99
100
  const check_paralelo_1 = require("./check-paralelo");
100
101
  const agendador_cpu_1 = require("./agendador-cpu");
102
+ const changelog_check_1 = require("./changelog-check");
101
103
  /** Teto de tempo de um comando de verificacao (build e suite completa cabem). */
102
104
  exports.TIMEOUT_VERIFY_MS = 10 * 60 * 1000;
103
105
  /** Ultimas linhas da saida real, o suficiente para servir de evidencia no ledger. */
@@ -302,6 +304,7 @@ function comandoNoLedger(c) {
302
304
  // RM-072 (D2): a duracao vai tambem no comando que passa, para a meta do CHECK ser medida pelo ledger. RM-072 (D4, D5):
303
305
  // no Agile, o uso do preparo e as lanes; o Classic nao tem esses campos.
304
306
  const doAgile = { ...(c.preparo ? { preparo: c.preparo } : {}),
307
+ ...(c.lane ? { lane: c.lane } : {}),
305
308
  ...(c.lanes ? { lanes: c.lanes.map((l) => ({ ...l, comando: (0, redacao_url_1.redigirCredenciaisUrl)(l.comando) })) } : {}),
306
309
  ...(c.comandoExecutado ? { comandoExecutado: (0, redacao_url_1.redigirCredenciaisUrl)(c.comandoExecutado) } : {}) };
307
310
  if (c.ok)
@@ -327,6 +330,19 @@ function comandosDoManifesto(manifesto) {
327
330
  }
328
331
  return lista;
329
332
  }
333
+ /** A regra estrutural do CHANGELOG entra no mesmo veredito e na mesma baseline dos comandos do manifesto. */
334
+ function verificarChangelog(cwd, base, baseCarimbada) {
335
+ if (!(0, changelog_check_1.changelogAplicavel)(cwd))
336
+ return null;
337
+ const inicio = Date.now();
338
+ const comando = `node core/scripts/checar-changelog.cjs --base "${base}"`;
339
+ const r = (0, changelog_check_1.checarChangelog)(cwd, { base, head: 'HEAD', baseCarimbada });
340
+ return {
341
+ nome: 'changelog', comando, ok: r.ok, code: r.ok ? 0 : r.motivo === 'changelog.base-indisponivel' ? 2 : 1,
342
+ resumo: r.ok ? r.detalhe : `${r.motivo}: ${r.detalhe}\ncorrecao: ${r.correcao ?? 'corrija o CHANGELOG'}`,
343
+ causa: 'exit', prazoMs: 0, duracaoMs: Date.now() - inicio, executado: true,
344
+ };
345
+ }
330
346
  /**
331
347
  * Diretorio onde a verificacao roda: a worktree da thread, ou a raiz do projeto. RM-047 (P2): a worktree
332
348
  * do `thread.json` so vale registrada no `git worktree list` e com o estado local (`estado.rastreado`,
@@ -464,12 +480,15 @@ function agileNoLedger(raiz, threadId, commit, pecasDoVerify, doAgile, guarda, r
464
480
  relatorio.fechar();
465
481
  const doRelatorio = (0, check_agil_1.pecasDoRelatorio)(linhas);
466
482
  const listas = (0, check_agil_1.listasDasLanes)(linhas);
483
+ const resultados = (0, check_agil_1.resultadosDasLanes)(linhas);
467
484
  const usos = claims.flatMap((c) => c.execucoes.map((e) => e.preparo).filter((u) => !!u));
485
+ const usosDasLanes = claims.flatMap((c) => c.execucoes.filter((e) => e.lane));
468
486
  const resumo = {
469
487
  pecas: [...pecasDoVerify, ...doRelatorio],
470
488
  grupo: { paralelo: doAgile?.paralelo ?? false, lanes: doAgile?.lanes ?? [] },
471
489
  produto: guarda.hashCurto(),
472
- claims: { reaproveitadas: usos.filter((u) => u.reaproveitado).length, inteiras: usos.filter((u) => !u.reaproveitado).length },
490
+ claims: { reaproveitadas: usos.filter((u) => u.reaproveitado).length, inteiras: usos.filter((u) => !u.reaproveitado).length,
491
+ lanes: usosDasLanes.length },
473
492
  lanes: listas.map((l) => ({ lane: l.lane, arquivos: l.arquivos.length, sha256: l.sha256 })),
474
493
  orcamento: linhas.filter((l) => l.tipo === 'duracoes').map(({ tipo: _tipo, ...resto }) => resto),
475
494
  };
@@ -478,7 +497,7 @@ function agileNoLedger(raiz, threadId, commit, pecasDoVerify, doAgile, guarda, r
478
497
  const arquivo = path.join((0, thread_1.dirThread)(raiz, threadId), 'check', `${commit.slice(0, 12)}-lanes.json`);
479
498
  try {
480
499
  fs.mkdirSync(path.dirname(arquivo), { recursive: true, mode: 0o700 });
481
- fs.writeFileSync(arquivo, JSON.stringify({ commit, lanes: listas.map((l) => ({ lane: l.lane, arquivos: l.arquivos })) }, null, 1) + '\n', { mode: 0o600 });
500
+ fs.writeFileSync(arquivo, JSON.stringify({ commit, lanes: listas.map((l) => ({ lane: l.lane, arquivos: l.arquivos })), resultados }, null, 1) + '\n', { mode: 0o600 });
482
501
  resumo.evidencia = path.relative((0, estado_thread_1.raizDoEstado)(raiz), arquivo);
483
502
  }
484
503
  catch { /* sem o arquivo, o ledger ainda guarda a contagem e o sha256 de cada lane */ }
@@ -529,7 +548,7 @@ function exigirDependenciasDaWorktree(carregado, threadId, cwd, operacao, conduc
529
548
  }
530
549
  /** `ork verify --baseline`: grava o estado do mundo ANTES do GO. Sem `executor`, o da rodada (`executorDaRodada`). */
531
550
  function gravarBaseline(carregado, threadId, executor, conducao = {}, origem = 'cli', leitor = steal_1.lerProcStat, modo) {
532
- const pedido = pedidoDoVerify(carregado, threadId, 'baseline', comandosDoManifesto(carregado.manifesto).length, conducao);
551
+ const pedido = pedidoDoVerify(carregado, threadId, 'baseline', comandosDoManifesto(carregado.manifesto).length + 1, conducao);
533
552
  return (0, conducao_1.comConducao)(carregado.raiz, threadId, pedido, (tomada) => naFilaPesada(carregado, threadId, 'baseline', conducao, () => gravarBaselineSobConducao(carregado, threadId, executor, tomada, origem, leitor, modo)));
534
553
  }
535
554
  function gravarBaselineSobConducao(carregado, threadId, base, tomada, origem, leitor, modo) {
@@ -551,7 +570,7 @@ function gravarBaselineSobConducao(carregado, threadId, base, tomada, origem, le
551
570
  // seria reaproveitado e os comandos rodam como no Classic.
552
571
  // RM-072 (A6 do CHECK): as pecas do preparo, do grupo e do relatorio vao ao `baseline_recorded`, como no `verify_run`.
553
572
  const pecasDaBaseline = [];
554
- const comandos = relatorio && manifesto.check?.agile?.produto?.length
573
+ const comandosDoManifestoExecutados = relatorio && manifesto.check?.agile?.produto?.length
555
574
  ? (() => {
556
575
  const p = (0, check_agil_1.prepararNoAgile)(manifesto, cwd, executor);
557
576
  pecasDaBaseline.push(...p.pecas);
@@ -560,6 +579,8 @@ function gravarBaselineSobConducao(carregado, threadId, base, tomada, origem, le
560
579
  return r.comandos;
561
580
  })()
562
581
  : comandosDoManifesto(manifesto).map((c) => executorDosComandos(c.nome, c.comando, cwd));
582
+ const changelog = verificarChangelog(cwd, manifesto.worktree.base_branch, thread.base?.commit);
583
+ const comandos = changelog ? [...comandosDoManifestoExecutados, changelog] : comandosDoManifestoExecutados;
563
584
  if (relatorio) {
564
585
  if (base)
565
586
  pecasDaBaseline.push(check_agil_1.PECA_SEM_RELATORIO);
@@ -582,7 +603,9 @@ function gravarBaselineSobConducao(carregado, threadId, base, tomada, origem, le
582
603
  commit: baseline.commit,
583
604
  cwd,
584
605
  comandos: comandos.map((c) => ({ nome: c.nome, comando: c.comando, ok: c.ok, code: c.code, prazoMs: c.prazoMs ?? null,
585
- ...(c.duracaoMs !== undefined ? { duracaoMs: c.duracaoMs } : {}), ...(c.ok ? {} : { causa: c.causa ?? null }) })),
606
+ ...(c.duracaoMs !== undefined ? { duracaoMs: c.duracaoMs } : {}), ...(c.ok ? {} : { causa: c.causa ?? null }),
607
+ ...(c.testeQueCaiu?.length ? { testeQueCaiu: c.testeQueCaiu } : {}),
608
+ ...(c.lanes?.length ? { lanes: c.lanes.map((l) => ({ ...l, comando: (0, redacao_url_1.redigirCredenciaisUrl)(l.comando) })) } : {}) })),
586
609
  check: checkNoLedger(check),
587
610
  ...(pecasDaBaseline.length ? { agile: { pecas: pecasDaBaseline } } : {}),
588
611
  // RM-037 (defeito 1 de 19 e 20/10/2026): o que a baseline nao provou fica dito no proprio evento.
@@ -592,6 +615,38 @@ function gravarBaselineSobConducao(carregado, threadId, base, tomada, origem, le
592
615
  });
593
616
  return baseline;
594
617
  }
618
+ function nomesUnicos(nomes) {
619
+ return [...new Set(nomes ?? [])];
620
+ }
621
+ /** Compara os conjuntos que o runner realmente nomeou; sucesso prova que o conjunto atual e vazio. */
622
+ function compararComBaseline(atual, antes) {
623
+ if (atual.ok && antes.ok)
624
+ return null;
625
+ const nomesAntes = nomesUnicos(antes.testeQueCaiu);
626
+ const nomesAtuais = nomesUnicos(atual.testeQueCaiu);
627
+ const baselineNominal = antes.ok || nomesAntes.length > 0;
628
+ const rodadaNominal = atual.ok || nomesAtuais.length > 0;
629
+ if (baselineNominal && rodadaNominal) {
630
+ const antesSet = new Set(nomesAntes);
631
+ const atualSet = new Set(nomesAtuais);
632
+ return {
633
+ comando: atual.nome,
634
+ granularidade: 'teste',
635
+ regressoes: nomesAtuais.filter((nome) => !antesSet.has(nome)),
636
+ preExistentes: nomesAtuais.filter((nome) => antesSet.has(nome)),
637
+ corrigidos: nomesAntes.filter((nome) => !atualSet.has(nome)),
638
+ };
639
+ }
640
+ return {
641
+ comando: atual.nome,
642
+ granularidade: 'comando',
643
+ regressoes: [],
644
+ preExistentes: [],
645
+ corrigidos: [],
646
+ classificacaoDoComando: atual.ok ? 'corrigido' : antes.ok ? 'regressao' : 'pre-existente',
647
+ motivoDoFallback: baselineNominal ? 'rodada-sem-nomes' : 'baseline-sem-nomes',
648
+ };
649
+ }
595
650
  /**
596
651
  * Reexecuta a claim no HEAD real e devolve o veredito com o motivo tipado.
597
652
  *
@@ -656,6 +711,7 @@ function verificarSobConducao(carregado, threadId, opcoes, conducao, canal, fila
656
711
  const inicioDaRodada = Date.now();
657
712
  const thread = (0, thread_1.lerThread)(raiz, threadId);
658
713
  const cwd = cwdDaThread(raiz, thread);
714
+ opcoes.progresso?.('preflight');
659
715
  exigirDependenciasDaWorktree(carregado, threadId, cwd, 'verify', conducao, opcoes.executor);
660
716
  const commit = commitReal(cwd);
661
717
  const dir = (0, thread_1.dirThread)(raiz, threadId);
@@ -673,14 +729,26 @@ function verificarSobConducao(carregado, threadId, opcoes, conducao, canal, fila
673
729
  // RM-072 (D8): no Agile, as pecas contam o que rodaram num relatorio da rodada, lido no fim.
674
730
  const agil = check.modo === 'agile';
675
731
  const relatorio = agil ? new check_agil_1.RelatorioDoCheck() : null;
676
- const ambienteDaRodada = { ...ambienteDoModo(check), ...relatorio?.ambiente() };
732
+ const casaDaRodada = agil && !(process.env.ORKASTERY_HOME || '').trim()
733
+ ? fs.mkdtempSync(path.join(os.tmpdir(), 'ork-verify-casa-')) : null;
734
+ const ambienteFuncional = { ...ambienteDoModo(check), ...(casaDaRodada ? { ORKASTERY_HOME: casaDaRodada } : {}) };
735
+ const ambienteDaRodada = { ...ambienteFuncional, ...relatorio?.ambiente() };
677
736
  // I-37 (D2, D3): o prazo de cada comando sai do manifesto carregado no inicio da rodada;
678
737
  // editar o manifesto no meio dela nao muda o teto de quem ainda vai rodar.
679
- const executor = executorSobConducao(executorDaRodada(manifesto, instabilidade, opcoes.executor, leitor, ambienteDaRodada), manifesto, conducao);
738
+ const observar = (base) => opcoes.progresso
739
+ ? (nome, comando, dir) => { opcoes.progresso(nome); return base(nome, comando, dir); }
740
+ : base;
741
+ const executor = executorSobConducao(observar(executorDaRodada(manifesto, instabilidade, opcoes.executor, leitor, ambienteDaRodada)), manifesto, conducao);
680
742
  // RM-072 (A3 do CHECK): as claims recebem o modo, nunca o relatorio da rodada; o teste que roda a varredura dentro de uma
681
743
  // claim gravaria pecas de fixture no relatorio do verify. O relatorio fica com o preparo e o grupo.
682
744
  const executorDasClaims = relatorio
683
- ? executorSobConducao(executorDaRodada(manifesto, instabilidade, opcoes.executor, leitor, ambienteDoModo(check)), manifesto, conducao)
745
+ ? executorSobConducao(observar(executorDaRodada(manifesto, instabilidade, opcoes.executor, leitor, ambienteFuncional)), manifesto, conducao)
746
+ : executor;
747
+ // Uma claim `node --test` elegivel usa o mesmo ambiente funcional das lanes: casa compartilhada, sem o modo da
748
+ // orquestracao. Assim o fallback da claim e a prova reaproveitada observam exatamente a mesma bateria.
749
+ const ambienteFuncionalDasLanes = { ...(casaDaRodada ? { ORKASTERY_HOME: casaDaRodada } : {}) };
750
+ const executorDasClaimsComoLane = relatorio
751
+ ? executorSobConducao(observar(executorDaRodada(manifesto, instabilidade, opcoes.executor, leitor, ambienteFuncionalDasLanes)), manifesto, conducao)
684
752
  : executor;
685
753
  // RM-072 (S-b do CHECK 2): fora do preparo, so a varredura e as lanes recebem o relatorio.
686
754
  const executorDosComandos = relatorio ? soAVarreduraEAsLanesComRelatorio(executor, executorDasClaims) : executor;
@@ -713,15 +781,34 @@ function verificarSobConducao(carregado, threadId, opcoes, conducao, canal, fila
713
781
  pecas.push(...(0, check_agil_1.refazerPreparo)(manifesto, cwd, executor, vigente, (p) => guardaDoPreparo(manifesto, cwd, p), quando));
714
782
  };
715
783
  let preparosRefeitos = 0;
784
+ let lanesVigentes = true;
716
785
  const refazerAntesDaClaim = (nome) => {
717
786
  if (!vigente || preparosRefeitos >= check_agil_1.MAXIMO_DE_PREPAROS_REFEITOS)
718
787
  return false;
719
788
  preparosRefeitos++;
789
+ lanesVigentes = false;
720
790
  refazer(`antes da claim ${nome}`);
721
791
  return true;
722
792
  };
793
+ // RM-074 M1: no Agile, as lanes rodam antes das claims para seus resultados nominais poderem provar `node --test`.
794
+ // Classic conserva a ordem anterior. O build continua vindo do preparo desta mesma rodada.
795
+ let doAgile = null;
796
+ const grupoAntesDasClaims = !opcoes.soClaims && !!vigente && vigente.guarda.conferir() === null
797
+ && Object.keys(manifesto.check?.agile?.test ?? {}).length > 0;
798
+ let comandosDoManifestoExecutados = grupoAntesDasClaims
799
+ ? (opcoes.progresso?.('grupo-agile'), doAgile = (0, check_agil_1.comandosNoAgile)(manifesto, cwd, comandosDoManifesto(manifesto), executorDosComandos, executorDoGrupo(manifesto, ambienteDaRodada, instabilidade, leitor, conducao, opcoes.executor ? executor : undefined, opcoes.medicaoCpuAgendador), vigente.guarda, vigente.preparo)).comandos
800
+ : [];
801
+ if (doAgile)
802
+ pecas.push(...doAgile.pecas);
803
+ const evidenciasDasLanes = relatorio && doAgile ? (0, check_agil_1.resultadosDasLanes)(relatorio.ler()) : [];
804
+ const ambienteEfetivoDasClaims = { ...ambienteDoVerify(), ...ambienteFuncionalDasLanes };
805
+ const executorComLanes = evidenciasDasLanes.length > 0
806
+ ? (0, check_agil_1.executorDaClaimPelasLanes)(executorDasClaims, evidenciasDasLanes, { head: commit, ambiente: ambienteEfetivoDasClaims,
807
+ podeReaproveitar: () => lanesVigentes && vigente?.guarda.conferir() === null,
808
+ executorEquivalente: executorDasClaimsComoLane })
809
+ : executorDasClaims;
723
810
  // Claim retirada sai do gate: o historico guarda a alegacao e o motivo da retirada.
724
- const executorDaClaim = vigente ? (0, check_agil_1.executorDaClaimNoAgile)(manifesto, executorDasClaims, vigente, refazerAntesDaClaim) : executorDasClaims;
811
+ const executorDaClaim = vigente ? (0, check_agil_1.executorDaClaimNoAgile)(manifesto, executorComLanes, vigente, refazerAntesDaClaim) : executorComLanes;
725
812
  const claims = (0, claims_1.lerClaims)(raiz, threadId)
726
813
  .filter((c) => c.estado !== 'retirada')
727
814
  .map((c) => verificarClaim(c, cwd, executorDaClaim));
@@ -737,20 +824,33 @@ function verificarSobConducao(carregado, threadId, opcoes, conducao, canal, fila
737
824
  if (r.claim.estado !== estado)
738
825
  (0, claims_1.carimbarEstado)(raiz, r.claim, estado);
739
826
  }
740
- // RM-072 (A1 do CHECK): o produto que mudou durante as claims tem o preparo refeito antes do grupo, que segue em paralelo.
741
- // S-h do CHECK 3: o grupo tem a vaga dele, fora do teto das claims.
742
- if (vigente && !opcoes.soClaims && vigente.guarda.conferir() === 'produto-mudou')
743
- refazer('antes do grupo');
744
- // RM-072 (D4, D6): no Agile, build do preparo e o resto no grupo paralelo, com a guarda valendo.
745
- let doAgile = null;
746
- const comandos = opcoes.soClaims
747
- ? []
748
- : vigente
749
- ? (doAgile = (0, check_agil_1.comandosNoAgile)(manifesto, cwd, comandosDoManifesto(manifesto), executorDosComandos, executorDoGrupo(manifesto, ambienteDaRodada, instabilidade, leitor, conducao, opcoes.executor ? executor : undefined, opcoes.medicaoCpuAgendador), vigente.guarda, vigente.preparo)).comandos
750
- : comandosDoManifesto(manifesto).map((c) => executor(c.nome, c.comando, cwd));
751
- if (doAgile)
827
+ // Claim nao reaproveitada pode recompilar e mudar o produto depois das lanes. Nesse caso o preparo e o grupo rodam
828
+ // novamente sobre o produto final; na rodada comum, as claims de teste vieram das lanes e este caminho nao custa nada.
829
+ const guardaDepoisDasClaims = grupoAntesDasClaims ? vigente?.guarda.conferir() : null;
830
+ if (grupoAntesDasClaims && guardaDepoisDasClaims) {
831
+ if (guardaDepoisDasClaims === 'produto-mudou')
832
+ refazer('depois das claims');
833
+ opcoes.progresso?.('grupo-agile');
834
+ doAgile = (0, check_agil_1.comandosNoAgile)(manifesto, cwd, comandosDoManifesto(manifesto), executorDosComandos, executorDoGrupo(manifesto, ambienteDaRodada, instabilidade, leitor, conducao, opcoes.executor ? executor : undefined, opcoes.medicaoCpuAgendador), vigente.guarda, vigente.preparo);
835
+ comandosDoManifestoExecutados = doAgile.comandos;
752
836
  pecas.push(...doAgile.pecas);
837
+ }
838
+ if (!opcoes.soClaims && !grupoAntesDasClaims) {
839
+ comandosDoManifestoExecutados = vigente
840
+ ? (opcoes.progresso?.('grupo-agile'), doAgile = (0, check_agil_1.comandosNoAgile)(manifesto, cwd, comandosDoManifesto(manifesto), executorDosComandos, executorDoGrupo(manifesto, ambienteDaRodada, instabilidade, leitor, conducao, opcoes.executor ? executor : undefined, opcoes.medicaoCpuAgendador), vigente.guarda, vigente.preparo)).comandos
841
+ : comandosDoManifesto(manifesto).map((c) => executorDosComandos(c.nome, c.comando, cwd));
842
+ if (doAgile)
843
+ pecas.push(...doAgile.pecas);
844
+ }
845
+ const changelog = opcoes.soClaims ? null
846
+ : verificarChangelog(cwd, manifesto.worktree.base_branch, thread.base?.commit);
847
+ const comandos = opcoes.soClaims ? []
848
+ : changelog ? [...comandosDoManifestoExecutados, changelog] : comandosDoManifestoExecutados;
753
849
  const resumoDoAgile = vigente ? agileNoLedger(raiz, threadId, commit, pecas, doAgile, vigente.guarda, relatorio, claims) : null;
850
+ if (relatorio && !vigente)
851
+ relatorio.fechar();
852
+ if (casaDaRodada)
853
+ fs.rmSync(casaDaRodada, { recursive: true, force: true });
754
854
  // RM-062 (F2): a barreira de dado privado no diff da thread, so com a policy `dado_privado` no manifesto.
755
855
  const dadoPrivado = (0, dado_privado_1.barreiraDaThread)(carregado, thread, cwd, 'verify');
756
856
  const dadoPrivadoBloqueia = (0, policies_1.bloqueantes)(dadoPrivado?.violacoes ?? []);
@@ -758,23 +858,41 @@ function verificarSobConducao(carregado, threadId, opcoes, conducao, canal, fila
758
858
  const regressoes = [];
759
859
  const preExistentes = [];
760
860
  const falhasSemBaseline = [];
861
+ const comparacoes = [];
761
862
  const estouros = [];
762
863
  // RM-037 (defeito 1 de 19 e 20/10/2026): o comando sem veredito na baseline nao separa regressao de divida.
763
864
  const baselineSemVeredito = (baseline?.comandos ?? []).filter((c) => vereditoDaBaseline(c) === 'sem-veredito').map((c) => c.nome);
764
865
  for (const atual of comandos) {
765
- if (atual.ok)
866
+ // I-54 (D11): comando que nao rodou nao tem resultado para comparar. O motivo tipado entra abaixo como
867
+ // `verify.sem-veredito`, sem tambem inventar regressao ou divida a partir da ausencia de nomes.
868
+ if (atual.executado === false)
766
869
  continue;
767
- if (semVereditoPorTempo(atual)) {
870
+ if (!atual.ok && semVereditoPorTempo(atual)) {
768
871
  estouros.push(atual);
769
872
  continue;
770
873
  }
771
874
  const antes = baseline?.comandos.find((c) => c.nome === atual.nome);
772
- if (!antes || vereditoDaBaseline(antes) === 'sem-veredito')
773
- falhasSemBaseline.push(atual);
774
- else if (antes.ok)
875
+ if (!antes || vereditoDaBaseline(antes) === 'sem-veredito') {
876
+ if (!atual.ok)
877
+ falhasSemBaseline.push(atual);
878
+ continue;
879
+ }
880
+ const comparacao = compararComBaseline(atual, antes);
881
+ if (!comparacao)
882
+ continue;
883
+ comparacoes.push(comparacao);
884
+ if (comparacao.granularidade === 'teste') {
885
+ if (comparacao.regressoes.length > 0)
886
+ regressoes.push(atual);
887
+ if (comparacao.preExistentes.length > 0)
888
+ preExistentes.push(atual);
889
+ }
890
+ else if (comparacao.classificacaoDoComando === 'regressao') {
775
891
  regressoes.push(atual);
776
- else
892
+ }
893
+ else if (comparacao.classificacaoDoComando === 'pre-existente') {
777
894
  preExistentes.push(atual);
895
+ }
778
896
  }
779
897
  // I-54 (D11): o produto mudou entre o preparo e o fim da rodada? Entao nenhum veredito vale.
780
898
  const identidadeFinal = preparo ? identidadeDoProduto(cwd) : null;
@@ -823,6 +941,7 @@ function verificarSobConducao(carregado, threadId, opcoes, conducao, canal, fila
823
941
  duracaoMs: c.execucoes.reduce((t, e) => t + (e.duracaoMs ?? 0), 0),
824
942
  // RM-072 (D5): no Agile, o que a guarda fez com cada comando da claim.
825
943
  ...(c.execucoes.some((e) => e.preparo) ? { preparo: c.execucoes.map((e) => e.preparo ?? null) } : {}),
944
+ ...(c.execucoes.some((e) => e.lane) ? { lane: c.execucoes.map((e) => e.lane ?? null) } : {}),
826
945
  ...(falhou ? { execucao: comandoNoLedger(falhou) } : {}),
827
946
  };
828
947
  }),
@@ -831,6 +950,7 @@ function verificarSobConducao(carregado, threadId, opcoes, conducao, canal, fila
831
950
  regressoes: regressoes.map((r) => r.nome),
832
951
  preExistentes: preExistentes.map((r) => r.nome),
833
952
  falhasSemBaseline: falhasSemBaseline.map((r) => r.nome),
953
+ comparacoes,
834
954
  ...(baselineSemVeredito.length > 0 ? { baselineSemVeredito } : {}),
835
955
  estouros: estouros.map((r) => r.nome),
836
956
  prazoMs: manifesto.verify.timeout_ms ?? exports.TIMEOUT_VERIFY_MS,
@@ -871,7 +991,9 @@ function verificarSobConducao(carregado, threadId, opcoes, conducao, canal, fila
871
991
  .map((c) => `${c.claim.id}: ${c.detalhe}`)
872
992
  .join('; ')
873
993
  : motivo === 'verify.regression'
874
- ? `regressao em: ${regressoes.map((r) => r.nome).join(', ')}`
994
+ ? `regressao em: ${comparacoes.flatMap((c) => c.granularidade === 'teste'
995
+ ? c.regressoes.map((teste) => `${c.comando}: ${teste}`)
996
+ : c.classificacaoDoComando === 'regressao' ? [c.comando] : []).join(', ')}`
875
997
  : motivo === 'verify.timeout'
876
998
  ? `sem veredito por tempo (prazo estourado, ou teste com relogio sob steal acima de ${steal_1.LIMIAR_STEAL_PCT}%): ${[
877
999
  ...estouros.map((r) => (0, check_agil_1.porQueDasLanes)(r) ? `${r.nome} (${(0, check_agil_1.porQueDasLanes)(r)})` : r.nome),
@@ -914,6 +1036,7 @@ function verificarSobConducao(carregado, threadId, opcoes, conducao, canal, fila
914
1036
  regressoes,
915
1037
  preExistentes,
916
1038
  falhasSemBaseline,
1039
+ comparacoes,
917
1040
  estouros,
918
1041
  preparo: preparo ? { ...preparo, identidadeFinal: identidadeFinal, produtoAlterado } : null,
919
1042
  steal,
@@ -1017,13 +1140,22 @@ function textoDoVerify(r) {
1017
1140
  linhas.push('');
1018
1141
  linhas.push(m.semVeredito(r.estouros.map((c) => c.nome).join(', ')));
1019
1142
  }
1020
- if (r.regressoes.length > 0 || r.preExistentes.length > 0 || r.falhasSemBaseline.length > 0) {
1143
+ if (r.comparacoes.length > 0 || r.falhasSemBaseline.length > 0) {
1021
1144
  linhas.push('');
1022
- linhas.push(m.classificacao(r.regressoes.length, r.preExistentes.length, r.falhasSemBaseline.length));
1023
- for (const c of r.regressoes)
1024
- linhas.push(m.regressao(c.nome, c.comando));
1025
- for (const c of r.preExistentes)
1026
- linhas.push(m.preExistente(c.nome, c.comando));
1145
+ for (const c of r.comparacoes) {
1146
+ if (c.granularidade === 'teste') {
1147
+ linhas.push(m.comparacaoPorTeste(c.comando, c.regressoes.length, c.preExistentes.length, c.corrigidos.length));
1148
+ for (const teste of c.regressoes)
1149
+ linhas.push(m.testeRegressao(teste));
1150
+ for (const teste of c.preExistentes)
1151
+ linhas.push(m.testePreExistente(teste));
1152
+ for (const teste of c.corrigidos)
1153
+ linhas.push(m.testeCorrigido(teste));
1154
+ }
1155
+ else {
1156
+ linhas.push(m.comparacaoPorComando(c.comando, c.classificacaoDoComando, c.motivoDoFallback));
1157
+ }
1158
+ }
1027
1159
  // RM-037 (defeito 1 de 19 e 20/10/2026): a baseline existe, mas nao teve veredito deste comando.
1028
1160
  const semVeredito = new Set((r.baseline?.comandos ?? []).filter((c) => vereditoDaBaseline(c) === 'sem-veredito').map((c) => c.nome));
1029
1161
  for (const c of r.falhasSemBaseline) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orkastery/cli",
3
- "version": "0.6.1",
3
+ "version": "0.6.2",
4
4
  "description": "A software factory of AI agents that proves its work: parallel threads, verified results, and short decisions only when they matter.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/orkastery/orkastery#readme",