@orkastery/cli 0.6.5 → 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 (42) 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/session-watcher.js +94 -23
  37. package/dist/ship.js +1 -1
  38. package/dist/temporario-das-lanes.js +94 -0
  39. package/dist/verify.js +662 -410
  40. package/eval/fixtures/fx-sensores-runtime/caso.json +1 -1
  41. package/package.json +1 -1
  42. package/skills/phases/check-quality/SKILL.md +14 -0
@@ -36,7 +36,7 @@ const ESTADOS_EN = {
36
36
  };
37
37
  exports.CLAIMS_PT_BR = {
38
38
  // ork claims add
39
- usoAdd: 'uso: ork claims add <thread-id> <arquivo> --claim "<alegacao>" [--verificar "<comando>"]',
39
+ usoAdd: 'uso: ork claims add <thread-id> <arquivo> --claim "<alegacao>" [--verificar "<comando>"] [--dependencia-externa rede,relogio]',
40
40
  registrada: (id, thread) => `Claim ${id} registrada na thread ${thread}.`,
41
41
  campoArquivo: (arquivo) => ` arquivo ${arquivo}`,
42
42
  campoAlegacao: (alegacao) => ` alegacao ${alegacao}`,
@@ -54,7 +54,9 @@ exports.CLAIMS_PT_BR = {
54
54
  nomesDoLint: { 'suite-inteira': 'roda a suite inteira', 'sha-intermediario': 'fixa um SHA intermediario',
55
55
  'contagem-de-commits': 'compara contagem de commits', 'binario-rg': 'usa o binario rg ausente no runner',
56
56
  'ork-fora-do-path': 'chama ork fora do PATH do runner', 'estado-local': 'le estado local fora do checkout',
57
- 'aspas-escapadas': 'leva aspas escapadas literalmente' },
57
+ 'aspas-escapadas': 'leva aspas escapadas literalmente',
58
+ 'contexto-artificial': 'executa contexto artificial isolado',
59
+ 'artefato-de-fechamento': 'le artefato gerado pelo fechamento' },
58
60
  correcoesDoLint: {
59
61
  'suite-inteira': 'rode só o teste da claim (`node --test` no arquivo do teste, ou o comando do projeto que roda um arquivo) ' +
60
62
  'ou um script hermético do projeto',
@@ -64,6 +66,8 @@ exports.CLAIMS_PT_BR = {
64
66
  'ork-fora-do-path': 'use `node core/dist/index.js ...`; o runner nao instala `ork` no PATH',
65
67
  'estado-local': 'prove somente arquivos versionados do checkout; `.orkastery/` e estado local e nao vai ao CI',
66
68
  'aspas-escapadas': 'use aspas reais do shell; nao serialize `\\x22` ou `\\x27` dentro do comando',
69
+ 'contexto-artificial': 'exercite a API publica ou um teste do produto no contexto real, sem reconstruir a funcao em `node -e`, `vm` ou `new Function`',
70
+ 'artefato-de-fechamento': 'prove a fonte versionada que gera o artefato; o artefato que contem a claim nao pode provar a si proprio',
67
71
  },
68
72
  // ork claims conferir
69
73
  usoConferir: 'uso: ork claims conferir <thread-id> --claims C1,C2 | --tarefa T1 [--prazo S] [--json]',
@@ -111,8 +115,12 @@ exports.CLAIMS_PT_BR = {
111
115
  usoVerificar: 'uso: ork claims verificar <thread-id> <claim-id> --comando "<comando>"',
112
116
  comandoAnexado: (id) => `Claim ${id} agora tem comando de verificacao.`,
113
117
  proveAgora: (thread) => `Prove agora: ork verify ${thread}`,
114
- usoRetirar: 'uso: ork claims retirar <thread-id> <claim-id> --motivo "<motivo>"',
118
+ usoRetirar: 'uso: ork claims retirar <thread-id> <claim-id> [--motivo-tipo substituicao|obsoleta|erro] [--substituida-por <claim-id>] --motivo "<motivo>"',
115
119
  retirada: (id, thread) => `Claim ${id} retirada da thread ${thread}.`,
120
+ retiradaTipada: (tipada) => ` retirada ${tipada ? 'tipada' : 'nao tipada (compatibilidade)'}`,
121
+ autorDaRetirada: (tipo, id) => ` autor ${tipo}:${id}`,
122
+ retiradaPendente: (id) => `Claim ${id} continua ativa; retirada sem substituta enviada ao dono.`,
123
+ substitutaDaRetirada: (id) => ` substituta ${id}`,
116
124
  historicoContinua: ' o historico da alegacao continua no claims.jsonl e no ledger.',
117
125
  usoAusente: 'uso: ork claims ausente <thread-id> --paths A,B --motivo "<motivo>" [--commit SHA]',
118
126
  ausenciaRegistrada: (thread, paths) => `Ausencia de prova registrada na thread ${thread}: ${paths}.`,
@@ -134,7 +142,7 @@ exports.CLAIMS_PT_BR = {
134
142
  ausenteSemCaminhos: 'prova.ausente: informe os caminhos que ficaram sem prova',
135
143
  };
136
144
  exports.CLAIMS_EN = {
137
- usoAdd: 'usage: ork claims add <thread-id> <file> --claim "<statement>" [--verificar "<command>"]',
145
+ usoAdd: 'usage: ork claims add <thread-id> <file> --claim "<statement>" [--verificar "<command>"] [--dependencia-externa rede,relogio]',
138
146
  registrada: (id, thread) => `Claim ${id} recorded in thread ${thread}.`,
139
147
  campoArquivo: (arquivo) => ` file ${arquivo}`,
140
148
  campoAlegacao: (alegacao) => ` statement ${alegacao}`,
@@ -150,7 +158,9 @@ exports.CLAIMS_EN = {
150
158
  nomesDoLint: { 'suite-inteira': 'runs the whole suite', 'sha-intermediario': 'pins an intermediate SHA',
151
159
  'contagem-de-commits': 'compares a commit count', 'binario-rg': 'uses the rg binary missing from the runner',
152
160
  'ork-fora-do-path': 'calls ork outside the runner PATH', 'estado-local': 'reads local state outside the checkout',
153
- 'aspas-escapadas': 'passes escaped quotes literally' },
161
+ 'aspas-escapadas': 'passes escaped quotes literally',
162
+ 'contexto-artificial': 'runs an isolated artificial context',
163
+ 'artefato-de-fechamento': 'reads an artifact generated by phase closing' },
154
164
  correcoesDoLint: {
155
165
  'suite-inteira': 'run only the claim test (`node --test` on the test file, or the project command that runs one file) ' +
156
166
  'or a hermetic project script',
@@ -160,6 +170,8 @@ exports.CLAIMS_EN = {
160
170
  'ork-fora-do-path': 'use `node core/dist/index.js ...`; the runner does not install `ork` in PATH',
161
171
  'estado-local': 'prove only versioned checkout files; `.orkastery/` is local state and is not sent to CI',
162
172
  'aspas-escapadas': 'use real shell quotes; do not serialize `\\x22` or `\\x27` inside the command',
173
+ 'contexto-artificial': 'exercise the public API or a product test in its real context instead of rebuilding the function with `node -e`, `vm`, or `new Function`',
174
+ 'artefato-de-fechamento': 'prove the versioned source that generates the artifact; an artifact containing the claim cannot prove itself',
163
175
  },
164
176
  usoConferir: 'usage: ork claims conferir <thread-id> --claims C1,C2 | --tarefa T1 [--prazo S] [--json]',
165
177
  conferencia: (thread) => `Spot check of thread ${thread} (no receipt: \`ork verify\` is still what verifies)`,
@@ -205,8 +217,12 @@ exports.CLAIMS_EN = {
205
217
  usoVerificar: 'usage: ork claims verificar <thread-id> <claim-id> --comando "<command>"',
206
218
  comandoAnexado: (id) => `Claim ${id} now has a verification command.`,
207
219
  proveAgora: (thread) => `Prove it now: \`ork verify ${thread}\``,
208
- usoRetirar: 'usage: ork claims retirar <thread-id> <claim-id> --motivo "<reason>"',
220
+ usoRetirar: 'usage: ork claims retirar <thread-id> <claim-id> [--motivo-tipo substituicao|obsoleta|erro] [--substituida-por <claim-id>] --motivo "<reason>"',
209
221
  retirada: (id, thread) => `Claim ${id} withdrawn from thread ${thread}.`,
222
+ retiradaTipada: (tipada) => ` withdrawal ${tipada ? 'typed' : 'untyped (compatibility)'}`,
223
+ autorDaRetirada: (tipo, id) => ` author ${tipo}:${id}`,
224
+ retiradaPendente: (id) => `Claim ${id} remains active; withdrawal without a replacement was sent to the owner.`,
225
+ substitutaDaRetirada: (id) => ` replacement ${id}`,
210
226
  historicoContinua: ' the history of the statement stays in claims.jsonl and in the ledger.',
211
227
  usoAusente: 'usage: ork claims ausente <thread-id> --paths A,B --motivo "<reason>" [--commit SHA]',
212
228
  ausenciaRegistrada: (thread, paths) => `Missing proof recorded in thread ${thread}: ${paths}.`,
@@ -102,6 +102,12 @@ exports.OPERACAO_RESTANTE_PT_BR = {
102
102
  fix: {
103
103
  usoOpen: 'uso: ork fix open <thread-id> [--so-claims] [--reverify]',
104
104
  usoList: 'uso: ork fix list <thread-id> [--rodada N]',
105
+ usoStatus: 'uso: ork fix status <thread-id> [--rodada N] [--json]',
106
+ status: (rodada, thread) => `Estado da rodada ${rodada} da thread ${thread}`,
107
+ estado: (estado) => ` estado ${estado}`,
108
+ faltam: (itens) => ` faltam ${itens}`,
109
+ semFaltas: '(nada)',
110
+ colunasStatus: ['CORRECAO', 'ACHADO', 'ORIGEM', 'LOCAL', 'ESTADO', 'COMMIT QUE FECHOU'],
105
111
  semRodada: (thread) => `A thread ${thread} nao tem rodada de GO-FIX. Abra com: ork fix open ${thread}`,
106
112
  lista: (rodada, thread) => `Correcoes da rodada ${rodada} da thread ${thread}`,
107
113
  colunasLista: ['ID', 'TIPO', 'MOTIVO', 'ALVO', 'ESTADO', 'JULGADO POR'],
@@ -578,6 +584,12 @@ exports.OPERACAO_RESTANTE_EN = {
578
584
  fix: {
579
585
  usoOpen: 'usage: ork fix open <thread-id> [--so-claims] [--reverify]',
580
586
  usoList: 'usage: ork fix list <thread-id> [--rodada N]',
587
+ usoStatus: 'usage: ork fix status <thread-id> [--rodada N] [--json]',
588
+ status: (round, thread) => `Status of round ${round} of thread ${thread}`,
589
+ estado: (state) => ` state ${state}`,
590
+ faltam: (items) => ` remaining ${items}`,
591
+ semFaltas: '(nothing)',
592
+ colunasStatus: ['FIX', 'FINDING', 'SOURCE', 'LOCATION', 'STATE', 'CLOSING COMMIT'],
581
593
  semRodada: (thread) => `Thread ${thread} has no GO-FIX round. Open one with: ork fix open ${thread}`,
582
594
  lista: (round, thread) => `Fixes from round ${round} of thread ${thread}`,
583
595
  colunasLista: ['ID', 'TYPE', 'REASON', 'TARGET', 'STATE', 'JUDGED BY'],
@@ -9,7 +9,8 @@ exports.PROMPTS_PT_BR = {
9
9
  PLAN: 'PLAN (F2): plano com tarefas, touch_paths, decisoes D1..Dn com domicilio unico e verify executavel por tarefa. Nao implementa.',
10
10
  GO: 'GO (F3): implementacao slice por slice, um commit atomico por tarefa, dentro da worktree da thread.',
11
11
  CHECK: 'CHECK (F4): verificacao contra a baseline (regressao versus divida pre-existente), review de codigo, testes, seguranca e performance; ' +
12
- 'o parecer diz o modo do CHECK da rodada (Agile Check ou Classic Check, da linha `check` do `ork verify`).',
12
+ 'o parecer diz o modo do CHECK da rodada (Agile Check ou Classic Check, da linha `check` do `ork verify`). ' +
13
+ 'Claim nova afirma o comportamento correto e passa com a correcao; a reproducao do defeito fica como comando no parecer e nunca vira claim.',
13
14
  SHIP: 'SHIP (F5): merge serializado, push provado por comando, roadmap atualizado e plano de rollback.',
14
15
  MASTER: 'MASTER (F6): MASTER log com postmortem tipado, licoes e o score humano de 0 a 5. Uma entrega sem MASTER log nao aconteceu.',
15
16
  },
@@ -103,7 +104,8 @@ exports.PROMPTS_EN = {
103
104
  PLAN: 'PLAN (F2): sliced tasks, queryable touch_paths, D1..Dn decisions with one home, and executable verify per task. Does not implement.',
104
105
  GO: 'GO (F3): implement slice by slice, one atomic commit per task, inside the thread worktree.',
105
106
  CHECK: 'CHECK (F4): verify against the baseline (regressions versus pre-existing debt), then review code, tests, security, and performance; ' +
106
- 'the verdict names the CHECK mode of the round (Agile Check or Classic Check, from the `check` line of `ork verify`).',
107
+ 'the verdict names the CHECK mode of the round (Agile Check or Classic Check, from the `check` line of `ork verify`). ' +
108
+ 'A new claim states the correct behavior and passes with the fix; defect reproduction stays as a command in the report and never becomes a claim.',
107
109
  SHIP: 'SHIP (F5): serialized merge, command-proven push, updated roadmap, and rollback plan.',
108
110
  MASTER: 'MASTER (F6): typed POSTMORTEM, MASTER log, lessons, and a human score from 0 to 5. A delivery without a MASTER log did not happen.',
109
111
  },
@@ -172,6 +172,7 @@ exports.PULSE_PT_BR = {
172
172
  'artifact.missing': 'artefato exigido pela fase nao existe no disco',
173
173
  'claims.failed': 'alegacao verificavel reprovou na reexecucao no HEAD real',
174
174
  'claims.unverifiable': 'alegacao sem comando de verificacao declarado',
175
+ 'claims.retirada-sem-substituta': 'retirada tipada sem claim substituta verde espera decisao do dono',
175
176
  'policy.violation': 'policy do manifesto com severidade block foi violada',
176
177
  'runtime.autoconferencia': 'o CHECK foi despachado no mesmo runtime que fez o GO, e a thread exige validacao cruzada',
177
178
  'verify.regression': 'comando que passava na baseline falha agora',
@@ -324,6 +325,7 @@ exports.PULSE_EN = {
324
325
  'artifact.missing': 'an artifact the phase requires does not exist on disk',
325
326
  'claims.failed': 'a verifiable claim failed when rerun on the real HEAD',
326
327
  'claims.unverifiable': 'a claim with no verification command declared',
328
+ 'claims.retirada-sem-substituta': 'typed withdrawal without a green replacement claim waits for the owner',
327
329
  'policy.violation': 'a manifest policy with block severity was violated',
328
330
  'runtime.autoconferencia': 'CHECK was dispatched on the same runtime that did GO, and the thread requires cross-validation',
329
331
  'verify.regression': 'a command that passed in the baseline fails now',
@@ -26,6 +26,10 @@ exports.RETRY_PT_BR = {
26
26
  porque: 'alegacao sem comando de verificacao vira correcao tipo A: anexar o comando (ou retirar a alegacao com motivo) e uma linha, nao um GO inteiro',
27
27
  correcao: 'ork claims verificar <thread> <claim> --comando "<comando>"',
28
28
  },
29
+ 'claims.retirada-sem-substituta': {
30
+ porque: 'retirar uma regra sem substituta verde apaga evidencia ativa: a maquina nao decide sozinha que a alegacao deixou de valer',
31
+ correcao: 'o dono decide pelo pedido tipado; ou registre e verifique uma claim substituta antes de repetir a retirada',
32
+ },
29
33
  'policy.violation': {
30
34
  porque: 'policy com severidade block reprova em qualquer modo: reexecutar sozinho seria a maquina revogando a policy do manifesto',
31
35
  correcao: 'corrija a violacao no pedido/manifesto; policy block nao se resolve por repeticao',
@@ -312,6 +316,10 @@ exports.RETRY_EN = {
312
316
  porque: 'a claim without a verification command becomes a type A fix: attaching the command (or withdrawing the claim with a reason) is one line, not a whole GO',
313
317
  correcao: 'ork claims verificar <thread> <claim> --comando "<command>"',
314
318
  },
319
+ 'claims.retirada-sem-substituta': {
320
+ porque: 'withdrawing a rule without a green replacement removes active evidence: the machine cannot decide by itself that the claim no longer applies',
321
+ correcao: 'the owner decides through the typed request; or record and verify a replacement claim before retrying the withdrawal',
322
+ },
315
323
  'policy.violation': {
316
324
  porque: 'a policy with block severity fails in any mode: rerunning on its own would be the machine revoking the manifest policy',
317
325
  correcao: 'fix the violation in the request or the manifest; a block policy is not solved by repetition',
@@ -54,8 +54,12 @@ exports.VERIFY_PT_BR = {
54
54
  agile: (reaproveitadas, inteiras, paralelo, lanes) => ` agile ${reaproveitadas} comando(s) de claim contra o produto do preparo, ${inteiras} inteiro(s); ` +
55
55
  `${paralelo ? `grupo em paralelo${lanes.length ? ` com as lanes ${lanes.join(', ')}` : ''}` : 'comandos do manifesto em serie'}`,
56
56
  agileFallback: (peca, motivo, fallback) => ` fallback ${peca}: ${motivo || 'sem motivo dito'}; rodou ${fallback || 'o equivalente do Classic'}`,
57
+ tmpdirDaRodada: 'TMPDIR compartilhado pela rodada',
58
+ discoComTmpdirDaRodada: 'disco com TMPDIR da rodada',
57
59
  agileDegradado: (peca, motivo) => ` degradado ${peca}: ${motivo || 'sem motivo dito'}; preservou o plano paralelo`,
58
60
  agilePeca: (peca, motivo) => ` agile ${peca}: ${motivo || 'sem motivo dito'}`,
61
+ claimLaneReaproveitada: (claim, evidencia) => ` claim-lane ${claim}: reaproveitada das lanes (${evidencia || 'evidencia nominal da rodada'})`,
62
+ claimLaneRecusada: (claim, motivo) => ` claim-lane ${claim}: recusada (${motivo}); rodou a claim inteira`,
59
63
  reservaIntegracao: (cpus, fonte) => `reserva da integracao ${cpus} CPU(s), origem ${{ medida: 'medida', conservadora: 'conservadora', classic: 'Classic' }[fonte]}`,
60
64
  preparo: (situacao, segundos, alterado) => ` preparo ${situacao} em ` +
61
65
  `${segundos} s, uma vez para a rodada; produto ` +
@@ -173,8 +177,12 @@ exports.VERIFY_EN = {
173
177
  agile: (reaproveitadas, inteiras, paralelo, lanes) => ` agile ${reaproveitadas} claim command(s) against the prepared product, ${inteiras} in full; ` +
174
178
  `${paralelo ? `parallel group${lanes.length ? ` with lanes ${lanes.join(', ')}` : ''}` : 'manifest commands in series'}`,
175
179
  agileFallback: (peca, motivo, fallback) => ` fallback ${peca}: ${motivo || 'no reason given'}; ran ${fallback || 'the Classic equivalent'}`,
180
+ tmpdirDaRodada: 'TMPDIR shared by the run',
181
+ discoComTmpdirDaRodada: 'disk with the run TMPDIR',
176
182
  agileDegradado: (peca, motivo) => ` degraded ${peca}: ${motivo || 'no reason given'}; kept the parallel plan`,
177
183
  agilePeca: (peca, motivo) => ` agile ${peca}: ${motivo || 'no reason given'}`,
184
+ claimLaneReaproveitada: (claim, evidencia) => ` claim-lane ${claim}: reused from lanes (${evidencia || 'nominal evidence from this run'})`,
185
+ claimLaneRecusada: (claim, motivo) => ` claim-lane ${claim}: refused (${motivo}); ran the full claim`,
178
186
  reservaIntegracao: (cpus, fonte) => `integration reservation ${cpus} CPU(s), ` +
179
187
  `source ${{ medida: 'measured', conservadora: 'conservative', classic: 'Classic' }[fonte]}`,
180
188
  preparo: (situacao, segundos, alterado) => ` prepare ${situacao} in ${segundos} s, once for the run; product ` +
@@ -39,6 +39,7 @@ exports.lerArtefatoMcp = lerArtefatoMcp;
39
39
  exports.escreverArtefatoMcp = escreverArtefatoMcp;
40
40
  exports.listarClaimsMcp = listarClaimsMcp;
41
41
  exports.adicionarClaimMcp = adicionarClaimMcp;
42
+ exports.retirarClaimMcp = retirarClaimMcp;
42
43
  /** Documentos do agente, separados dos recibos oficiais do nucleo. Backend Linux como HITL. */
43
44
  const fs = __importStar(require("node:fs"));
44
45
  const path = __importStar(require("node:path"));
@@ -47,6 +48,7 @@ const thread_1 = require("./thread");
47
48
  const hitl_gates_1 = require("./hitl-gates");
48
49
  const leases_1 = require("./leases");
49
50
  const claims_1 = require("./claims");
51
+ const verify_1 = require("./verify");
50
52
  const fases = { goal: 'GOAL', plan: 'PLAN', check: 'CHECK' };
51
53
  const limite = 128 * 1024;
52
54
  const hash = (bytes) => (0, node_crypto_1.createHash)('sha256').update(bytes).digest('hex');
@@ -267,6 +269,8 @@ function adicionarClaimMcp(raiz, id, entrada) {
267
269
  if (!entrada.alegacao.trim() || entrada.alegacao.length > 8192 || entrada.verificar.length > 20 ||
268
270
  entrada.verificar.some(c => typeof c !== 'string' || !c.trim() || c.length > 8192))
269
271
  throw Error('mcp.claim.invalid');
272
+ if (entrada.dependenciasExternas?.some(d => d !== 'rede' && d !== 'relogio'))
273
+ throw Error('mcp.claim.external-dependency.invalid');
270
274
  return comDocs(raiz, id, () => comEscrita(raiz, id, () => {
271
275
  if ((0, thread_1.lerThread)(raiz, id).status === 'fechada')
272
276
  throw Error('mcp.claim.closed');
@@ -274,3 +278,23 @@ function adicionarClaimMcp(raiz, id, entrada) {
274
278
  return (0, claims_1.adicionarClaim)(raiz, id, { ...entrada, semProvaLocal: true });
275
279
  }));
276
280
  }
281
+ function claimDaRetiradaMcp(claim) {
282
+ const { verificar, ...projecao } = claim;
283
+ return { ...projecao, verificarSha256: (0, verify_1.impressaoDosComandos)(verificar) };
284
+ }
285
+ function retirarClaimMcp(raiz, id, entrada) {
286
+ validarId(id);
287
+ if (!/^C\d+$/.test(entrada.claimId) || entrada.substituidaPor !== undefined && !/^C\d+$/.test(entrada.substituidaPor) ||
288
+ !entrada.motivo.trim() || entrada.motivo.length > 8192)
289
+ throw Error('mcp.claim.withdraw.invalid');
290
+ const resultado = comDocs(raiz, id, () => comEscrita(raiz, id, () => {
291
+ if ((0, thread_1.lerThread)(raiz, id).status === 'fechada')
292
+ throw Error('mcp.claim.closed');
293
+ return (0, claims_1.retirarClaimTipada)(raiz, id, entrada.claimId, entrada);
294
+ }));
295
+ const resposta = { ...resultado, claim: claimDaRetiradaMcp(resultado.claim) };
296
+ if (resultado.estado !== 'pendente-do-dono')
297
+ return resposta;
298
+ const pedido = (0, hitl_gates_1.abrirPedidoGate)(raiz, id, resultado.motivo);
299
+ return { ...resposta, pedidoId: pedido.id };
300
+ }
@@ -55,7 +55,7 @@ const IMPRESSAO_SSH = /^SHA256:[A-Za-z0-9+/]{43}$/;
55
55
  exports.TOOLS_DONO_CODEX = ['ork_thread_new', 'ork_phase_run', 'ork_request_decision'];
56
56
  // RM-037 (rm037defeito, defeito 1): a decisao autonoma da sessao entra com as outras mutacoes do filho;
57
57
  // sem o grant, o codex com approval_policy never recusaria a chamada.
58
- exports.TOOLS_FILHO_CODEX = ['ork_artifact_write', 'ork_claim_add', 'ork_decision_record', 'ork_request_conduction',
58
+ exports.TOOLS_FILHO_CODEX = ['ork_artifact_write', 'ork_claim_add', 'ork_claim_withdraw', 'ork_decision_record', 'ork_request_conduction',
59
59
  'ork_git_commit', 'ork_verify', 'ork_verify_status', 'ork_ship'];
60
60
  // Brain mutation tools are deliberately excluded from the existing opt-in bundle.
61
61
  // A native integration must request exact startup grants plus scoped activation.
@@ -531,7 +531,17 @@ function criarServidorMcp(opcoes) {
531
531
  });
532
532
  registrarTool('ork_claim_add', { description: 'Registra alegacao prospectiva e comandos como dados. Nao executa comandos nem declara a alegacao verificada.',
533
533
  inputSchema: daThread.extend({ arquivo: zod_1.z.string().min(1).max(500), alegacao: zod_1.z.string().min(1).max(8192),
534
- verificar: zod_1.z.array(zod_1.z.string().min(1).max(8192)).max(20) }).strict(), annotations: { readOnlyHint: false, destructiveHint: false } }, async ({ threadId, ...entrada }) => { thread(threadId); livre(threadId); return resposta((0, mcp_artifacts_1.adicionarClaimMcp)(raiz, threadId, entrada)); });
534
+ verificar: zod_1.z.array(zod_1.z.string().min(1).max(8192)).max(20),
535
+ dependenciasExternas: zod_1.z.array(zod_1.z.enum(['rede', 'relogio'])).max(2).optional() }).strict(), annotations: { readOnlyHint: false, destructiveHint: false } }, async ({ threadId, ...entrada }) => { thread(threadId); livre(threadId); return resposta((0, mcp_artifacts_1.adicionarClaimMcp)(raiz, threadId, entrada)); });
536
+ registrarTool('ork_claim_withdraw', { description: 'Retira claim com motivo tipado. Exige substituta ativa e verde no ultimo verify; sem substituta, preserva a claim e abre pedido ao dono.',
537
+ inputSchema: daThread.extend({ claimId: zod_1.z.string().regex(/^C\d+$/), motivoTipo: zod_1.z.enum(['substituicao', 'obsoleta', 'erro']),
538
+ substituidaPor: zod_1.z.string().regex(/^C\d+$/).optional(), motivo: zod_1.z.string().min(1).max(8192) }).strict(),
539
+ annotations: { readOnlyHint: false, destructiveHint: true } }, async ({ threadId, ...entrada }) => {
540
+ thread(threadId);
541
+ livre(threadId);
542
+ const autor = { tipo: 'sessao', id: opcoes.dispatchId ?? `mcp:${opcoes.host}` };
543
+ return resposta((0, mcp_artifacts_1.retirarClaimMcp)(raiz, threadId, { ...entrada, autor }));
544
+ });
535
545
  registrarTool('ork_thread_status', { description: 'Estado canonico de uma thread deste projeto; pausas previstas nao sao pedidos abertos.',
536
546
  inputSchema: daThread, annotations: { readOnlyHint: true } }, async ({ threadId }) => {
537
547
  const t = thread(threadId);
@@ -326,7 +326,8 @@ function executarVerifyMcpWorker(raiz, threadId, rodadaId) {
326
326
  }));
327
327
  const locale = (0, locale_1.resolverLocale)(carregado.manifesto.owner?.language).locale;
328
328
  const veredito = { ok: resultado.ok, commit: resultado.commit, motivos: resultado.motivos,
329
- check: resultado.check, texto: (0, locale_1.comLocale)(locale, () => (0, verify_1.textoDoVerify)(resultado)), resultado };
329
+ check: resultado.check, texto: (0, locale_1.comLocale)(locale, () => (0, verify_1.textoDoVerify)(resultado)),
330
+ resultado: (0, verify_1.resultadoVerifyRedigido)(resultado) };
330
331
  terminal = { estado: 'concluido', lane: null, veredito, falha: undefined, ocupantes: undefined };
331
332
  }
332
333
  catch (e) {
@@ -82,9 +82,9 @@ const policies_1 = require("./policies");
82
82
  const session_watcher_claude_1 = require("./session-watcher-claude");
83
83
  const ship_publicar_1 = require("./ship-publicar");
84
84
  const thread_1 = require("./thread");
85
- const types_1 = require("./types");
86
85
  const verify_1 = require("./verify");
87
86
  const worktree_1 = require("./worktree");
87
+ const recibo_prova_1 = require("./recibo-prova");
88
88
  exports.CONTRATO_DAS_PROVAS = 'ork.noctum-provas/v1';
89
89
  /** D2, D3, D4, D6 a D11: as recusas da entrega, num tipo fechado próprio (fora de `MotivoGate` e das recusas do envelope). */
90
90
  exports.MOTIVOS_DA_ENTREGA = [
@@ -201,7 +201,12 @@ function lacunaDaCadeia(f) {
201
201
  const c = f.cadeia.find(x => x.tipo === 'sem-leitura');
202
202
  return c ? `o commit ${curto(c.sha)} da cadeia não se classificou: ${c.detalhe}` : null;
203
203
  }
204
- /** Os `verify_run` que valem para a fonte (D3: no SHA, ou num ancestral seguido só de commits de bundle), na ordem do ledger. */
204
+ /**
205
+ * Os `verify_run` do SHA da fonte, na ordem do ledger.
206
+ *
207
+ * A chave decide se um resultado verde ainda pode ser reaproveitado; ela nao tira uma
208
+ * rodada da historia. Assim, uma chave antiga nunca esconde reprovacao nem reduz o teto.
209
+ */
205
210
  function verifiesDaFonte(f) {
206
211
  return f.verifies.filter(v => equivalente(f.cadeia, v.commit, NEUTROS_DO_VERIFY).ok);
207
212
  }
@@ -234,6 +239,12 @@ function provaDoVerify(f) {
234
239
  if (semExecucao.length) {
235
240
  return repetir('noctum.entrega.verify-ausente', `claim(s) ativa(s) que o verify ${ultimo.eventId} não executou: ${semExecucao.slice(0, 10).join(', ')}`, { semExecucao });
236
241
  }
242
+ if (!ultimo.chaveAtual || !(0, recibo_prova_1.mesmaChaveDoRecibo)(ultimo.recibo, ultimo.chaveAtual)) {
243
+ return repetir('noctum.entrega.verify-ausente', `a chave do recibo verde ${ultimo.eventId} não confere com HEAD, claims ativas e plano atuais`);
244
+ }
245
+ if (ultimo.reavaliadoSemMudanca && ultimo.chaveAtual.dependenciasExternas.length > 0) {
246
+ return repetir('noctum.entrega.verify-ausente', `o verify ${ultimo.eventId} tem dependência externa e precisa rodar depois da reavaliação sem mudança`);
247
+ }
237
248
  return fim(prova('verify', null, `verify verde em ${curto(ultimo.commit)} (${ultimo.eventId})` +
238
249
  (falhas.length ? `, com ${falhas.length} reprovação(ões) anterior(es) no mesmo SHA registrada(s)` : ''), evidencia));
239
250
  }
@@ -281,9 +292,6 @@ function provaDaRevisao(f) {
281
292
  if (f.autores.includes(r.sessionId)) {
282
293
  return prova('revisao', 'noctum.entrega.revisor-autor', `a sessão ${r.sessionId.slice(0, 8)} também é autora do candidato: não é revisão independente`, ev);
283
294
  }
284
- if (f.ultimoDespachoDeAutor && !(Date.parse(r.despachadaEm) > Date.parse(f.ultimoDespachoDeAutor))) {
285
- return prova('revisao', 'noctum.entrega.revisao-antiga', 'o CHECK foi despachado antes do último despacho de quem escreveu o candidato', ev);
286
- }
287
295
  if (r.resultado === 'em-curso')
288
296
  return prova('revisao', 'noctum.entrega.revisao-em-curso', `a sessão de CHECK ${r.sessionId.slice(0, 8)} ainda não tem resultado`, ev);
289
297
  if (r.prova.veredito && r.prova.veredito !== 'PASSOU') {
@@ -298,6 +306,9 @@ function provaDaRevisao(f) {
298
306
  return prova('revisao', 'noctum.entrega.revisao-antiga', 'o parecer do CHECK mudou depois do resultado que o observador gravou', ev);
299
307
  if (!r.revisado)
300
308
  return prova('revisao', 'noctum.entrega.revisao-sem-sha', 'o CHECK não rodou verify no intervalo: o SHA revisado é desconhecido', ev);
309
+ if (!r.chaveAtual || !(0, recibo_prova_1.mesmaChaveDoRecibo)(r.recibo, r.chaveAtual)) {
310
+ return prova('revisao', 'noctum.entrega.revisao-antiga', 'a chave do recibo do CHECK não confere com HEAD, claims ativas e plano atuais', ev);
311
+ }
301
312
  const lacuna = lacunaDaCadeia(f);
302
313
  if (lacuna)
303
314
  return prova('revisao', LACUNA, lacuna, ev);
@@ -650,28 +661,17 @@ function segredosDaHistoria(raiz, base, fonte) {
650
661
  }
651
662
  return { lido: true, commits: blocos.length, polvos, achados };
652
663
  }
653
- /** Os autores do candidato: as sessões de GO, as que começaram antes do GO num bloco com GO, e as que commitaram. */
654
- function autoresDaThread(t, eventos) {
664
+ /** RM-078: autores do candidato são somente sessões com commit confirmado na cadeia da fonte. */
665
+ function autoresDaThread(raiz, eventos, fonte) {
655
666
  const autores = new Set();
656
- let ultimo = null;
657
- const marcar = (id, em) => {
658
- autores.add(id);
659
- if (em && (!ultimo || Date.parse(em) > Date.parse(ultimo)))
660
- ultimo = em;
661
- };
662
- for (const s of t.sessoes ?? []) {
663
- const fases = (s.bloco ?? '').split('-');
664
- const antesDoGo = fases.includes('GO') && types_1.FASES.indexOf(s.fase) <= types_1.FASES.indexOf('GO');
665
- if (s.fase === 'GO' || antesDoGo)
666
- marcar(s.sessionId, s.despachadaEm);
667
- }
668
667
  for (const e of eventos) {
669
- if ((e.tipo === 'mcp_git_committed' || e.tipo === 'commit') && typeof e.sessionId === 'string' && e.sessionId) {
670
- const s = (t.sessoes ?? []).find(x => x.sessionId === e.sessionId);
671
- marcar(e.sessionId, s?.despachadaEm);
668
+ const commit = typeof e.commit === 'string' && SHA.test(e.commit) ? e.commit : null;
669
+ const naCadeia = !!commit && !!fonte && git(raiz, ['merge-base', '--is-ancestor', commit, fonte]).ok;
670
+ if ((e.tipo === 'mcp_git_committed' || e.tipo === 'commit') && naCadeia && typeof e.sessionId === 'string' && e.sessionId) {
671
+ autores.add(e.sessionId);
672
672
  }
673
673
  }
674
- return { autores: [...autores], ultimoDespacho: ultimo };
674
+ return [...autores];
675
675
  }
676
676
  /** O vínculo da sessão de CHECK provado pelo núcleo, como o Maestro confere (`maestro-authority.ts`), ou o que faltou. */
677
677
  function vinculoDaSessao(raiz, t, s, eventos) {
@@ -719,16 +719,20 @@ function revisoesDaThread(raiz, t, eventos) {
719
719
  const gravada = resultado?.provaOrk;
720
720
  let prova;
721
721
  let revisado;
722
+ let eventoDoVerify;
723
+ const ate = resultado ? posicao.get(resultado) ?? eventos.length : eventos.length;
722
724
  if (gravada && typeof gravada.ok === 'boolean') {
723
725
  prova = { ok: gravada.ok, fonte: texto(gravada.fonte) ?? 'a prova do núcleo gravada no resultado do CHECK', veredito: texto(gravada.veredito) };
724
726
  revisado = texto(gravada.verify?.commit);
727
+ eventoDoVerify = [...intervalo].reverse().find(e => e.tipo === ledger_1.TIPOS_DE_EVENTO.verificacao &&
728
+ (posicao.get(e) ?? eventos.length) < ate && e.commit === revisado);
725
729
  }
726
730
  else {
727
731
  const p = (0, session_watcher_claude_1.provaDoOrk)(raiz, t.id, sessao, eventos, (0, session_watcher_claude_1.fonteRegistrada)(eventos, sessao));
728
732
  prova = { ok: p.ok, fonte: p.fonte, veredito: p.veredito ?? null };
729
733
  // Pela posição no ledger, e não pela hora: dois eventos no mesmo milissegundo não trocam de lado.
730
- const ate = resultado ? posicao.get(resultado) ?? eventos.length : eventos.length;
731
- revisado = texto(intervalo.filter(e => e.tipo === ledger_1.TIPOS_DE_EVENTO.verificacao && (posicao.get(e) ?? eventos.length) < ate).at(-1)?.commit);
734
+ eventoDoVerify = intervalo.filter(e => e.tipo === ledger_1.TIPOS_DE_EVENTO.verificacao && (posicao.get(e) ?? eventos.length) < ate).at(-1);
735
+ revisado = texto(eventoDoVerify?.commit);
732
736
  }
733
737
  // O parecer do resultado: o sha que o observador viu; sem ele (o observador do codex não grava), a data do arquivo, que
734
738
  // não pode ser depois do resultado (A5 da rodada 1). O `ts` do ledger é de milissegundo; a data do arquivo, mais fina.
@@ -736,11 +740,30 @@ function revisoesDaThread(raiz, t, eventos) {
736
740
  const parecer = (0, session_watcher_claude_1.estadoDoArtefato)(raiz, t.id, 'docs/check.md');
737
741
  const parecerMudou = viu ? parecer.sha256 !== viu
738
742
  : !!resultado && parecer.mtimeMs !== null && Math.floor(parecer.mtimeMs) > Date.parse(resultado.ts);
743
+ const shaRevisado = revisado && SHA.test(revisado) ? revisado : null;
739
744
  return { sessionId: s.sessionId, runtime: s.runtime, despachadaEm: s.despachadaEm, semVinculo: vinculoDaSessao(raiz, t, s, eventos),
740
- resultado: estado, prova, revisado: revisado && SHA.test(revisado) ? revisado : null, parecerMudou };
745
+ resultado: estado, prova, revisado: shaRevisado, recibo: reciboDoEvento(eventoDoVerify?.recibo),
746
+ chaveAtual: shaRevisado ? chaveAtualSegura(raiz, t.id, shaRevisado) : null, parecerMudou };
741
747
  });
742
748
  }
743
- function verifiesDoLedger(eventos) {
749
+ function reciboDoEvento(valor) {
750
+ if (!valor || typeof valor !== 'object')
751
+ return null;
752
+ const r = valor;
753
+ return r.contrato === recibo_prova_1.CONTRATO_DO_RECIBO_DE_PROVA && typeof r.head === 'string' && typeof r.claimsSha256 === 'string' &&
754
+ (typeof r.planoSha256 === 'string' || r.planoSha256 === null) && Array.isArray(r.dependenciasExternas) && typeof r.sha256 === 'string'
755
+ ? r : null;
756
+ }
757
+ function chaveAtualSegura(raiz, threadId, head) {
758
+ try {
759
+ return (0, recibo_prova_1.chaveDoReciboDeProva)(raiz, threadId, head);
760
+ }
761
+ catch {
762
+ return null;
763
+ }
764
+ }
765
+ function verifiesDoLedger(raiz, threadId, eventos) {
766
+ const posicao = new Map(eventos.map((e, i) => [e, i]));
744
767
  return eventos.filter(e => e.tipo === ledger_1.TIPOS_DE_EVENTO.verificacao && typeof e.commit === 'string' && SHA.test(e.commit)).map(e => ({
745
768
  eventId: texto(e.eventId) ?? '-', ts: e.ts, commit: e.commit, ok: e.veredito === 'verdade sustentada',
746
769
  motivos: Array.isArray(e.motivos) ? e.motivos.map(String) : [],
@@ -748,6 +771,9 @@ function verifiesDoLedger(eventos) {
748
771
  comComandos: Array.isArray(e.comandos) && e.comandos.length > 0,
749
772
  claims: Array.isArray(e.claims) ? e.claims.filter(c => typeof c?.id === 'string')
750
773
  .map(c => ({ id: c.id, verificado: c.verificado === true, motivo: texto(c.motivo) })) : [],
774
+ recibo: reciboDoEvento(e.recibo), chaveAtual: chaveAtualSegura(raiz, threadId, e.commit),
775
+ reavaliadoSemMudanca: eventos.slice((posicao.get(e) ?? eventos.length) + 1)
776
+ .some(x => x.tipo === 'phase_result' && x.resultado === 'go.sem-mudanca'),
751
777
  }));
752
778
  }
753
779
  /** Os fatos da entrega de uma thread, lidos agora do disco, do git e da forja. Não grava nada. */
@@ -766,7 +792,7 @@ function lerFatosDaEntrega(carregado, threadId, opcoes = {}) {
766
792
  base.erro = `o commit ${curto(base.sha)} da base não chegou a esta cópia`;
767
793
  base.sha = null;
768
794
  }
769
- const { autores, ultimoDespacho } = autoresDaThread(t, eventos);
795
+ const autores = autoresDaThread(raiz, eventos, fonte);
770
796
  const claimsAtivas = (() => { try {
771
797
  return (0, claims_1.lerClaims)(raiz, threadId).filter(c => c.estado !== 'retirada');
772
798
  }
@@ -775,8 +801,8 @@ function lerFatosDaEntrega(carregado, threadId, opcoes = {}) {
775
801
  } })();
776
802
  const daBranch = (0, ship_publicar_1.manifestoDaBranch)(carregado, fonte);
777
803
  const fatos = {
778
- thread: t.id, branch, fonte, base, integrada: null, cadeia: [], cadeiaLida: false, autores, ultimoDespachoDeAutor: ultimoDespacho,
779
- revisoes: revisoesDaThread(raiz, t, eventos), verifies: verifiesDoLedger(eventos),
804
+ thread: t.id, branch, fonte, base, integrada: null, cadeia: [], cadeiaLida: false, autores,
805
+ revisoes: revisoesDaThread(raiz, t, eventos), verifies: verifiesDoLedger(raiz, t.id, eventos),
780
806
  comandosEsperados: (0, verify_1.comandosDoManifesto)(daBranch.manifesto).length,
781
807
  // A2 da rodada 1: a claim de mutante sem comando não executa nada e nunca prova a guarda; ela não conta como mutante.
782
808
  claims: claimsAtivas.map(c => ({ id: c.id, mutante: exports.ALEGACAO_DE_MUTANTE.test(c.alegacao) && c.verificar.length > 0 })),
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.CONTRATO_DO_RECIBO_DE_PROVA = void 0;
37
+ exports.materialDasClaims = materialDasClaims;
38
+ exports.chaveDoReciboDeProva = chaveDoReciboDeProva;
39
+ exports.mesmaChaveDoRecibo = mesmaChaveDoRecibo;
40
+ /**
41
+ * RM-078: identidade do que um verify e um CHECK provaram.
42
+ *
43
+ * O recibo nao depende do horario do despacho. Ele depende do HEAD exato da prova,
44
+ * dos ids e comandos das claims ativas e dos bytes do plano. A equivalencia entre
45
+ * esse HEAD e a fonte continua sendo responsabilidade da cadeia classificada em
46
+ * `noctum-provas.ts`; este modulo nunca chama commits de produto de equivalentes.
47
+ */
48
+ const node_crypto_1 = require("node:crypto");
49
+ const fs = __importStar(require("node:fs"));
50
+ const path = __importStar(require("node:path"));
51
+ const thread_1 = require("./thread");
52
+ const util_1 = require("./util");
53
+ exports.CONTRATO_DO_RECIBO_DE_PROVA = 'ork.recibo-prova/v1';
54
+ const sha256 = (valor) => (0, node_crypto_1.createHash)('sha256').update(valor).digest('hex');
55
+ /** Forma canonica das claims: id ordenado e ordem declarada dos comandos preservada. */
56
+ function materialDasClaims(claims) {
57
+ const ativas = claims.filter((c) => c.estado !== 'retirada').sort((a, b) => a.id.localeCompare(b.id, 'en', { numeric: true }));
58
+ const dependenciasExternas = [...new Set(ativas.flatMap((c) => c.dependenciasExternas ?? []))].sort();
59
+ return {
60
+ claims: ativas.map((c) => ({ id: c.id, verificar: [...c.verificar] })),
61
+ dependenciasExternas,
62
+ };
63
+ }
64
+ /** A chave atual. Plano ausente fica explicito e nunca compara como recibo valido. */
65
+ function chaveDoReciboDeProva(raiz, threadId, head) {
66
+ const dir = (0, thread_1.dirThread)(raiz, threadId);
67
+ const claims = (0, util_1.lerJsonl)(path.join(dir, 'claims.jsonl'));
68
+ const material = materialDasClaims(claims);
69
+ const plano = path.join(dir, 'docs', 'plan.md');
70
+ const planoSha256 = fs.existsSync(plano) ? sha256(fs.readFileSync(plano)) : null;
71
+ const base = {
72
+ contrato: exports.CONTRATO_DO_RECIBO_DE_PROVA,
73
+ head,
74
+ claimsSha256: sha256(JSON.stringify(material.claims)),
75
+ planoSha256,
76
+ dependenciasExternas: material.dependenciasExternas,
77
+ };
78
+ return { ...base, sha256: sha256(JSON.stringify(base)) };
79
+ }
80
+ /** Recibo completo e igual; forma legada, adulterada ou sem plano volta a executar. */
81
+ function mesmaChaveDoRecibo(recibo, atual) {
82
+ return recibo?.contrato === exports.CONTRATO_DO_RECIBO_DE_PROVA && recibo.planoSha256 !== null &&
83
+ recibo.head === atual.head && recibo.claimsSha256 === atual.claimsSha256 &&
84
+ recibo.planoSha256 === atual.planoSha256 &&
85
+ JSON.stringify(recibo.dependenciasExternas) === JSON.stringify(atual.dependenciasExternas) &&
86
+ recibo.sha256 === atual.sha256;
87
+ }
package/dist/retry.js CHANGED
@@ -160,6 +160,12 @@ exports.POLITICA_DE_RETRY = {
160
160
  automatica: true,
161
161
  ...TEXTOS_DA_POLITICA['claims.unverifiable'],
162
162
  },
163
+ 'claims.retirada-sem-substituta': {
164
+ motivo: 'claims.retirada-sem-substituta',
165
+ acao: 'escalar-humano',
166
+ automatica: false,
167
+ ...TEXTOS_DA_POLITICA['claims.retirada-sem-substituta'],
168
+ },
163
169
  'policy.violation': {
164
170
  motivo: 'policy.violation',
165
171
  acao: 'escalar-humano',
@@ -38,6 +38,7 @@ exports.defaultProbe = defaultProbe;
38
38
  exports.chooseExecutor = chooseExecutor;
39
39
  exports.nativeVersionOf = nativeVersionOf;
40
40
  exports.defaultsDir = defaultsDir;
41
+ exports.opcoesDoPrazo = opcoesDoPrazo;
41
42
  exports.argumentosDeCpuDoDocker = argumentosDeCpuDoDocker;
42
43
  exports.expand = expand;
43
44
  exports.ensureImage = ensureImage;
@@ -109,6 +110,10 @@ function defaultsDir() {
109
110
  exports.CONFIG_FILES = { gitleaks: 'gitleaks.toml', eslint: 'eslint.config.mjs', ruff: 'ruff.toml', 'golangci-lint': '.golangci.yml' };
110
111
  const CONTAINER = { src: '/src', out: '/out', config: '/config' };
111
112
  const MAX_BUFFER = 512 * 1024 * 1024;
113
+ /** `spawnSync` rejects Infinity; an unbounded fixture omits the inner timeout and remains bounded by the outer round. */
114
+ function opcoesDoPrazo(timeoutMs) {
115
+ return Number.isFinite(timeoutMs) ? { timeout: timeoutMs } : {};
116
+ }
112
117
  /** A cota so existe no grupo Agile; sem a variavel, o Classic continua com o argv de sempre. */
113
118
  function argumentosDeCpuDoDocker(ambiente = process.env) {
114
119
  const limite = Number(ambiente[agendador_cpu_1.ENV_CPUS_DOCKER]);
@@ -131,7 +136,7 @@ function ensureImage(image, timeoutMs) {
131
136
  const present = (0, node_child_process_1.spawnSync)('docker', ['image', 'inspect', image], { stdio: 'ignore', timeout: Math.min(PROBE_TIMEOUT_MS, timeoutMs) });
132
137
  if (!present.error && present.status === 0)
133
138
  return true;
134
- const pull = (0, node_child_process_1.spawnSync)('docker', ['pull', '--quiet', image], { stdio: 'ignore', timeout: timeoutMs, killSignal: 'SIGKILL' });
139
+ const pull = (0, node_child_process_1.spawnSync)('docker', ['pull', '--quiet', image], { stdio: 'ignore', ...opcoesDoPrazo(timeoutMs), killSignal: 'SIGKILL' });
135
140
  return !pull.error && pull.status === 0;
136
141
  }
137
142
  /**
@@ -162,7 +167,7 @@ function runDocker(inv) {
162
167
  ...inv.argv.slice(1).map(a => expand(a, values))];
163
168
  (0, agendador_cpu_1.registrarContainerDaLane)(name);
164
169
  inv.guard?.add('containers', name);
165
- const r = (0, node_child_process_1.spawnSync)('docker', args, { encoding: 'utf8', timeout: inv.timeoutMs, killSignal: 'SIGKILL', maxBuffer: MAX_BUFFER, env: process.env });
170
+ const r = (0, node_child_process_1.spawnSync)('docker', args, { encoding: 'utf8', ...opcoesDoPrazo(inv.timeoutMs), killSignal: 'SIGKILL', maxBuffer: MAX_BUFFER, env: process.env });
166
171
  const timedOut = r.error?.code === 'ETIMEDOUT';
167
172
  if (timedOut || r.error)
168
173
  (0, node_child_process_1.spawnSync)('docker', ['rm', '-f', name], { stdio: 'ignore', timeout: PROBE_TIMEOUT_MS });
@@ -186,7 +191,8 @@ function runNative(inv) {
186
191
  const pgidFile = inv.guard?.pgidFile() ?? path.join(inv.outHost, '.native.pgid');
187
192
  const marker = `${pgidFile}${guard_1.LAUNCH_TIMEOUT_SUFFIX}`;
188
193
  try {
189
- const r = (0, node_child_process_1.spawnSync)(process.execPath, (0, guard_1.launcherArgs)(pgidFile, inv.timeoutMs, bin, rest), { cwd: inv.srcHost, encoding: 'utf8', timeout: inv.timeoutMs + 10000, killSignal: 'SIGKILL', maxBuffer: MAX_BUFFER, env });
194
+ const prazoDoLauncher = Number.isFinite(inv.timeoutMs) ? inv.timeoutMs + 10000 : inv.timeoutMs;
195
+ const r = (0, node_child_process_1.spawnSync)(process.execPath, (0, guard_1.launcherArgs)(pgidFile, inv.timeoutMs, bin, rest), { cwd: inv.srcHost, encoding: 'utf8', ...opcoesDoPrazo(prazoDoLauncher), killSignal: 'SIGKILL', maxBuffer: MAX_BUFFER, env });
190
196
  const timedOut = fs.existsSync(marker) || r.error?.code === 'ETIMEDOUT';
191
197
  return { status: r.status, timedOut, stdout: r.stdout ?? '', stderr: r.stderr ?? '' };
192
198
  }