@orkastery/cli 0.6.4 → 0.6.6

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,36 +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
- const medida = p.medicaoCpuIntegracao;
452
- const evidencia = medida
453
- ? `; CPU da integracao: arvore ${Math.round(medida.arvoreMs)} ms + conteineres ${Math.round(medida.containersMs)} ms` +
454
- ` = ${Math.round(medida.totalMs)} ms, pico ${medida.picoCpus ?? 'indisponivel'} CPU, ` +
455
- (medida.completa ? 'medida completa' : medida.anomalia
456
- ? `medida anomala (${medida.anomalia})`
457
- : `medida incompleta (${medida.motivosDosAusentes.map((x) => `${x.container}: ${x.motivo}`).join('; ')})`)
458
- : '';
459
- executar.pecas = [{ peca: 'agendador-cpu', estado: p.saturado ? 'fallback' : 'agile',
460
- motivo: p.saturado
461
- ? `${p.motivo}; hermetica com ${p.cpusHermetica}/${p.cpus} CPUs, varredura e integracao depois dela`
462
- : `${p.cpusHermetica}/${p.cpus} CPUs na hermetica; depois da varredura ${p.cpusHermeticaAposVarredura}` +
463
- ` e depois da integracao ${p.cpusHermeticaAposReserva}; reserva da integracao ${p.fonteCpusIntegracao}` +
464
- ` (${p.cpusIntegracao} CPU), ${p.integracaoEmParalelo ? 'em paralelo com a varredura' : 'encadeada depois da varredura'}` +
465
- `; conteineres com ${p.cpusPorContainer} CPU${evidencia}`,
466
- ...(p.saturado ? { fallback: 'ordem do Classic para hermetica, varredura e integracao' } : {}) }];
585
+ executar.pecas = [pecaDoAgendadorCpu(processos.agendamentoCpu)];
467
586
  }
468
587
  const stealPct = janela.fechar();
469
588
  return processos.map((p, k) => {
470
- 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));
471
592
  // S1 do CHECK: o auxiliar que morreu sem gravar o resultado deixa o comando sem veredito, nunca uma regressao.
472
593
  if (p.semResultado)
473
594
  r.executado = false;
474
- if (!r.ok && r.causa !== 'timeout' && r.executado !== false && (0, steal_1.stealAlto)(stealPct)) {
475
- const testes = (0, steal_1.reprovacaoSoDeRelogio)(r.falhasDeTeste, relogio);
476
- if (testes)
477
- r.relogioSobSteal = testes;
478
- }
479
- 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
+ });
480
602
  });
481
603
  });
482
604
  executar.pecas = [];
@@ -616,6 +738,7 @@ function gravarBaselineSobConducao(carregado, threadId, base, tomada, origem, le
616
738
  comandos: comandos.map((c) => ({ nome: c.nome, comando: c.comando, ok: c.ok, code: c.code, prazoMs: c.prazoMs ?? null,
617
739
  ...(c.duracaoMs !== undefined ? { duracaoMs: c.duracaoMs } : {}), ...(c.ok ? {} : { causa: c.causa ?? null }),
618
740
  ...(c.testeQueCaiu?.length ? { testeQueCaiu: c.testeQueCaiu } : {}),
741
+ ...(c.junit ? { junit: c.junit } : {}),
619
742
  ...(c.lanes?.length ? { lanes: c.lanes.map((l) => ({ ...l, comando: (0, redacao_url_1.redigirCredenciaisUrl)(l.comando) })) } : {}) })),
620
743
  check: checkNoLedger(check),
621
744
  ...(pecasDaBaseline.length ? { agile: { pecas: pecasDaBaseline } } : {}),
@@ -726,8 +849,11 @@ function verificarSobConducao(carregado, threadId, opcoes, conducao, canal, fila
726
849
  exigirDependenciasDaWorktree(carregado, threadId, cwd, 'verify', conducao, opcoes.executor);
727
850
  const commit = commitReal(cwd);
728
851
  const dir = (0, thread_1.dirThread)(raiz, threadId);
852
+ const eventos = (0, ledger_1.lerLedger)(dir);
853
+ const selecaoAnterior = selecaoDaRodadaAnterior(eventos, manifesto);
729
854
  // RM-072 (D1): o modo da rodada; a opcao vence o manifesto, que vence o padrao.
730
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);
731
857
  // I-43 (D4, viga b): os criterios de pronto da thread viram claims do nucleo ANTES
732
858
  // da reexecucao, para entrarem no MESMO veredito das demais alegacoes. Idempotente:
733
859
  // `ork verify` roda em toda fase, e uma claim por criterio por rodada viraria
@@ -807,7 +933,7 @@ function verificarSobConducao(carregado, threadId, opcoes, conducao, canal, fila
807
933
  const grupoAntesDasClaims = !opcoes.soClaims && !!vigente && vigente.guarda.conferir() === null
808
934
  && Object.keys(manifesto.check?.agile?.test ?? {}).length > 0;
809
935
  let comandosDoManifestoExecutados = grupoAntesDasClaims
810
- ? (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
811
937
  : [];
812
938
  if (doAgile)
813
939
  pecas.push(...doAgile.pecas);
@@ -842,14 +968,14 @@ function verificarSobConducao(carregado, threadId, opcoes, conducao, canal, fila
842
968
  if (guardaDepoisDasClaims === 'produto-mudou')
843
969
  refazer('depois das claims');
844
970
  opcoes.progresso?.('grupo-agile');
845
- 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);
846
972
  comandosDoManifestoExecutados = doAgile.comandos;
847
973
  pecas.push(...doAgile.pecas);
848
974
  }
849
975
  if (!opcoes.soClaims && !grupoAntesDasClaims) {
850
976
  comandosDoManifestoExecutados = vigente
851
- ? (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
852
- : 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]));
853
979
  if (doAgile)
854
980
  pecas.push(...doAgile.pecas);
855
981
  }
