@orkastery/cli 0.6.6 → 0.6.7

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 (41) hide show
  1. package/adapters/claude-code/agents/ork-check.md +4 -0
  2. package/adapters/claude-code/mods/ork-freio/hooks/lote.ts +1 -1
  3. package/adapters/claude-code/mods/ork-torre/hooks/frases.ts +2 -1
  4. package/adapters/openclaw/openclaw.plugin.json +1 -1
  5. package/adapters/openclaw/package.json +1 -1
  6. package/assets/adapters-en/claude-code/agents/ork-check.md +4 -0
  7. package/assets/skills-en/phases/check-quality/SKILL.md +14 -0
  8. package/dist/adapters/claude-bg.js +1 -1
  9. package/dist/agendador-cpu.js +27 -4
  10. package/dist/ambiente-funcional.js +72 -0
  11. package/dist/cenarios-sensores.js +3 -7
  12. package/dist/check-agil.js +204 -60
  13. package/dist/check-paralelo.js +101 -36
  14. package/dist/claim-lint.js +19 -0
  15. package/dist/claims.js +77 -11
  16. package/dist/fila-pesada.js +8 -6
  17. package/dist/fix.js +152 -6
  18. package/dist/hitl-gates.js +1 -1
  19. package/dist/index.js +53 -4
  20. package/dist/locale-ajuda.js +10 -4
  21. package/dist/locale-claims.js +22 -6
  22. package/dist/locale-operacao-restante.js +12 -0
  23. package/dist/locale-prompts.js +4 -2
  24. package/dist/locale-pulse.js +2 -0
  25. package/dist/locale-retry.js +8 -0
  26. package/dist/locale-verify.js +8 -0
  27. package/dist/mcp-artifacts.js +24 -0
  28. package/dist/mcp-install.js +1 -1
  29. package/dist/mcp-server.js +11 -1
  30. package/dist/mcp-verify-async.js +2 -1
  31. package/dist/noctum-provas.js +56 -30
  32. package/dist/recibo-prova.js +87 -0
  33. package/dist/retry.js +6 -0
  34. package/dist/security-scan/executor.js +9 -3
  35. package/dist/security-scan/guard.js +5 -2
  36. package/dist/ship.js +1 -1
  37. package/dist/temporario-das-lanes.js +94 -0
  38. package/dist/verify.js +662 -410
  39. package/eval/fixtures/fx-sensores-runtime/caso.json +1 -1
  40. package/package.json +1 -1
  41. package/skills/phases/check-quality/SKILL.md +14 -0
@@ -35,6 +35,8 @@ const CORRECAO = {
35
35
  'ork-fora-do-path': (0, locale_1.msg)('pt-BR').claims.correcoesDoLint['ork-fora-do-path'],
36
36
  'estado-local': (0, locale_1.msg)('pt-BR').claims.correcoesDoLint['estado-local'],
37
37
  'aspas-escapadas': (0, locale_1.msg)('pt-BR').claims.correcoesDoLint['aspas-escapadas'],
38
+ 'contexto-artificial': (0, locale_1.msg)('pt-BR').claims.correcoesDoLint['contexto-artificial'],
39
+ 'artefato-de-fechamento': (0, locale_1.msg)('pt-BR').claims.correcoesDoLint['artefato-de-fechamento'],
38
40
  };
39
41
  /** RM-037: estes achados tornam uma claim nova inexequivel no checkout do runner. */
