@orkastery/cli 0.6.3 → 0.6.5

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.
package/dist/verify.js CHANGED
@@ -56,15 +56,23 @@ exports.executorDoManifesto = executorDoManifesto;
56
56
  exports.executar = executar;
57
57
  exports.resultadoDoProcesso = resultadoDoProcesso;
58
58
  exports.semVereditoPorTempo = semVereditoPorTempo;
59
+ exports.resultadoComSteal = resultadoComSteal;
59
60
  exports.executorComSteal = executorComSteal;
61
+ exports.executorComJUnit = executorComJUnit;
60
62
  exports.identidadeDoProduto = identidadeDoProduto;
61
63
  exports.causaDoEncerramento = causaDoEncerramento;
62
64
  exports.comandoNoLedger = comandoNoLedger;
65
+ exports.filtroDoComandoNoManifesto = filtroDoComandoNoManifesto;
63
66
  exports.comandosDoManifesto = comandosDoManifesto;
67
+ exports.comandoComTestesFiltrados = comandoComTestesFiltrados;
68
+ exports.executarComFiltro = executarComFiltro;
69
+ exports.executarPassagemParcial = executarPassagemParcial;
70
+ exports.selecaoDaRodadaAnterior = selecaoDaRodadaAnterior;
64
71
  exports.cwdDaThread = cwdDaThread;
65
72
  exports.commitReal = commitReal;
66
73
  exports.executorDaRodada = executorDaRodada;
67
74
  exports.ambienteDoModo = ambienteDoModo;
75
+ exports.pecaDoAgendadorCpu = pecaDoAgendadorCpu;
68
76
  exports.vereditoDaBaseline = vereditoDaBaseline;
69
77
  exports.gravarBaseline = gravarBaseline;
70
78
  exports.verificarClaim = verificarClaim;
@@ -100,6 +108,8 @@ const check_agil_1 = require("./check-agil");
100
108
  const check_paralelo_1 = require("./check-paralelo");
101
109
  const agendador_cpu_1 = require("./agendador-cpu");
102
110
  const changelog_check_1 = require("./changelog-check");
111
+ const junit_1 = require("./junit");
112
+ const check_selecao_1 = require("./check-selecao");
103
113
  /** Teto de tempo de um comando de verificacao (build e suite completa cabem). */
104
114
  exports.TIMEOUT_VERIFY_MS = 10 * 60 * 1000;
105
115
  /** Ultimas linhas da saida real, o suficiente para servir de evidencia no ledger. */
@@ -125,7 +135,7 @@ exports.PREFIXO_BLOQUEADO_NO_VERIFY = 'ORK_HITL_';
125
135
  * rodada (o verify que roda dentro de um comando de outro verify), elas levariam pecas ao relatorio errado.
126
136
  */
127
137
  const VARIAVEIS_DA_RODADA = new Set([
128
- check_modo_1.ENV_RELATORIO_DO_CHECK, check_modo_1.ENV_VARREDURA_PARALELA_DO_CHECK, agendador_cpu_1.ENV_CPUS_DA_LANE, agendador_cpu_1.ENV_CPUS_DOCKER,
138
+ check_modo_1.ENV_RELATORIO_DO_CHECK, check_modo_1.ENV_VARREDURA_PARALELA_DO_CHECK, agendador_cpu_1.ENV_CPUS_DA_LANE, agendador_cpu_1.ENV_CPUS_DOCKER, agendador_cpu_1.ENV_WORKERS,
129
139
  ]);