@@ -857,6 +983,7 @@ function verificarSobConducao(carregado, threadId, opcoes, conducao, canal, fila
857
983
  : verificarChangelog(cwd, manifesto.worktree.base_branch, thread.base?.commit);
858
984
  const comandos = opcoes.soClaims ? []
859
985
  : changelog ? [...comandosDoManifestoExecutados, changelog] : comandosDoManifestoExecutados;
986
+ const parcial = comandos.some((c) => !!c.parcial || c.lanes?.some((l) => !!l.parcial));
860
987
  const resumoDoAgile = vigente ? agileNoLedger(raiz, threadId, commit, pecas, doAgile, vigente.guarda, relatorio, claims) : null;
861
988
  if (relatorio && !vigente)
862
989
  relatorio.fechar();
@@ -957,6 +1084,8 @@ function verificarSobConducao(carregado, threadId, opcoes, conducao, canal, fila
957
1084
  };
958
1085
  }),
959
1086
  comandos: comandos.map(comandoNoLedger),
1087
+ parcial,
1088
+ ...(selecaoPorMudanca.cache ? { selecao: selecaoPorMudanca.cache } : {}),
960
1089
  baseline: baseline ? { commit: baseline.commit, gravadaEm: baseline.gravadaEm } : null,
961
1090
  regressoes: regressoes.map((r) => r.nome),
962
1091
  preExistentes: preExistentes.map((r) => r.nome),
@@ -1007,7 +1136,8 @@ function verificarSobConducao(carregado, threadId, opcoes, conducao, canal, fila
1007
1136
  : c.classificacaoDoComando === 'regressao' ? [c.comando] : []).join(', ')}`
1008
1137
  : motivo === 'verify.timeout'
1009
1138
  ? `sem veredito por tempo (prazo estourado, ou teste com relogio sob steal acima de ${steal_1.LIMIAR_STEAL_PCT}%): ${[
1010
- ...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),
1011
1141
  ...claims.filter((c) => c.motivo === 'verify.timeout').map((c) => c.claim.id)
1012
1142
  ].join(', ')}`
1013
1143
  : motivo === 'verify.sem-veredito'
@@ -1059,6 +1189,7 @@ function verificarSobConducao(carregado, threadId, opcoes, conducao, canal, fila
1059
1189
  agile: resumoDoAgile,
1060
1190
  motivos,
1061
1191
  ok,
1192
+ parcial,
1062
1193
  };
1063
1194
  }
1064
1195
  /**
@@ -1084,6 +1215,20 @@ function situacaoDoComando(c) {
1084
1215
  return m.mortoPorSinal(c.code);
1085
1216
  return m.falhou(c.code);
1086
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
+ }
1087
1232
  /**
1088
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
1089
1234
  * linha que diz o que a baseline nao provou e como regravar.
@@ -1112,8 +1257,15 @@ function textoDoVerify(r) {
1112
1257
  if (a && (a.claims.reaproveitadas + a.claims.inteiras > 0 || a.grupo.paralelo)) {
1113
1258
  linhas.push(m.agile(a.claims.reaproveitadas, a.claims.inteiras, a.grupo.paralelo, a.grupo.lanes));
1114
1259
  }
1260
+ const motivoDaPeca = (p) => [p.motivo,
1261
+ p.reservaIntegracao ? m.reservaIntegracao(p.reservaIntegracao.cpus, p.reservaIntegracao.fonte) : null]
1262
+ .filter((x) => !!x).join('; ');
1115
1263
  for (const p of a?.pecas.filter((x) => x.estado === 'fallback') ?? [])
1116
- 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)));
1117
1269
  linhas.push(r.baseline ? m.baseline((0, util_1.shaCurto)(r.baseline.commit), (0, horario_1.formatarDataHoraRotulada)(r.baseline.gravadaEm)) : m.semBaseline);
1118
1270
  if (r.preparo) {
1119
1271
  const p = r.preparo;
@@ -1143,8 +1295,19 @@ function textoDoVerify(r) {
1143
1295
  linhas.push(m.comandosDoManifesto);
1144
1296
  for (const c of r.comandos) {
1145
1297
  linhas.push(m.comando(c.nome, c.ok ? m.comandoOk : situacaoDoComando(c), c.comando));
1146
- if (!c.ok && c.testeQueCaiu?.length)
1147
- 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
+ }
1148
1311
  }
1149
1312
  }
1150
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.4",
3
+ "version": "0.6.6",
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",