40
42
  exports.REGRAS_FATAIS_DE_CLAIM = new Set([
@@ -54,6 +56,12 @@ const SO_ANCESTRALIDADE = /merge-base\s+--is-ancestor\s+\S+\s+\S+/g;
54
56
  /** (b2) Contagem de commits comparada a um numero. */
55
57
  const CONTAGEM = /rev-list\s+(?:[^|;&]*\s)?--count\b|\bgit\s+log\b[^|;&]*\|\s*wc\s+-l\b/;
56
58
  const COMPARACAO = /-(?:eq|ne|gt|lt|ge|le)\s+['"]?\d|[^=!<>]==?\s*['"]?\d|grep\s+-(?:q?x|xq)\s+['"]?\d/;
59
+ /** RM-079: execucao isolada que reconstrói contexto do produto dentro da propria claim. */
60
+ const NODE_EVAL = /\bnode\b[^;&|]*(?:\s-e\b|\s--eval\b)/;
61
+ const PARSER_DE_AST = /\b(?:@babel\/parser|acorn|esprima|recast|ts-morph|typescript)\b|\b(?:createSourceFile|parseExpression|parseScript|parseModule)\s*\(/;
62
+ const EXECUCAO_ARTIFICIAL = /\brunIn(?:Context|NewContext|ThisContext)\s*\(|\bnew\s+Function\s*\(/;
63
+ /** Artefatos produzidos pelo proprio fechamento nao podem ser a fonte circular da claim. */
64
+ const ARTEFATO_DE_FECHAMENTO = /(?:^|[\s"'=])((?:\.\/)?\.ork-ci\/[\w./-]+\.json|(?:\.\/)?docs\/check\.md|(?:\.\/)?pr-body\.md|(?:\.\/)?(?:POSTMORTEM|MASTER)\.(?:json|md))(?=$|[\s"'])/;
57
65
  /** Executavel em posicao de comando, no inicio ou depois de um operador do shell. */
58
66
  function executavelDireto(comando, nome) {
59
67
  return new RegExp(`(?:^|&&|\\|\\||[;|(])\\s*(?:command\\s+|env\\s+)?(${nome})(?=\\s|$)`).exec(comando);
@@ -72,6 +80,17 @@ function analisarComando(comando) {
72
80
  const aspas = /\\x(?:22|27)/i.exec(comando);
73
81
  if (aspas)
74
82
  achados.push({ regra: 'aspas-escapadas', trecho: aspas[0], correcao: CORRECAO['aspas-escapadas'] });
83
+ const artificial = EXECUCAO_ARTIFICIAL.exec(comando)
84
+ ?? (NODE_EVAL.test(comando) ? PARSER_DE_AST.exec(comando) : null);
85
+ if (artificial)
86
+ achados.push({
87
+ regra: 'contexto-artificial', trecho: artificial[0].trim(), correcao: CORRECAO['contexto-artificial'],
88
+ });
89
+ const fechamento = ARTEFATO_DE_FECHAMENTO.exec(comando);
90
+ if (fechamento)
91
+ achados.push({
92
+ regra: 'artefato-de-fechamento', trecho: fechamento[1], correcao: CORRECAO['artefato-de-fechamento'],
93
+ });
75
94
  const suite = SUITE_INTEIRA.exec(comando);
76
95
  if (suite)
77
96
  achados.push({ regra: 'suite-inteira', trecho: suite[0].trim(), correcao: CORRECAO['suite-inteira'] });
package/dist/claims.js CHANGED
@@ -56,6 +56,7 @@ exports.registrarCriteriosDePronto = registrarCriteriosDePronto;
56
56
  exports.exigirClaim = exigirClaim;
57
57
  exports.anexarComando = anexarComando;
58
58
  exports.retirarClaim = retirarClaim;
59
+ exports.retirarClaimTipada = retirarClaimTipada;
59
60
  exports.carimbarEstado = carimbarEstado;
60
61
  exports.tabelaDeClaims = tabelaDeClaims;
61
62
  const fs = __importStar(require("node:fs"));
@@ -69,6 +70,8 @@ const verify_1 = require("./verify");
69
70
  const util_1 = require("./util");
70
71
  const claim_lint_1 = require("./claim-lint");
71
72
  const locale_1 = require("./locale");
73
+ const gates_1 = require("./gates");
74
+ const hitl_1 = require("./hitl");
72
75
  /** Caminho do `claims.jsonl` da thread (append-only, como o ledger). */
73
76
  function caminhoClaims(raiz, threadId) {
74
77
  return path.join((0, thread_1.dirThread)(raiz, threadId), 'claims.jsonl');
@@ -116,6 +119,10 @@ function ehAlegacaoNegativa(texto) {
116
119
  */
117
120
  function montarClaim(entrada) {
118
121
  const verificar = (entrada.verificar ?? []).filter((c) => c.trim() !== '');
122
+ const dependenciasExternas = [...new Set(entrada.dependenciasExternas ?? [])].sort();
123
+ if (dependenciasExternas.some((d) => d !== 'rede' && d !== 'relogio')) {
124
+ throw new Error('claim.dependencia-externa-invalida: use rede ou relogio');
125
+ }
119
126
  return {
120
127
  id: entrada.id,
121
128
  thread: entrada.dono,
@@ -124,6 +131,7 @@ function montarClaim(entrada) {
124
131
  alegacao: entrada.alegacao.trim(),
125
132
  negativa: ehAlegacaoNegativa(entrada.alegacao),
126
133
  verificar,
134
+ ...(dependenciasExternas.length ? { dependenciasExternas } : {}),
127
135
  criadoEm: (0, util_1.agora)(),
128
136
  estado: verificar.length > 0 ? 'pendente' : 'nao-verificavel',
129
137
  ...(entrada.origem ? { origem: entrada.origem } : {}),
@@ -152,6 +160,7 @@ function adicionarClaim(raiz, threadId, opcoes) {
152
160
  arquivo: opcoes.arquivo,
153
161
  alegacao: opcoes.alegacao,
154
162
  verificar: opcoes.verificar,
163
+ dependenciasExternas: opcoes.dependenciasExternas,
155
164
  origem: opcoes.origem,
156
165
  });
157
166
  const recusas = (claim.lint ?? []).filter((a) => (0, claim_lint_1.regraFatalDeClaim)(a.regra));
@@ -170,6 +179,7 @@ function adicionarClaim(raiz, threadId, opcoes) {
170
179
  alegacao: claim.alegacao,
171
180
  negativa,
172
181
  verificar,
182
+ ...(claim.dependenciasExternas?.length ? { dependenciasExternas: claim.dependenciasExternas } : {}),
173
183
  estado: claim.estado,
174
184
  ...(claim.lint?.length ? { lint: claim.lint.map((a) => a.regra) } : {}),
175
185
  });
@@ -286,25 +296,81 @@ function anexarComando(raiz, threadId, claimId, comando) {
286
296
  });
287
297
  return atualizada;
288
298
  }
289
- /**
290
- * Retira uma claim, com motivo obrigatorio.
291
- *
292
- * O `claims.jsonl` e append-only: a alegacao errada continua no historico, com a retirada
293
- * registrada em cima. `ork verify` para de exigir prova dela, e o ledger guarda o porque.
294
- */
295
- function retirarClaim(raiz, threadId, claimId, motivo) {
296
- const claim = exigirClaim(raiz, threadId, claimId);
297
- const atualizada = { ...claim, estado: 'retirada', motivoDaRetirada: motivo };
299
+ const AUTOR_LEGADO = { tipo: 'sessao', id: 'api:legada' };
300
+ function retirarERegistrar(raiz, threadId, claim, motivo, autor, tipada, motivoTipo, substituidaPor) {
301
+ if (claim.estado === 'retirada')
302
+ throw new Error(`claim "${claim.id}" ja foi retirada`);
303
+ const motivoSeguro = (0, hitl_1.redigirSegredos)(motivo);
304
+ const atualizada = { ...claim, estado: 'retirada', motivoDaRetirada: motivoSeguro };
298
305
  gravarClaim(raiz, atualizada);
299
306
  (0, ledger_1.registrar)((0, thread_1.dirThread)(raiz, threadId), threadId, ledger_1.TIPOS_DE_EVENTO.claimRegistrada, {
300
- claim: claimId,
307
+ claim: claim.id,
301
308
  alteracao: 'claim retirada',
302
309
  alegacao: claim.alegacao,
303
- motivoDaRetirada: motivo,
310
+ motivoDaRetirada: motivoSeguro,
304
311
  estado: 'retirada',
312
+ tipada,
313
+ autor,
314
+ ...(motivoTipo ? { motivoTipo } : {}),
315
+ ...(substituidaPor ? { substituidaPor } : {}),
305
316
  });
306
317
  return atualizada;
307
318
  }
319
+ function retirarClaim(raiz, threadId, claimId, motivo, autor = AUTOR_LEGADO) {
320
+ const claim = exigirClaim(raiz, threadId, claimId);
321
+ return retirarERegistrar(raiz, threadId, claim, motivo, autor, false);
322
+ }
323
+ /** RM-079: troca tipada de regra; sem substituta, preserva a claim e escala ao dono. */
324
+ function retirarClaimTipada(raiz, threadId, claimId, opcoes) {
325
+ if (!['substituicao', 'obsoleta', 'erro'].includes(opcoes.motivoTipo)) {
326
+ throw new Error(`motivo-tipo invalido: ${opcoes.motivoTipo}`);
327
+ }
328
+ if (!opcoes.motivo.trim())
329
+ throw new Error('o motivo da retirada e obrigatorio');
330
+ const claim = exigirClaim(raiz, threadId, claimId);
331
+ if (claim.estado === 'retirada')
332
+ throw new Error(`claim "${claim.id}" ja foi retirada`);
333
+ if (opcoes.substituidaPor) {
334
+ const repetirVerify = (motivo, detalhe) => new Error(`${motivo}: ${detalhe}; rode node core/dist/index.js verify ${threadId}`);
335
+ if (opcoes.substituidaPor === claimId)
336
+ throw new Error('a claim substituta precisa ser diferente da retirada');
337
+ const substituta = exigirClaim(raiz, threadId, opcoes.substituidaPor);
338
+ if (substituta.estado === 'retirada') {
339
+ throw repetirVerify('claims.substituta.inativa', `claim substituta "${substituta.id}" nao esta ativa`);
340
+ }
341
+ const rodada = (0, ledger_1.lerLedger)((0, thread_1.dirThread)(raiz, threadId)).filter((e) => e.tipo === ledger_1.TIPOS_DE_EVENTO.verificacao).at(-1);
342
+ const headAtual = (0, verify_1.commitReal)((0, verify_1.cwdDaThread)(raiz, (0, thread_1.lerThread)(raiz, threadId)));
343
+ if (rodada?.commit !== headAtual) {
344
+ throw repetirVerify('claims.substituta.verify-head', `o ultimo verify_run pertence a ${String(rodada?.commit ?? 'commit ausente')}, nao ao HEAD ${headAtual}`);
345
+ }
346
+ const prova = Array.isArray(rodada?.claims)
347
+ ? rodada.claims.find((c) => c.id === substituta.id)
348
+ : undefined;
349
+ if (prova?.verificado !== true) {
350
+ throw repetirVerify('claims.substituta.nao-verificada', `claim substituta "${substituta.id}" nao passou no ultimo verify_run`);
351
+ }
352
+ if (typeof prova.verificarSha256 !== 'string'
353
+ || prova.verificarSha256 !== (0, verify_1.impressaoDosComandos)(substituta.verificar)) {
354
+ throw repetirVerify('claims.substituta.alterada-depois-do-verify', `claim substituta "${substituta.id}" mudou o comando verificar depois do ultimo verify_run`);
355
+ }
356
+ const retirada = retirarERegistrar(raiz, threadId, claim, opcoes.motivo, opcoes.autor, true, opcoes.motivoTipo, substituta.id);
357
+ return { estado: 'retirada', claim: retirada, tipada: true, autor: opcoes.autor, substituidaPor: substituta.id };
358
+ }
359
+ const thread = (0, thread_1.lerThread)(raiz, threadId);
360
+ (0, gates_1.registrarGateBloqueado)((0, thread_1.dirThread)(raiz, threadId), threadId, {
361
+ gate: 'claims.retirar',
362
+ motivo: 'claims.retirada-sem-substituta',
363
+ modo: thread.modo,
364
+ fase: thread.faseAtual,
365
+ detalhe: `${claim.id}: ${opcoes.motivoTipo}; ${(0, hitl_1.redigirSegredos)(opcoes.motivo.trim())}`,
366
+ evidencia: `claim ativa ${claim.id}`,
367
+ correcao: 'o dono decide pelo pedido tipado ou uma substituta verde e informada',
368
+ claim: claim.id,
369
+ autor: opcoes.autor,
370
+ });
371
+ return { estado: 'pendente-do-dono', claim, tipada: true, autor: opcoes.autor,
372
+ motivo: 'claims.retirada-sem-substituta' };
373
+ }
308
374
  /** Regrava a claim com o estado carimbado pelo `ork verify` (o append mais novo vence). */
309
375
  function carimbarEstado(raiz, claim, estado) {
310
376
  const atualizada = { ...claim, estado };
@@ -495,7 +495,7 @@ function vagaDoAncestral(chave, dir) {
495
495
  * artefato e uma das `slots` vagas, todas do kernel. Espera em primeiro plano até `esperarMs` e, sem a vez, lança
496
496
  * `ErroDaFilaPesada` sem rodar nada. Dentro de quem já segura a vez neste processo, roda direto.
497
497
  */
498
- function comRecursoPesado(raiz, pedido, executar) {
498
+ function comRecursoPesado(raiz, pedido, executar, dependencias = {}) {
499
499
  if (!(pedido.slots > 0))
500
500
  return executar({ situacao: 'desligada', slot: null, esperouMs: 0 });
501
501
  const chave = path.resolve((0, estado_thread_1.raizDoEstado)(raiz));
@@ -508,7 +508,9 @@ function comRecursoPesado(raiz, pedido, executar) {
508
508
  const doAncestral = vagaDoAncestral(chave, dir);
509
509
  if (doAncestral !== null)
510
510
  return executar({ situacao: 'reentrada', slot: doAncestral, esperouMs: 0 });
511
- const inicio = Date.now();
511
+ const agora = dependencias.agora ?? Date.now;
512
+ const pausar = dependencias.pausar ?? dormir;
513
+ const inicio = agora();
512
514
  const prazo = inicio + Math.max(0, pedido.esperarMs);
513
515
  let senha = tirarSenha(dir);
514
516
  let avisou = false;
@@ -525,7 +527,7 @@ function comRecursoPesado(raiz, pedido, executar) {
525
527
  process.env[exports.ENV_DONO_DA_FILA] = JSON.stringify({ raiz: chave, pid: process.pid, inicio: inicioDoProcesso(process.pid), slot: posse.slot,
526
528
  posse: posse.id });
527
529
  try {
528
- return executar({ situacao: 'adquirida', slot: posse.slot, esperouMs: Date.now() - inicio });
530
+ return executar({ situacao: 'adquirida', slot: posse.slot, esperouMs: agora() - inicio });
529
531
  }
530
532
  finally {
531
533
  if (marcaAnterior === undefined)
@@ -536,8 +538,8 @@ function comRecursoPesado(raiz, pedido, executar) {
536
538
  soltar(posse);
537
539
  }
538
540
  }
539
- if (Date.now() >= prazo)
540
- throw new ErroDaFilaPesada(recusaDaFila(pedido, Date.now() - inicio, ocupantesDaFilaPesada(raiz)));
541
+ if (agora() >= prazo)
542
+ throw new ErroDaFilaPesada(recusaDaFila(pedido, agora() - inicio, ocupantesDaFilaPesada(raiz)));
541
543
  if (!avisou) {
542
544
  avisou = true;
543
545
  try {
@@ -545,7 +547,7 @@ function comRecursoPesado(raiz, pedido, executar) {
545
547
  }
546
548
  catch { /* o aviso nunca derruba a espera */ }
547
549
  }
548
- dormir(Math.min(PAUSA_MS, prazo - Date.now()));
550
+ pausar(Math.min(PAUSA_MS, prazo - agora()));
549
551
  }
550
552
  }
551
553
  finally {
package/dist/fix.js CHANGED
@@ -56,19 +56,24 @@ var __importStar = (this && this.__importStar) || (function () {
56
56
  };
57
57
  })();
58
58
  Object.defineProperty(exports, "__esModule", { value: true });
59
+ exports.lerParecerParaFix = lerParecerParaFix;
59
60
  exports.caminhoDosFixes = caminhoDosFixes;
60
61
  exports.lerCorrecoes = lerCorrecoes;
61
62
  exports.correcoesDaRodada = correcoesDaRodada;
62
63
  exports.ultimaRodada = ultimaRodada;
63
64
  exports.tipoDoMotivo = tipoDoMotivo;
64
65
  exports.derivarCorrecoes = derivarCorrecoes;
66
+ exports.derivarCorrecoesDoParecer = derivarCorrecoesDoParecer;
65
67
  exports.specDoGoFix = specDoGoFix;
66
68
  exports.abrirRodada = abrirRodada;
67
69
  exports.reverificar = reverificar;
68
70
  exports.rodadasGastas = rodadasGastas;
71
+ exports.statusDaRodada = statusDaRodada;
69
72
  exports.textoDaRodada = textoDaRodada;
70
73
  exports.textoDoReverify = textoDoReverify;
74
+ const fs = __importStar(require("node:fs"));
71
75
  const path = __importStar(require("node:path"));
76
+ const node_crypto_1 = require("node:crypto");
72
77
  const gates_1 = require("./gates");
73
78
  const ledger_1 = require("./ledger");
74
79
  const thread_1 = require("./thread");
@@ -76,6 +81,95 @@ const util_1 = require("./util");
76
81
  const verify_1 = require("./verify");
77
82
  const conducao_1 = require("./conducao");
78
83
  const locale_1 = require("./locale");
84
+ const hitl_1 = require("./hitl");
85
+ const LIMITE_DO_PARECER = 128 * 1024;
86
+ function vereditoBilíngue(texto) {
87
+ const linhasDeVeredito = [];
88
+ const malformadas = [];
89
+ const achados = new Set();
90
+ const mapa = {
91
+ passou: 'PASSOU', reprovou: 'REPROVOU', 'precisa de mudanca': 'PRECISA DE MUDANCA', bloqueado: 'BLOQUEADO',
92
+ passed: 'PASSOU', failed: 'REPROVOU', 'needs changes': 'PRECISA DE MUDANCA', blocked: 'BLOQUEADO',
93
+ };
94
+ for (const [i, linha] of texto.split(/\r?\n/).entries()) {
95
+ const rotulo = /^[#>*_`\s-]*(?:veredito|verdict)[*_`\s]*:[*_`\s]*(.*?)\s*$/iu.exec(linha);
96
+ if (!rotulo)
97
+ continue;
98
+ const listada = `${i + 1}: ${(0, hitl_1.redigirSegredos)(linha)}`;
99
+ linhasDeVeredito.push(listada);
100
+ const valor = rotulo[1].replace(/[*_`]+$/g, '').replace(/[.!]$/g, '').trim()
101
+ .normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase();
102
+ const canonico = mapa[valor];
103
+ if (canonico)
104
+ achados.add(canonico);
105
+ else
106
+ malformadas.push(listada);
107
+ }
108
+ if (linhasDeVeredito.length === 0)
109
+ throw new Error('fix.check.veredito-ausente: docs/check.md');
110
+ if (malformadas.length > 0 || achados.size !== 1) {
111
+ throw new Error(`fix.check.veredito-malformado: ${[...malformadas, ...linhasDeVeredito].join(' | ')}`);
112
+ }
113
+ return [...achados][0];
114
+ }
115
+ /** RM-079: le somente docs/check.md e extrai achados sem claim no contrato bilíngue do CHECK. */
116
+ function lerParecerParaFix(raiz, threadId) {
117
+ const arquivo = path.join((0, thread_1.dirThread)(raiz, threadId), 'docs', 'check.md');
118
+ let fd;
119
+ try {
120
+ fd = fs.openSync(arquivo, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW | fs.constants.O_NONBLOCK);
121
+ }
122
+ catch (e) {
123
+ if (e.code === 'ENOENT') {
124
+ throw new Error('fix.check.ausente: docs/check.md');
125
+ }
126
+ throw new Error('fix.check.unsafe');
127
+ }
128
+ try {
129
+ const st = fs.fstatSync(fd);
130
+ if (!st.isFile() || st.nlink !== 1 || st.size > LIMITE_DO_PARECER)
131
+ throw new Error('fix.check.unsafe');
132
+ const bytes = fs.readFileSync(fd);
133
+ if (bytes.length > LIMITE_DO_PARECER)
134
+ throw new Error('fix.check.too-large');
135
+ const texto = bytes.toString('utf8');
136
+ const linhas = texto.split(/\r?\n/);
137
+ const achados = [];
138
+ const linhasMalformadas = [];
139
+ const vistos = new Set();
140
+ const regra = /^\s*-\s+(A\d+)\s+\[(bloqueador|aviso|sugestao|blocker|warning|suggestion)\]\s+([A-Za-z0-9_./-]+):(\d+)\s+-\s+(.+?)\s*$/i;
141
+ const severidade = (valor) => ({ blocker: 'bloqueador', warning: 'aviso', suggestion: 'sugestao' }[valor.toLowerCase()]
142
+ ?? valor.toLowerCase());
143
+ for (let i = 0; i < linhas.length; i++) {
144
+ const m = regra.exec(linhas[i]);
145
+ if (!m) {
146
+ if (/^\s*-\s+A\d+\b/i.test(linhas[i]))
147
+ linhasMalformadas.push(`${i + 1}: ${(0, hitl_1.redigirSegredos)(linhas[i])}`);
148
+ continue;
149
+ }
150
+ if (vistos.has(m[1]))
151
+ throw new Error(`fix.check.finding.duplicado: ${m[1]}`);
152
+ const relativo = path.posix.normalize(m[3]);
153
+ if (relativo.startsWith('../') || relativo === '..' || path.posix.isAbsolute(relativo) || relativo.includes('\\')) {
154
+ throw new Error(`fix.check.finding.path: ${m[3]}`);
155
+ }
156
+ if (Number(m[4]) < 1)
157
+ throw new Error(`fix.check.finding.line: ${m[4]}`);
158
+ const proxima = linhas[i + 1] ?? '';
159
+ const reproducao = /^\s*(?:reproduzir|reproduce)\s*:\s*`([^`]+)`\s*$/i.exec(proxima);
160
+ achados.push({ id: m[1], severidade: severidade(m[2]), arquivo: relativo, linha: Number(m[4]),
161
+ descricao: (0, hitl_1.redigirSegredos)(m[5]), ...(reproducao ? { reproduzir: (0, hitl_1.redigirSegredos)(reproducao[1]) } : {}) });
162
+ vistos.add(m[1]);
163
+ }
164
+ if (linhasMalformadas.length > 0) {
165
+ throw new Error(`fix.check.finding.malformado: ${linhasMalformadas.join(' | ')}`);
166
+ }
167
+ return { veredito: vereditoBilíngue(texto), sha256: (0, node_crypto_1.createHash)('sha256').update(bytes).digest('hex'), achados };
168
+ }
169
+ finally {
170
+ fs.closeSync(fd);
171
+ }
172
+ }
79
173
  /** Armazem append-only das correcoes da thread. */
80
174
  function caminhoDosFixes(raiz, threadId) {
81
175
  return path.join((0, thread_1.dirThread)(raiz, threadId), 'fixes.jsonl');
@@ -132,6 +226,7 @@ function derivarCorrecoes(thread, rodada, resultado, idsUsados) {
132
226
  estado: 'aberta',
133
227
  vereditoEm: null,
134
228
  detalheDoVeredito: '',
229
+ commitQueFechou: null,
135
230
  criadaEm: (0, util_1.agora)(),
136
231
  });
137
232
  };
@@ -161,6 +256,28 @@ function derivarCorrecoes(thread, rodada, resultado, idsUsados) {
161
256
  }
162
257
  return correcoes;
163
258
  }
259
+ /** Achados sem claim entram na mesma rodada, mas so um novo parecer PASSOU consegue fecha-los. */
260
+ function derivarCorrecoesDoParecer(thread, rodada, parecer, idsUsados) {
261
+ const ids = [...idsUsados];
262
+ return parecer.achados.map((achado) => {
263
+ const id = (0, util_1.proximoIdSequencial)(ids, 'FX');
264
+ ids.push(id);
265
+ return {
266
+ id, thread: thread.id, rodada, tipo: 'B', origem: 'check.finding',
267
+ alvo: `${achado.arquivo}:${achado.linha}`,
268
+ spec: `${achado.id} [${achado.severidade}]: ${achado.descricao}. Corrija o artefato indicado; ` +
269
+ 'o fechamento exige um novo parecer independente com veredito PASSOU.',
270
+ verificar: [],
271
+ evidencia: [achado.reproduzir ? `reproduzir: ${achado.reproduzir}` : '',
272
+ `parecer sha256 ${parecer.sha256 ?? 'ausente'}`].filter(Boolean).join('\n'),
273
+ estado: 'aberta', vereditoEm: null, detalheDoVeredito: '', commitQueFechou: null,
274
+ achado: { id: achado.id, severidade: achado.severidade, arquivo: achado.arquivo, linha: achado.linha,
275
+ ...(achado.reproduzir ? { reproduzir: achado.reproduzir } : {}) },
276
+ parecerSha256: parecer.sha256 ?? undefined,
277
+ criadaEm: (0, util_1.agora)(),
278
+ };
279
+ });
280
+ }
164
281
  /** A spec EXATA que vai no prompt do GO-FIX. Sai dos dados, nunca de prosa livre. */
165
282
  function specDoGoFix(thread, rodada, correcoes) {
166
283
  const temTipoB = correcoes.some((c) => c.tipo === 'B');
@@ -217,7 +334,9 @@ function abrirRodadaSobConducao(carregado, threadId, opcoes) {
217
334
  // RM-058 N2 (A1 da rodada 2 do CHECK): a espera da fila pesada que o `--esperar` do CLI pediu.
218
335
  const resultado = (0, verify_1.verificar)(carregado, threadId, { soClaims: opcoes.soClaims, conducao: { filaPesada: opcoes.conducao?.filaPesada } });
219
336
  const rodada = ultimaRodada(raiz, threadId) + 1;
220
- if (resultado.ok && resultado.motivos.length === 0) {
337
+ const parecer = lerParecerParaFix(raiz, threadId);
338
+ const parecerNegativo = parecer.veredito !== 'PASSOU';
339
+ if (resultado.ok && resultado.motivos.length === 0 && !parecerNegativo) {
221
340
  return {
222
341
  thread: threadId,
223
342
  rodada: rodada - 1,
@@ -230,7 +349,11 @@ function abrirRodadaSobConducao(carregado, threadId, opcoes) {
230
349
  };
231
350
  }
232
351
  const idsUsados = lerCorrecoes(raiz, threadId).map((c) => c.id);
233
- const correcoes = derivarCorrecoes(thread, rodada, resultado, idsUsados);
352
+ if (parecerNegativo && parecer.achados.length === 0 && resultado.ok) {
353
+ throw new Error('fix.check.findings: parecer negativo sem achado estruturado; use A<n> [severidade] caminho:linha - descricao');
354
+ }
355
+ const doVerify = derivarCorrecoes(thread, rodada, resultado, idsUsados);
356
+ const correcoes = [...doVerify, ...(parecerNegativo ? derivarCorrecoesDoParecer(thread, rodada, parecer, [...idsUsados, ...doVerify.map((c) => c.id)]) : [])];
234
357
  for (const c of correcoes)
235
358
  (0, util_1.anexarJsonl)(caminhoDosFixes(raiz, threadId), c);
236
359
  const temTipoB = correcoes.some((c) => c.tipo === 'B');
@@ -244,6 +367,7 @@ function abrirRodadaSobConducao(carregado, threadId, opcoes) {
244
367
  motivos: resultado.motivos,
245
368
  commit: resultado.commit,
246
369
  evidencia: `ork verify reexecutado no HEAD ${resultado.commit} em ${resultado.cwd}`,
370
+ parecer: parecer.sha256 ? { sha256: parecer.sha256, veredito: parecer.veredito, achados: parecer.achados.map((a) => a.id) } : null,
247
371
  reexecucaoCompletaObrigatoria: temTipoB,
248
372
  });
249
373
  return {
@@ -258,12 +382,13 @@ function abrirRodadaSobConducao(carregado, threadId, opcoes) {
258
382
  };
259
383
  }
260
384
  /** Regrava uma correcao com o veredito do reverify. */
261
- function carimbar(raiz, correcao, aprovada, detalhe) {
385
+ function carimbar(raiz, correcao, aprovada, detalhe, commit) {
262
386
  const atualizada = {
263
387
  ...correcao,
264
388
  estado: aprovada ? 'aprovada' : 'reprovada',
265
389
  vereditoEm: (0, util_1.agora)(),
266
390
  detalheDoVeredito: detalhe,
391
+ commitQueFechou: aprovada ? (correcao.commitQueFechou ?? commit) : null,
267
392
  };
268
393
  (0, util_1.anexarJsonl)(caminhoDosFixes(raiz, correcao.thread), atualizada);
269
394
  return atualizada;
@@ -317,17 +442,25 @@ function reverificarSobConducao(carregado, threadId, opcoes) {
317
442
  aprovada: !falhou,
318
443
  execucoes,
319
444
  detalhe: falhou
320
- ? `"${falhou.comando}" ainda sai com codigo ${falhou.code}`
445
+ ? `"${(0, hitl_1.redigirSegredos)(falhou.comando)}" ainda sai com codigo ${falhou.code}`
321
446
  : `${execucoes.length} comando(s) reexecutado(s) com sucesso no HEAD real`,
322
447
  };
323
448
  });
324
449
  const verify = cobertura === 'completa' ? (0, verify_1.verificar)(carregado, threadId, { conducao: { filaPesada: opcoes.conducao?.filaPesada } }) : null;
450
+ const parecer = lerParecerParaFix(raiz, threadId);
325
451
  for (const v of vereditos) {
326
452
  // Correcao sem comando proprio herda o veredito do verify completo, e so quando ele
327
453
  // roda: sem ele, ela continua aberta em vez de virar aprovada por conveniencia.
328
- const aprovada = v.correcao.verificar.length === 0 ? (verify?.ok ?? false) : v.aprovada;
454
+ const eAchado = v.correcao.origem === 'check.finding';
455
+ const checkNovoPassou = eAchado && parecer.veredito === 'PASSOU' && parecer.sha256 !== v.correcao.parecerSha256;
456
+ const aprovada = eAchado ? Boolean(verify?.ok && checkNovoPassou)
457
+ : v.correcao.verificar.length === 0 ? (verify?.ok ?? false) : v.aprovada;
329
458
  v.aprovada = aprovada;
330
- carimbar(raiz, v.correcao, aprovada, v.detalhe);
459
+ if (eAchado)
460
+ v.detalhe = checkNovoPassou
461
+ ? 'novo parecer independente PASSOU e o verify completo ficou verde'
462
+ : 'aguarda novo parecer independente com veredito PASSOU';
463
+ v.correcao = carimbar(raiz, v.correcao, aprovada, v.detalhe, aprovada ? (verify?.commit ?? (0, verify_1.commitReal)(cwd)) : null);
331
464
  }
332
465
  const rodadas = rodada;
333
466
  const limite = manifesto.retry.max_tentativas;
@@ -417,6 +550,19 @@ function rodadasGastas(raiz, threadId) {
417
550
  return (0, ledger_1.lerLedger)((0, thread_1.dirThread)(raiz, threadId)).filter((e) => e.tipo === ledger_1.TIPOS_DE_EVENTO.fixAberto)
418
551
  .length;
419
552
  }
553
+ function statusDaRodada(raiz, threadId, rodada = ultimaRodada(raiz, threadId)) {
554
+ (0, thread_1.lerThread)(raiz, threadId);
555
+ if (rodada === 0)
556
+ return { thread: threadId, rodada: 0, estado: 'sem-rodada', achados: [], faltam: [] };
557
+ const correcoes = correcoesDaRodada(raiz, threadId, rodada);
558
+ if (correcoes.length === 0)
559
+ throw new Error(`rodada ${rodada} nao existe na thread ${threadId}`);
560
+ const achados = correcoes.map((c) => ({ correcao: c.id, achado: c.achado?.id ?? null, origem: c.origem,
561
+ arquivo: c.achado?.arquivo ?? null, linha: c.achado?.linha ?? null, estado: c.estado,
562
+ commitQueFechou: c.commitQueFechou ?? null }));
563
+ const faltam = achados.filter((a) => a.estado !== 'aprovada').map((a) => a.correcao);
564
+ return { thread: threadId, rodada, estado: faltam.length ? 'aberta' : 'concluida', achados, faltam };
565
+ }
420
566
  /** Texto de `ork fix open`. */
421
567
  function textoDaRodada(r) {
422
568
  const m = (0, locale_1.msg)().operacao.restante.fix;
@@ -259,7 +259,7 @@ function diagnosticoSemConclusao(resultado) {
259
259
  * gate e quem pergunta se um gate ainda espera o dono leem a mesma.
260
260
  */
261
261
  exports.MOTIVOS_DE_ESCALACAO_HUMANA = ['policy.violation', 'cost.violation', 'retry.max_tentativas',
262
- 'hitl.credencial', 'runtime.unavailable'];
262
+ 'hitl.credencial', 'runtime.unavailable', 'claims.retirada-sem-substituta'];
263
263
  /** Uma pausa do modo cria pedido, nunca human_gate. Escalação exige evento tipado. */
264
264
  function abrirPedidoGate(raiz, id, motivo = 'human.pending', quando = new Date().toISOString()) {
265
265
  const preparado = prepararPedidoGate(raiz, id, motivo, quando);
package/dist/index.js CHANGED
@@ -1413,12 +1413,14 @@ function comandoClaims(args) {
1413
1413
  return 2;
1414
1414
  }
1415
1415
  const comando = texto(args.opcoes.verificar) ?? texto(args.opcoes.verify);
1416
+ const dependenciasExternas = (texto(args.opcoes['dependencia-externa']) ?? '').split(',').map((d) => d.trim()).filter(Boolean);
1416
1417
  const faseBruta = texto(args.opcoes.fase);
1417
1418
  const thread = (0, thread_1.lerThread)(carregado.raiz, id);
1418
1419
  const claim = (0, locale_1.comLocale)('pt-BR', () => (0, claims_1.adicionarClaim)(carregado.raiz, id, {
1419
1420
  arquivo,
1420
1421
  alegacao,
1421
1422
  verificar: comando ? [comando] : [],
1423
+ dependenciasExternas,
1422
1424
  fase: faseBruta ? (0, thread_1.exigirFase)(thread, faseBruta) : undefined,
1423
1425
  }));
1424
1426
  console.log(mc.registrada(claim.id, id));
@@ -1500,14 +1502,40 @@ function comandoClaims(args) {
1500
1502
  const id = args.posicionais[2];
1501
1503
  const claimId = args.posicionais[3];
1502
1504
  const motivo = texto(args.opcoes.motivo);
1505
+ const motivoTipo = texto(args.opcoes['motivo-tipo']);
1506
+ const substituidaPor = texto(args.opcoes['substituida-por']);
1503
1507
  if (!id || !claimId || !motivo) {
1504
1508
  console.error(mc.usoRetirar);
1505
1509
  return 2;
1506
1510
  }
1507
- const claim = (0, locale_1.comLocale)('pt-BR', () => (0, claims_1.retirarClaim)(carregado.raiz, id, claimId, motivo));
1508
- console.log(mc.retirada(claim.id, id));
1509
- console.log(mc.campoAlegacao(claim.alegacao));
1510
- console.log(mc.campoMotivo(motivo));
1511
+ const dispatchId = (0, conducao_1.identidadeDoAmbiente)(id, process.env, carregado.raiz);
1512
+ const autor = dispatchId ? { tipo: 'sessao', id: dispatchId } : { tipo: 'dono', id: 'terminal-local' };
1513
+ const motivoSeguro = (0, hitl_1.redigirSegredos)(motivo);
1514
+ if (!motivoTipo) {
1515
+ const claim = (0, locale_1.comLocale)('pt-BR', () => (0, claims_1.retirarClaim)(carregado.raiz, id, claimId, motivo, autor));
1516
+ console.log(mc.retirada(claim.id, id));
1517
+ console.log(mc.retiradaTipada(false));
1518
+ console.log(mc.autorDaRetirada(autor.tipo, autor.id));
1519
+ console.log(mc.campoAlegacao(claim.alegacao));
1520
+ console.log(mc.campoMotivo(motivoSeguro));
1521
+ console.log(mc.historicoContinua);
1522
+ return 0;
1523
+ }
1524
+ const resultado = (0, locale_1.comLocale)('pt-BR', () => (0, claims_1.retirarClaimTipada)(carregado.raiz, id, claimId, { motivoTipo, substituidaPor, motivo, autor }));
1525
+ if (resultado.estado === 'pendente-do-dono') {
1526
+ (0, hitl_gates_1.abrirPedidoGate)(carregado.raiz, id, resultado.motivo);
1527
+ console.log(mc.retiradaPendente(claimId));
1528
+ console.log(mc.retiradaTipada(true));
1529
+ console.log(mc.autorDaRetirada(autor.tipo, autor.id));
1530
+ console.log(mc.campoMotivo(motivoSeguro));
1531
+ return 1;
1532
+ }
1533
+ console.log(mc.retirada(resultado.claim.id, id));
1534
+ console.log(mc.retiradaTipada(true));
1535
+ console.log(mc.autorDaRetirada(autor.tipo, autor.id));
1536
+ console.log(mc.substitutaDaRetirada(resultado.substituidaPor));
1537
+ console.log(mc.campoAlegacao(resultado.claim.alegacao));
1538
+ console.log(mc.campoMotivo(motivoSeguro));
1511
1539
  console.log(mc.historicoContinua);
1512
1540
  return 0;
1513
1541
  }
@@ -2026,6 +2054,27 @@ function comandoFix(args) {
2026
2054
  ])));
2027
2055
  return 0;
2028
2056
  }
2057
+ if (sub === 'status') {
2058
+ if (!id) {
2059
+ console.error(mf.usoStatus);
2060
+ return 2;
2061
+ }
2062
+ const rodadaBruta = texto(args.opcoes.rodada);
2063
+ const status = (0, fix_1.statusDaRodada)(carregado.raiz, id, rodadaBruta ? Number(rodadaBruta) : undefined);
2064
+ if (args.opcoes.json === true) {
2065
+ console.log(JSON.stringify(status, null, 2));
2066
+ return 0;
2067
+ }
2068
+ console.log(mf.status(status.rodada, id));
2069
+ console.log(mf.estado(status.estado));
2070
+ console.log(mf.faltam(status.faltam.length ? status.faltam.join(', ') : mf.semFaltas));
2071
+ if (status.achados.length) {
2072
+ console.log('');
2073
+ console.log((0, util_1.tabela)([...mf.colunasStatus], status.achados.map((a) => [a.correcao, a.achado ?? '-', a.origem,
2074
+ a.arquivo && a.linha ? `${a.arquivo}:${a.linha}` : '-', a.estado, a.commitQueFechou ?? '-'])));
2075
+ }
2076
+ return 0;
2077
+ }
2029
2078
  if (sub === 'reverify') {
2030
2079
  if (!id) {
2031
2080
  console.error(mf.usoReverify);
@@ -141,7 +141,7 @@ Uso: ork [--projeto <nome|caminho>] <comando> [argumentos]
141
141
  [--limiares <arquivo>] os limiares do dono; o que faltar fica com a proposta, marcada
142
142
  [--publicar <arquivo>] grava a versão publicável, sem dado pessoal
143
143
 
144
- claims add <thread-id> <arquivo> --claim "<alegacao>" [--verificar "<comando>"] [--fase F]
144
+ claims add <thread-id> <arquivo> --claim "<alegacao>" [--verificar "<comando>"] [--dependencia-externa rede,relogio] [--fase F]
145
145
  Registra alegacao verificavel da thread
146
146
  claims list <thread-id> Claims da thread e o estado de cada uma
147
147
  claims conferir <thread-id> --claims C1,C2|--tarefa T1 [--prazo S] [--json]
@@ -154,7 +154,8 @@ Uso: ork [--projeto <nome|caminho>] <comando> [argumentos]
154
154
  decisao placar <thread-id> [--json] Decididas contra perguntas por fase, reversoes e o limiar
155
155
  claims verificar <thread-id> <claim-id> --comando "<comando>"
156
156
  Anexa o comando que comprova uma claim ja feita
157
- claims retirar <thread-id> <claim-id> --motivo "<motivo>"
157
+ claims retirar <thread-id> <claim-id> [--motivo-tipo substituicao|obsoleta|erro]
158
+ [--substituida-por <claim-id>] --motivo "<motivo>"
158
159
  Retira a alegacao (o historico fica no claims.jsonl)
159
160
  claims ausente <thread-id> --paths A,B --motivo "<motivo>" [--commit SHA]
160
161
  Ciclo sem CHECK: declara no ledger o que ficou sem prova
@@ -191,6 +192,8 @@ Uso: ork [--projeto <nome|caminho>] <comando> [argumentos]
191
192
  fix open <thread-id> [--so-claims] GO-FIX: deriva a spec exata do CHECK reprovado
192
193
  [--reverify] [--json] (uma correcao por motivo tipado, tipo A ou B)
193
194
  fix list <thread-id> [--rodada N] Correcoes da rodada e o veredito de cada uma
195
+ fix status <thread-id> [--rodada N] Achados, commit que fechou e o que falta
196
+ [--json]
194
197
  fix reverify <thread-id> [--rodada N] CHECK-REVERIFY com veredito POR correcao
195
198
  [--parcial] [--json] (--parcial e RECUSADO se houver tipo B)
196
199
 
@@ -555,7 +558,7 @@ Usage: ork [--projeto <name|path>] <command> [arguments]
555
558
  [--limiares <file>] the owner's thresholds; what is missing stays with the proposal, marked
556
559
  [--publicar <file>] writes the publishable version, without personal data
557
560
 
558
- claims add <thread-id> <file> --claim "<claim>" [--verificar "<command>"] [--fase F]
561
+ claims add <thread-id> <file> --claim "<claim>" [--verificar "<command>"] [--dependencia-externa rede,relogio] [--fase F]
559
562
  Records a verifiable claim of the thread
560
563
  claims list <thread-id> Claims of the thread and the state of each one
561
564
  claims conferir <thread-id> --claims C1,C2|--tarefa T1 [--prazo S] [--json]
@@ -568,7 +571,8 @@ Usage: ork [--projeto <name|path>] <command> [arguments]
568
571
  decisao placar <thread-id> [--json] Decided versus asked per phase, reversals and the threshold
569
572
  claims verificar <thread-id> <claim-id> --comando "<command>"
570
573
  Attaches the command that proves a claim already made
571
- claims retirar <thread-id> <claim-id> --motivo "<reason>"
574
+ claims retirar <thread-id> <claim-id> [--motivo-tipo substituicao|obsoleta|erro]
575
+ [--substituida-por <claim-id>] --motivo "<reason>"
572
576
  Withdraws the claim (the history stays in claims.jsonl)
573
577
  claims ausente <thread-id> --paths A,B --motivo "<reason>" [--commit SHA]
574
578
  Cycle without CHECK: declares in the ledger what was left without proof
@@ -605,6 +609,8 @@ Usage: ork [--projeto <name|path>] <command> [arguments]
605
609
  fix open <thread-id> [--so-claims] GO-FIX: derives the exact spec of the failed CHECK
606
610
  [--reverify] [--json] (one fix per typed reason, type A or B)
607
611
  fix list <thread-id> [--rodada N] Fixes of the round and the verdict of each one
612
+ fix status <thread-id> [--rodada N] Findings, closing commit, and remaining work
613
+ [--json]
608
614
  fix reverify <thread-id> [--rodada N] CHECK-REVERIFY with a verdict PER fix
609
615
  [--parcial] [--json] (--parcial is REFUSED if there is a type B)
610
616