130
140
  function ambienteDoVerify(base = process.env) {
131
141
  return Object.fromEntries(Object.entries(base).filter(([chave]) => !chave.startsWith(exports.PREFIXO_BLOQUEADO_NO_VERIFY)
@@ -234,23 +244,43 @@ function resultadoDoProcesso(nome, comando, r, comGrupo, duracaoMs, tetoMs) {
234
244
  function semVereditoPorTempo(c) {
235
245
  return c.causa === 'timeout' || (c.relogioSobSteal?.length ?? 0) > 0;
236
246
  }
237
- /**
238
- * RM-037 (fatia 6): mede o steal na janela de cada comando e marca a reprovacao que foi so de teste com
239
- * relogio sob steal alto. Os testes de relogio sao os da assinatura de prazo do runner e os do registro
240
- * de instabilidade vigente. Comando que passou, estourou ou nem rodou so ganha a medida.
241
- */
242
- function executorComSteal(base, leitor = steal_1.lerProcStat, registro = new Set()) {
247
+ function reciboDoReteste(testes, resultado) {
248
+ if (!resultado)
249
+ return { testes: [...testes], estado: 'sem-filtro' };
250
+ if (resultado.executado === false || resultado.causa === 'timeout') {
251
+ return { testes: [...testes], estado: 'sem-veredito', code: resultado.code };
252
+ }
253
+ return { testes: [...testes], estado: resultado.ok ? 'passou' : 'reprovou', code: resultado.code };
254
+ }
255
+ /** Aplica a medida a um resultado ja enriquecido pelo JUnit e, quando cabivel, registra o reteste isolado. */
256
+ function resultadoComSteal(r, stealPct, registro, retestar) {
257
+ const resultado = { ...r, stealPct };
258
+ if (!r.ok && r.causa !== 'timeout' && r.executado !== false && (0, steal_1.stealAlto)(stealPct)) {
259
+ const testes = (0, steal_1.reprovacaoSoDeRelogio)(r.falhasDeTeste, registro);
260
+ if (testes) {
261
+ resultado.relogioSobSteal = testes;
262
+ if (retestar)
263
+ resultado.retesteDeRelogio = reciboDoReteste(testes, retestar(testes));
264
+ }
265
+ }
266
+ return resultado;
267
+ }
268
+ function executorComSteal(base, leitor = steal_1.lerProcStat, registro = new Set(), retestar) {
243
269
  return (nome, comando, cwd) => {
244
270
  const janela = (0, steal_1.abrirJanela)(leitor);
245
271
  const r = base(nome, comando, cwd);
246
272
  const stealPct = janela.fechar();
247
- const resultado = { ...r, stealPct };
248
- if (!r.ok && r.causa !== 'timeout' && r.executado !== false && (0, steal_1.stealAlto)(stealPct)) {
249
- const testes = (0, steal_1.reprovacaoSoDeRelogio)(r.falhasDeTeste, registro);
250
- if (testes)
251
- resultado.relogioSobSteal = testes;
252
- }
253
- return resultado;
273
+ return resultadoComSteal(r, stealPct, registro, retestar ? (testes) => retestar(nome, comando, cwd, testes) : undefined);
274
+ };
275
+ }
276
+ /** RM-081 F1: o relatorio e preparado antes e anexado depois, sem alterar o codigo de saida observado. */
277
+ function executorComJUnit(base, manifesto) {
278
+ return (nome, comando, cwd) => {
279
+ const caminho = (0, junit_1.caminhoJUnitDoComando)(manifesto, nome);
280
+ if (!caminho)
281
+ return base(nome, comando, cwd);
282
+ const preparado = (0, junit_1.prepararRelatorioJUnit)(cwd, caminho);
283
+ return (0, junit_1.anexarRelatorioJUnit)(base(nome, comando, cwd), preparado, (0, junit_1.marcacaoDeRelogioDoComando)(manifesto, nome));
254
284
  };
255
285
  }
256
286
  /** I-54 (D11): erros de spawn em que o processo nem chegou a existir. */
@@ -305,8 +335,12 @@ function comandoNoLedger(c) {
305
335
  // no Agile, o uso do preparo e as lanes; o Classic nao tem esses campos.
306
336
  const doAgile = { ...(c.preparo ? { preparo: c.preparo } : {}),
307
337
  ...(c.lane ? { lane: c.lane } : {}),
308
- ...(c.lanes ? { lanes: c.lanes.map((l) => ({ ...l, comando: (0, redacao_url_1.redigirCredenciaisUrl)(l.comando) })) } : {}),
309
- ...(c.comandoExecutado ? { comandoExecutado: (0, redacao_url_1.redigirCredenciaisUrl)(c.comandoExecutado) } : {}) };
338
+ ...(c.lanes ? { lanes: c.lanes.map((l) => ({ ...l, comando: (0, redacao_url_1.redigirCredenciaisUrl)(l.comando),
339
+ ...(l.comandoExecutado ? { comandoExecutado: (0, redacao_url_1.redigirCredenciaisUrl)(l.comandoExecutado) } : {}) })) } : {}),
340
+ ...(c.comandoExecutado ? { comandoExecutado: (0, redacao_url_1.redigirCredenciaisUrl)(c.comandoExecutado) } : {}),
341
+ ...(c.parcial ? { parcial: c.parcial } : {}),
342
+ ...(c.junit ? { junit: c.junit } : {}),
343
+ ...(c.retesteDeRelogio ? { retesteDeRelogio: c.retesteDeRelogio } : {}) };
310
344
  if (c.ok)
311
345
  return { nome: c.nome, ok: true, code: c.code, ...(c.duracaoMs !== undefined ? { duracaoMs: c.duracaoMs } : {}), ...doAgile };
312
346
  return {
@@ -320,16 +354,89 @@ function comandoNoLedger(c) {
320
354
  ...(c.relogioSobSteal?.length ? { relogioSobSteal: c.relogioSobSteal } : {}),
321
355
  };
322
356
  }
323
- /** Comandos declarados em `verify:` no manifesto, na ordem canonica. */
357
+ /** O filtro nominal da F3 para o mesmo comando/lane; F2 nao cria uma segunda selecao. */
358
+ function filtroDoComandoNoManifesto(manifesto, nome) {
359
+ if (nome.startsWith('test:'))
360
+ return manifesto.check?.agile?.filtro?.[nome.slice('test:'.length)];
361
+ return manifesto.verify.filtro?.[nome];
362
+ }
324
363
  function comandosDoManifesto(manifesto) {
325
364
  const lista = [];
326
365
  for (const nome of ['typecheck', 'build', 'test', 'security_scan']) {
327
366
  const comando = manifesto.verify[nome];
328
367
  if (comando && comando.trim() !== '')
329
- lista.push({ nome, comando });
368
+ lista.push({ nome, comando,
369
+ ...(manifesto.verify.junit?.[nome] ? { junit: manifesto.verify.junit[nome] } : {}),
370
+ ...(manifesto.verify.filtro?.[nome] ? { filtro: manifesto.verify.filtro[nome] } : {}) });
330
371
  }
331
372
  return lista;
332
373
  }
374
+ /** Um argumento por nome, sem deixar metacaractere do JUnit virar sintaxe do shell. */
375
+ function argumentoDoFiltro(nome) {
376
+ return `'${nome.replace(/'/g, `'"'"'`)}'`;
377
+ }
378
+ /** RM-081 F3: expande o unico marcador validado pelo manifesto. */
379
+ function comandoComTestesFiltrados(filtro, testes) {
380
+ return filtro.replace('{testes}', testes.map(argumentoDoFiltro).join(' '));
381
+ }
382
+ /**
383
+ * Roda a passagem parcial e so segue para o comando integral quando ela fica verde. O resultado
384
+ * parcial conserva `comando` como o contrato integral e guarda em `comandoExecutado` o que rodou.
385
+ */
386
+ function executarComFiltro(nome, comando, cwd, executor, filtro, testes) {
387
+ const parcial = executarPassagemParcial(nome, comando, cwd, executor, filtro, testes);
388
+ if (!parcial)
389
+ return executor(nome, comando, cwd);
390
+ if (parcial.ok)
391
+ return executor(nome, comando, cwd);
392
+ return parcial;
393
+ }
394
+ /** Uma passagem parcial isolada; o Agile decide depois se inclui a suite completa no grupo. */
395
+ function executarPassagemParcial(nome, comando, cwd, executor, filtro, testes) {
396
+ if (!filtro || !testes?.length)
397
+ return null;
398
+ const filtrado = comandoComTestesFiltrados(filtro, testes);
399
+ return { ...executor(nome, filtrado, cwd), comando, comandoExecutado: filtrado, parcial: { testes: [...testes] } };
400
+ }
401
+ function objeto(v) {
402
+ return v && typeof v === 'object' && !Array.isArray(v) ? v : null;
403
+ }
404
+ function nomesPersistidos(v) {
405
+ return Array.isArray(v) ? [...new Set(v.filter((x) => typeof x === 'string' && !!x))] : [];
406
+ }
407
+ /**
408
+ * So o ultimo verify_run da thread vale. Comando, JUnit lido e nomes precisam conferir; uma
409
+ * baseline, um stdout heuristico ou um comando antigo nunca autorizam reduzir a cobertura.
410
+ */
411
+ function selecaoDaRodadaAnterior(eventos, manifesto) {
412
+ const rodada = [...eventos].reverse().find((e) => e.tipo === ledger_1.TIPOS_DE_EVENTO.verificacao);
413
+ const comandos = {}, lanes = {};
414
+ if (!rodada || !Array.isArray(rodada.comandos))
415
+ return { comandos, lanes };
416
+ for (const atual of comandosDoManifesto(manifesto)) {
417
+ if (!atual.filtro || !atual.junit)
418
+ continue;
419
+ const anterior = rodada.comandos.map(objeto).find((c) => c?.nome === atual.nome);
420
+ const junit = objeto(anterior?.junit);
421
+ const nomes = nomesPersistidos(anterior?.testeQueCaiu);
422
+ if (anterior?.ok === false && anterior.comando === (0, redacao_url_1.redigirCredenciaisUrl)(atual.comando)
423
+ && junit?.estado === 'lido' && nomes.length > 0)
424
+ comandos[atual.nome] = nomes;
425
+ }
426
+ const anteriorTest = rodada.comandos.map(objeto).find((c) => c?.nome === 'test');
427
+ const lanesAnteriores = Array.isArray(anteriorTest?.lanes) ? anteriorTest.lanes.map(objeto) : [];
428
+ for (const [lane, comando] of Object.entries(manifesto.check?.agile?.test ?? {})) {
429
+ if (!manifesto.check?.agile?.filtro?.[lane] || !manifesto.check.agile.junit?.[lane])
430
+ continue;
431
+ const anterior = lanesAnteriores.find((l) => l?.lane === lane);
432
+ const junit = objeto(anterior?.junit);
433
+ const nomes = nomesPersistidos(anterior?.testeQueCaiu);
434
+ if (anterior?.ok === false && anterior.comando === (0, redacao_url_1.redigirCredenciaisUrl)(comando)
435
+ && junit?.estado === 'lido' && nomes.length > 0)
436
+ lanes[lane] = nomes;
437
+ }
438
+ return { comandos, lanes };
439
+ }
333
440
  /** A regra estrutural do CHANGELOG entra no mesmo veredito e na mesma baseline dos comandos do manifesto. */
334
441
  function verificarChangelog(cwd, base, baseCarimbada) {
335
442
  if (!(0, changelog_check_1.changelogAplicavel)(cwd))
@@ -398,11 +505,15 @@ function executorSobConducao(base, manifesto, conducao) {
398
505
  * falhava" com o manifesto dando 2400 s.
399
506
  */
400
507
  function executorDaRodada(manifesto, instabilidade, base, leitor = steal_1.lerProcStat, ambienteDaRodada) {
401
- return executorComSteal(base ?? executorDoManifesto(manifesto, ambienteDaRodada), leitor, (0, instabilidade_1.testesDeRelogioVigentes)(instabilidade));
508
+ const comJUnit = executorComJUnit(base ?? executorDoManifesto(manifesto, ambienteDaRodada), manifesto);
509
+ return executorComSteal(comJUnit, leitor, (0, instabilidade_1.testesDeRelogioVigentes)(instabilidade), (nome, _comando, cwd, testes) => {
510
+ const filtro = filtroDoComandoNoManifesto(manifesto, nome);
511
+ return filtro ? comJUnit(nome, comandoComTestesFiltrados(filtro, testes), cwd) : null;
512
+ });
402
513
  }
403
514
  /** RM-072 (D2): o que a rodada passa a todo comando dela, nos dois modos: o modo do CHECK. */
404
515
  function ambienteDoModo(check) {
405
- return { [check_modo_1.ENV_MODO_DO_CHECK]: check.modo };
516
+ return { [check_modo_1.ENV_MODO_DO_CHECK]: check.modo, [agendador_cpu_1.ENV_WORKERS]: String((0, agendador_cpu_1.workersDaReserva)(os.availableParallelism())) };
406
517
  }
407
518
  /** RM-072 (D1, D2): o modo da rodada, como vai ao ledger. */
408
519
  function checkNoLedger(check) {
@@ -428,6 +539,28 @@ function soAVarreduraEAsLanesComRelatorio(comRelatorio, semRelatorio) {
428
539
  * cada um com o prazo e o ambiente que o `executorDoManifesto` daria a ele (o do scan com o prazo interno), o steal medido
429
540
  * na janela do grupo e a conducao renovada com o maior prazo. Com executor injetado (MCP, testes), um por vez, por ele.
430
541
  */
542
+ /** A evidencia publica do plano: estado, capacidade usada, amostras e, no fallback, o equivalente do Classic. */
543
+ function pecaDoAgendadorCpu(p) {
544
+ const medida = p.medicaoCpuIntegracao;
545
+ const evidencia = medida
546
+ ? `; CPU da integracao: arvore ${Math.round(medida.arvoreMs)} ms + conteineres ${Math.round(medida.containersMs)} ms` +
547
+ ` = ${Math.round(medida.totalMs)} ms, pico da arvore ${medida.picoCpusDaArvore ?? 'indisponivel'} CPU ` +
548
+ `(${medida.arvoreCompleta ? 'arvore completa' : medida.anomaliaDaArvore
549
+ ? `arvore anomala: ${medida.anomaliaDaArvore}` : 'arvore incompleta'}), pico total ${medida.picoCpus ?? 'indisponivel'} CPU, ` +
550
+ (medida.completa ? 'medida completa' : medida.anomalia
551
+ ? `medida anomala (${medida.anomalia})`
552
+ : `medida incompleta (${medida.motivosDosAusentes.map((x) => `${x.container}: ${x.motivo}`).join('; ')})`)
553
+ : '';
554
+ return { peca: 'agendador-cpu', estado: p.estado,
555
+ motivo: p.saturado
556
+ ? `${p.motivo}; hermetica com ${p.cpusHermetica}/${p.capacidadeEfetiva} CPUs, varredura e integracao depois dela`
557
+ : `${p.motivo}; ${p.cpusHermetica}/${p.capacidadeEfetiva} CPUs na hermetica; depois da varredura ` +
558
+ `${p.cpusHermeticaAposVarredura} e depois da integracao ${p.cpusHermeticaAposReserva}; integracao ` +
559
+ `${p.integracaoEmParalelo ? 'em paralelo com a varredura' : 'encadeada depois da varredura'}; ` +
560
+ `conteineres com ${p.cpusPorContainer} CPU${evidencia}`,
561
+ reservaIntegracao: { cpus: p.cpusIntegracao, fonte: p.fonteCpusIntegracao },
562
+ ...(p.saturado ? { fallback: 'ordem do Classic para hermetica, varredura e integracao' } : {}) };
563
+ }
431
564
  function executorDoGrupo(manifesto, ambienteDaRodada, instabilidade, leitor, conducao, injetado, medicaoCpu) {
432
565
  if (injetado)
433
566
  return (itens, cwd) => itens.map((i) => injetado(i.nome, i.comando, cwd));
@@ -435,9 +568,11 @@ function executorDoGrupo(manifesto, ambienteDaRodada, instabilidade, leitor, con
435
568
  // N5 do CHECK 3: no grupo, como em serie, so a varredura e as lanes recebem o relatorio da rodada.
436
569
  const { [check_modo_1.ENV_RELATORIO_DO_CHECK]: _relatorio, ...semRelatorio } = ambienteDaRodada;
437
570
  const executar = ((itens, cwd) => {
571
+ const relatorios = itens.map((i) => i.junit ? (0, junit_1.prepararRelatorioJUnit)(cwd, i.junit) : null);
438
572
  const prazos = itens.map((i) => prazoDaExecucao(manifesto, i.nome, i.comando));
439
573
  const paraOAuxiliar = itens.map((i, k) => ({ nome: i.nome, comando: i.comando, cwd, prazoMs: prazos[k],
440
574
  ...(i.nice ? { nice: i.nice } : {}), ...(i.apos?.length ? { apos: i.apos } : {}), ...(i.papelCpu ? { papelCpu: i.papelCpu } : {}),
575
+ ...(i.recursos?.length ? { recursos: i.recursos } : {}),
441
576
  ambiente: { ...(recebeORelatorio(i.nome) ? ambienteDaRodada : semRelatorio), ...(ehComandoDoScan(manifesto, i.nome, i.comando)
442
577
  // RM-072: ao lado das lanes, a varredura roda poucas ferramentas por vez, para o grupo nao passar da CPU.
443
578
  ? { ORK_SECURITY_SCAN_DEADLINE_MS: String(prazoInternoDoScan(prazos[k])),
@@ -447,25 +582,23 @@ function executorDoGrupo(manifesto, ambienteDaRodada, instabilidade, leitor, con
447
582
  const janela = (0, steal_1.abrirJanela)(leitor);
448
583
  const processos = (0, check_paralelo_1.rodarEmParalelo)(paraOAuxiliar, ambienteDoVerify(), medicaoCpu ? { medicaoCpu } : {});
449
584
  if (processos.agendamentoCpu) {
450
- const p = processos.agendamentoCpu;
451
- executar.pecas = [{ peca: 'agendador-cpu', estado: p.saturado ? 'fallback' : 'agile',
452
- motivo: p.saturado
453
- ? `${p.motivo}; hermetica com ${p.cpusHermetica}/${p.cpus} CPUs, varredura e integracao depois dela`
454
- : `${p.cpusHermetica}/${p.cpus} CPUs na hermetica e ${p.cpusDaFatia} reservadas; conteineres com ${p.cpusPorContainer} CPU`,
455
- ...(p.saturado ? { fallback: 'ordem do Classic para hermetica, varredura e integracao' } : {}) }];
585
+ executar.pecas = [pecaDoAgendadorCpu(processos.agendamentoCpu)];
456
586
  }
457
587
  const stealPct = janela.fechar();
458
588
  return processos.map((p, k) => {
459
- const r = { ...resultadoDoProcesso(itens[k].nome, itens[k].comando, p, p.comGrupo, p.duracaoMs, prazos[k]), stealPct };
589
+ let r = { ...resultadoDoProcesso(itens[k].nome, itens[k].comando, p, p.comGrupo, p.duracaoMs, prazos[k]), stealPct };
590
+ if (relatorios[k])
591
+ r = (0, junit_1.anexarRelatorioJUnit)(r, relatorios[k], (0, junit_1.marcacaoDeRelogioDoComando)(manifesto, itens[k].nome));
460
592
  // S1 do CHECK: o auxiliar que morreu sem gravar o resultado deixa o comando sem veredito, nunca uma regressao.
461
593
  if (p.semResultado)
462
594
  r.executado = false;
463
- if (!r.ok && r.causa !== 'timeout' && r.executado !== false && (0, steal_1.stealAlto)(stealPct)) {
464
- const testes = (0, steal_1.reprovacaoSoDeRelogio)(r.falhasDeTeste, relogio);
465
- if (testes)
466
- r.relogioSobSteal = testes;
467
- }
468
- return r;
595
+ return resultadoComSteal(r, stealPct, relogio, (testes) => {
596
+ if (!itens[k].filtro)
597
+ return null;
598
+ const filtrado = comandoComTestesFiltrados(itens[k].filtro, testes);
599
+ const executarReteste = executorSobConducao(executorComJUnit(executorDoManifesto(manifesto, paraOAuxiliar[k].ambiente), manifesto), manifesto, conducao);
600
+ return executarReteste(itens[k].nome, filtrado, cwd);
601
+ });
469
602
  });
470
603
  });
471
604
  executar.pecas = [];
@@ -605,6 +738,7 @@ function gravarBaselineSobConducao(carregado, threadId, base, tomada, origem, le
605
738
  comandos: comandos.map((c) => ({ nome: c.nome, comando: c.comando, ok: c.ok, code: c.code, prazoMs: c.prazoMs ?? null,
606
739
  ...(c.duracaoMs !== undefined ? { duracaoMs: c.duracaoMs } : {}), ...(c.ok ? {} : { causa: c.causa ?? null }),
607
740
  ...(c.testeQueCaiu?.length ? { testeQueCaiu: c.testeQueCaiu } : {}),
741
+ ...(c.junit ? { junit: c.junit } : {}),
608
742
  ...(c.lanes?.length ? { lanes: c.lanes.map((l) => ({ ...l, comando: (0, redacao_url_1.redigirCredenciaisUrl)(l.comando) })) } : {}) })),
609
743
  check: checkNoLedger(check),
610
744
  ...(pecasDaBaseline.length ? { agile: { pecas: pecasDaBaseline } } : {}),
@@ -715,8 +849,11 @@ function verificarSobConducao(carregado, threadId, opcoes, conducao, canal, fila
715
849
  exigirDependenciasDaWorktree(carregado, threadId, cwd, 'verify', conducao, opcoes.executor);
716
850
  const commit = commitReal(cwd);
717
851
  const dir = (0, thread_1.dirThread)(raiz, threadId);
852
+ const eventos = (0, ledger_1.lerLedger)(dir);
853
+ const selecaoAnterior = selecaoDaRodadaAnterior(eventos, manifesto);
718
854
  // RM-072 (D1): o modo da rodada; a opcao vence o manifesto, que vence o padrao.
719
855
  const check = (0, check_modo_1.resolverModo)(manifesto.check?.mode, opcoes.check);
856
+ const selecaoPorMudanca = (0, check_selecao_1.planejarSelecaoNativa)(cwd, manifesto, eventos, commit, check.modo);
720
857
  // I-43 (D4, viga b): os criterios de pronto da thread viram claims do nucleo ANTES
721
858
  // da reexecucao, para entrarem no MESMO veredito das demais alegacoes. Idempotente:
722
859
  // `ork verify` roda em toda fase, e uma claim por criterio por rodada viraria
@@ -796,7 +933,7 @@ function verificarSobConducao(carregado, threadId, opcoes, conducao, canal, fila
796
933
  const grupoAntesDasClaims = !opcoes.soClaims && !!vigente && vigente.guarda.conferir() === null
797
934
  && Object.keys(manifesto.check?.agile?.test ?? {}).length > 0;
798
935
  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
936
+ ? (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, selecaoAnterior, (nome, comando, dir, filtro, testes) => executarPassagemParcial(nome, comando, dir, executorDosComandos, filtro, testes), selecaoPorMudanca.lanes)).comandos
800
937
  : [];
801
938
  if (doAgile)
802
939
  pecas.push(...doAgile.pecas);
@@ -831,14 +968,14 @@ function verificarSobConducao(carregado, threadId, opcoes, conducao, canal, fila
831
968
  if (guardaDepoisDasClaims === 'produto-mudou')
832
969
  refazer('depois das claims');
833
970
  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);
971
+ 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, selecaoAnterior, (nome, comando, dir, filtro, testes) => executarPassagemParcial(nome, comando, dir, executorDosComandos, filtro, testes), selecaoPorMudanca.lanes);
835
972
  comandosDoManifestoExecutados = doAgile.comandos;
836
973
  pecas.push(...doAgile.pecas);
837
974
  }
838
975
  if (!opcoes.soClaims && !grupoAntesDasClaims) {
839
976
  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));
977
+ ? (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, selecaoAnterior, (nome, comando, dir, filtro, testes) => executarPassagemParcial(nome, comando, dir, executorDosComandos, filtro, testes), selecaoPorMudanca.lanes)).comandos
978
+ : comandosDoManifesto(manifesto).map((c) => executarComFiltro(c.nome, c.comando, cwd, executorDosComandos, c.filtro, selecaoAnterior.comandos[c.nome]));
842
979
  if (doAgile)
843
980
  pecas.push(...doAgile.pecas);
844
981
  }
@@ -846,6 +983,7 @@ function verificarSobConducao(carregado, threadId, opcoes, conducao, canal, fila
846
983
  : verificarChangelog(cwd, manifesto.worktree.base_branch, thread.base?.commit);
847
984
  const comandos = opcoes.soClaims ? []
848
985
  : changelog ? [...comandosDoManifestoExecutados, changelog] : comandosDoManifestoExecutados;
986
+ const parcial = comandos.some((c) => !!c.parcial || c.lanes?.some((l) => !!l.parcial));
849
987
  const resumoDoAgile = vigente ? agileNoLedger(raiz, threadId, commit, pecas, doAgile, vigente.guarda, relatorio, claims) : null;
850
988
  if (relatorio && !vigente)
851
989
  relatorio.fechar();
@@ -946,6 +1084,8 @@ function verificarSobConducao(carregado, threadId, opcoes, conducao, canal, fila
946
1084
  };
947
1085
  }),
948
1086
  comandos: comandos.map(comandoNoLedger),
1087
+ parcial,
1088
+ ...(selecaoPorMudanca.cache ? { selecao: selecaoPorMudanca.cache } : {}),
949
1089
  baseline: baseline ? { commit: baseline.commit, gravadaEm: baseline.gravadaEm } : null,
950
1090
  regressoes: regressoes.map((r) => r.nome),
951
1091
  preExistentes: preExistentes.map((r) => r.nome),
@@ -996,7 +1136,8 @@ function verificarSobConducao(carregado, threadId, opcoes, conducao, canal, fila
996
1136
  : c.classificacaoDoComando === 'regressao' ? [c.comando] : []).join(', ')}`
997
1137
  : motivo === 'verify.timeout'
998
1138
  ? `sem veredito por tempo (prazo estourado, ou teste com relogio sob steal acima de ${steal_1.LIMIAR_STEAL_PCT}%): ${[
999
- ...estouros.map((r) => (0, check_agil_1.porQueDasLanes)(r) ? `${r.nome} (${(0, check_agil_1.porQueDasLanes)(r)})` : r.nome),
1139
+ ...estouros.map((r) => r.relogioSobSteal?.length ? motivoDoRelogioSobSteal(r)
1140
+ : (0, check_agil_1.porQueDasLanes)(r) ? `${r.nome} (${(0, check_agil_1.porQueDasLanes)(r)})` : r.nome),
1000
1141
  ...claims.filter((c) => c.motivo === 'verify.timeout').map((c) => c.claim.id)
1001
1142
  ].join(', ')}`
1002
1143
  : motivo === 'verify.sem-veredito'
@@ -1048,6 +1189,7 @@ function verificarSobConducao(carregado, threadId, opcoes, conducao, canal, fila
1048
1189
  agile: resumoDoAgile,
1049
1190
  motivos,
1050
1191
  ok,
1192
+ parcial,
1051
1193
  };
1052
1194
  }
1053
1195
  /**
@@ -1073,6 +1215,20 @@ function situacaoDoComando(c) {
1073
1215
  return m.mortoPorSinal(c.code);
1074
1216
  return m.falhou(c.code);
1075
1217
  }
1218
+ function resultadoDoReteste(c) {
1219
+ const reteste = c.retesteDeRelogio;
1220
+ if (!reteste)
1221
+ return 'segunda rodada sem recibo';
1222
+ if (reteste.estado === 'sem-filtro')
1223
+ return 'segunda rodada nao executada: filtro ausente';
1224
+ if (reteste.estado === 'sem-veredito')
1225
+ return `segunda rodada sem veredito (codigo ${reteste.code ?? 'ausente'})`;
1226
+ return `segunda rodada ${reteste.estado} (codigo ${reteste.code ?? 'ausente'})`;
1227
+ }
1228
+ function motivoDoRelogioSobSteal(c) {
1229
+ return `${c.nome}: testes ${c.relogioSobSteal?.join(', ') ?? 'nao informados'}; steal ${c.stealPct ?? 'nao medido'}%; ` +
1230
+ resultadoDoReteste(c);
1231
+ }
1076
1232
  /**
1077
1233
  * RM-037 (defeito 1 de 19 e 20/10/2026): as linhas de cada comando de `ork verify --baseline`, com o veredito dele, e a
1078
1234
  * linha que diz o que a baseline nao provou e como regravar.
@@ -1101,8 +1257,15 @@ function textoDoVerify(r) {
1101
1257
  if (a && (a.claims.reaproveitadas + a.claims.inteiras > 0 || a.grupo.paralelo)) {
1102
1258
  linhas.push(m.agile(a.claims.reaproveitadas, a.claims.inteiras, a.grupo.paralelo, a.grupo.lanes));
1103
1259
  }
1260
+ const motivoDaPeca = (p) => [p.motivo,
1261
+ p.reservaIntegracao ? m.reservaIntegracao(p.reservaIntegracao.cpus, p.reservaIntegracao.fonte) : null]
1262
+ .filter((x) => !!x).join('; ');
1104
1263
  for (const p of a?.pecas.filter((x) => x.estado === 'fallback') ?? [])
1105
- linhas.push(m.agileFallback(p.peca, p.motivo ?? '', p.fallback ?? ''));
1264
+ linhas.push(m.agileFallback(p.peca, motivoDaPeca(p), p.fallback ?? ''));
1265
+ for (const p of a?.pecas.filter((x) => x.estado === 'degradado') ?? [])
1266
+ linhas.push(m.agileDegradado(p.peca, motivoDaPeca(p)));
1267
+ for (const p of a?.pecas.filter((x) => x.estado === 'agile' && x.reservaIntegracao) ?? [])
1268
+ linhas.push(m.agilePeca(p.peca, motivoDaPeca(p)));
1106
1269
  linhas.push(r.baseline ? m.baseline((0, util_1.shaCurto)(r.baseline.commit), (0, horario_1.formatarDataHoraRotulada)(r.baseline.gravadaEm)) : m.semBaseline);
1107
1270
  if (r.preparo) {
1108
1271
  const p = r.preparo;
@@ -1132,8 +1295,19 @@ function textoDoVerify(r) {
1132
1295
  linhas.push(m.comandosDoManifesto);
1133
1296
  for (const c of r.comandos) {
1134
1297
  linhas.push(m.comando(c.nome, c.ok ? m.comandoOk : situacaoDoComando(c), c.comando));
1135
- if (!c.ok && c.testeQueCaiu?.length)
1136
- linhas.push(m.testesQueCairam(c.testeQueCaiu.join('; ')));
1298
+ for (const lane of c.lanes ?? [])
1299
+ if (lane.selecao) {
1300
+ linhas.push(m.selecaoDaLane(lane.lane, lane.selecao.modo, lane.selecao.motivo, lane.selecao.arquivos?.join('; ') ?? ''));
1301
+ }
1302
+ if (c.parcial?.testes.length)
1303
+ linhas.push(m.rodadaParcial(c.parcial.testes.join('; ')));
1304
+ if (c.retesteDeRelogio)
1305
+ linhas.push(m.retesteDeRelogio(c.retesteDeRelogio.testes.join('; '), c.stealPct, c.retesteDeRelogio.estado, c.retesteDeRelogio.code));
1306
+ if (!c.ok && c.testeQueCaiu?.length) {
1307
+ const visiveis = c.junit?.estado === 'lido' ? c.testeQueCaiu.slice(0, 10) : c.testeQueCaiu;
1308
+ const restante = c.testeQueCaiu.length - visiveis.length;
1309
+ linhas.push(m.testesQueCairam(visiveis.join('; ') + (restante > 0 ? `; +${restante} no ledger` : '')));
1310
+ }
1137
1311
  }
1138
1312
  }
1139
1313
  if (r.estouros.length > 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orkastery/cli",
3
- "version": "0.6.3",
3
+ "version": "0.6.5",
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",