@orkastery/cli 0.5.1 → 0.5.3

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 (110) hide show
  1. package/adapters/claude-code/agents/ork-master.md +2 -0
  2. package/adapters/claude-code/commands/master.md +8 -0
  3. package/adapters/claude-code/commands/ork.md +9 -0
  4. package/adapters/codex/skills/ork/SKILL.md +18 -3
  5. package/adapters/hermes/README.md +1 -0
  6. package/adapters/hermes/bin/ork-network-status.sh +10 -0
  7. package/adapters/hermes/hermes.plugin.json +1 -0
  8. package/adapters/hermes/hitl-ingress/__init__.py +2 -2
  9. package/adapters/hermes/skills/orkastery-devmaster/SKILL.md +6 -5
  10. package/adapters/openclaw/README.md +24 -4
  11. package/adapters/openclaw/dist/hitl-ingress.js +2 -2
  12. package/adapters/openclaw/dist/index.js +46 -11
  13. package/adapters/openclaw/openclaw.plugin.json +5 -1
  14. package/adapters/openclaw/package.json +2 -2
  15. package/adapters/openclaw/src/hitl-ingress.ts +2 -2
  16. package/adapters/openclaw/src/index.ts +49 -11
  17. package/assets/orkmind_bridge.py +117 -9
  18. package/dist/adapters/claude-bg.js +39 -5
  19. package/dist/adapters/codex.js +4 -3
  20. package/dist/agents-md.js +1 -0
  21. package/dist/auditrun.js +5 -1
  22. package/dist/board.js +16 -14
  23. package/dist/branch-de-estado.js +66 -10
  24. package/dist/busca-semantica.js +52 -12
  25. package/dist/canarios-rm057.js +149 -0
  26. package/dist/canarios.js +2 -0
  27. package/dist/ci.js +8 -1
  28. package/dist/claims.js +39 -1
  29. package/dist/company-brain-context.js +158 -22
  30. package/dist/docs.js +48 -9
  31. package/dist/doctor-pulse-cron.js +194 -0
  32. package/dist/doctor-rede.js +149 -0
  33. package/dist/doctor.js +89 -9
  34. package/dist/entrega-pr.js +20 -8
  35. package/dist/esgotamentos.js +181 -0
  36. package/dist/experiencia.js +12 -2
  37. package/dist/fabrica-estado.js +14 -6
  38. package/dist/fabrica-publicar.js +6 -0
  39. package/dist/fechamento.js +20 -66
  40. package/dist/forja.js +31 -7
  41. package/dist/gates.js +5 -1
  42. package/dist/hitl-contract.js +59 -4
  43. package/dist/hitl-curto.js +11 -6
  44. package/dist/hitl-gates.js +5 -0
  45. package/dist/hitl-lote.js +5 -2
  46. package/dist/hitl-resumo.js +9 -0
  47. package/dist/hitl-tempo-parado.js +201 -0
  48. package/dist/hitl-texto-livre.js +5 -5
  49. package/dist/horario.js +27 -0
  50. package/dist/hosts.js +52 -10
  51. package/dist/impedimento.js +205 -0
  52. package/dist/index.js +391 -74
  53. package/dist/indice-vetorial.js +87 -10
  54. package/dist/indice.js +3 -1
  55. package/dist/init.js +17 -0
  56. package/dist/integracoes-locais.js +2 -0
  57. package/dist/intelligence-graph-cli.js +91 -8
  58. package/dist/intelligence-graph-contexto.js +215 -0
  59. package/dist/intelligence-graph-parsers.js +6 -4
  60. package/dist/leases.js +664 -87
  61. package/dist/ledger-stats.js +15 -1
  62. package/dist/ledger.js +3 -0
  63. package/dist/licoes.js +1 -0
  64. package/dist/maestro-runtime.js +4 -1
  65. package/dist/manifest.js +20 -1
  66. package/dist/master-nota.js +14 -5
  67. package/dist/master.js +129 -12
  68. package/dist/mcp-artifacts.js +34 -3
  69. package/dist/mcp-git.js +20 -3
  70. package/dist/mcp-grafo-worker.js +5 -3
  71. package/dist/mcp-grafo.js +50 -4
  72. package/dist/mcp-server.js +14 -1
  73. package/dist/memoria.js +48 -6
  74. package/dist/network-roadmap.js +191 -16
  75. package/dist/ocupacao.js +11 -3
  76. package/dist/orkmind.js +90 -5
  77. package/dist/orquestracao.js +12 -10
  78. package/dist/parado-no-condutor.js +188 -32
  79. package/dist/phase.js +68 -15
  80. package/dist/policies.js +52 -1
  81. package/dist/portfolio.js +4 -1
  82. package/dist/projeto-alvo.js +5 -1
  83. package/dist/prompts.js +36 -1
  84. package/dist/prova-ativacao.js +75 -6
  85. package/dist/pulse-delivery.js +34 -2
  86. package/dist/pulse.js +49 -3
  87. package/dist/ratelimit.js +20 -4
  88. package/dist/recall.js +3 -1
  89. package/dist/rede-adesao.js +399 -0
  90. package/dist/rede-forja.js +289 -0
  91. package/dist/rede-projetos.js +239 -0
  92. package/dist/rede-status.js +336 -0
  93. package/dist/rede.js +980 -0
  94. package/dist/retry.js +69 -2
  95. package/dist/roadmap-status.js +23 -3
  96. package/dist/runtime-profiles.js +22 -1
  97. package/dist/saida-segura.js +58 -0
  98. package/dist/session-watcher-claude.js +113 -23
  99. package/dist/sessoes-contas.js +187 -0
  100. package/dist/sessoes-inventario.js +6 -18
  101. package/dist/sessoes.js +83 -2
  102. package/dist/ship.js +85 -19
  103. package/dist/thread.js +192 -13
  104. package/dist/worktree.js +5 -2
  105. package/eval/fixtures/fx-pedido-colado/caso.json +20 -0
  106. package/package.json +7 -3
  107. package/references/definition-of-done.md +1 -1
  108. package/skills/README.md +1 -1
  109. package/skills/core/orkastery-bootstrap/SKILL.md +2 -2
  110. package/skills/phases/master-metrics/SKILL.md +8 -3
@@ -33,11 +33,14 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.LIMITE_DE_RECENTES = exports.LIMITE_DE_PRS = exports.VALIDADE_DO_RETRATO_MIN = exports.LIMIAR_PARADO_NO_CONDUTOR_MIN = exports.CONTRATO_PRS = void 0;
36
+ exports.CONTRATO_FORJA_SEM_LEITURA = exports.FORJA_SEM_LEITURA = exports.LIMITE_DE_RECENTES = exports.LIMITE_DE_PRS = exports.VALIDADE_DO_RETRATO_MIN = exports.LIMIAR_PARADO_NO_CONDUTOR_MIN = exports.CONTRATO_PRS = void 0;
37
37
  exports.nomeDeCheck = nomeDeCheck;
38
38
  exports.repositorioDoRemoto = repositorioDoRemoto;
39
39
  exports.lerPrsDaForja = lerPrsDaForja;
40
40
  exports.gravarRetratoDePrs = gravarRetratoDePrs;
41
+ exports.avisarForjaSemLeitura = avisarForjaSemLeitura;
42
+ exports.esquecerForjaSemLeitura = esquecerForjaSemLeitura;
43
+ exports.forjaSemLeituraDoRemoto = forjaSemLeituraDoRemoto;
41
44
  exports.lerRetratoDePrs = lerRetratoDePrs;
42
45
  exports.perguntaAberta = perguntaAberta;
43
46
  exports.fimDoTurno = fimDoTurno;
@@ -72,6 +75,7 @@ exports.entregasDoProjeto = entregasDoProjeto;
72
75
  const fs = __importStar(require("node:fs"));
73
76
  const path = __importStar(require("node:path"));
74
77
  const node_child_process_1 = require("node:child_process");
78
+ const branch_de_estado_1 = require("./branch-de-estado");
75
79
  const conducao_1 = require("./conducao");
76
80
  const estado_thread_1 = require("./estado-thread");
77
81
  const forja_1 = require("./forja");
@@ -111,6 +115,8 @@ const MINIMO_DA_CONFERENCIA_MS = 5000;
111
115
  const FASES_ANTES_DA_ENTREGA = ['GOAL', 'PLAN', 'GO', 'CHECK'];
112
116
  /** Os modos em que a #TAG ja autoriza o push (`autorizacaoDePush`, `core/src/ship.ts`). */
113
117
  const MODOS_COM_PUSH_AUTORIZADO = ['auto', 'maestro'];
118
+ /** RM-037 (fatia 5, A5): o que as linhas e o resumo dizem quando a forja nao tem leitura de PR. */
119
+ exports.FORJA_SEM_LEITURA = 'forja sem leitura de PR';
114
120
  /** O ambiente do `gh`: sem cor, sem prompt e sem host ou repositorio herdados (o host vai no `--repo`). */
115
121
  function ambienteDoGh() {
116
122
  const env = { ...process.env, GH_PROMPT_DISABLED: '1', NO_COLOR: '1', CLICOLOR: '0', CLICOLOR_FORCE: '0' };
@@ -128,6 +134,8 @@ const git = (raiz, args) => (0, util_1.exec)('git', args, raiz, 60000, { ...proc
128
134
  const SHA = /^[0-9a-f]{40}$/;
129
135
  const BRANCH = /^[A-Za-z0-9._/-]{1,200}$/;
130
136
  const REPOSITORIO = /^[A-Za-z0-9_.-]{1,100}\/[A-Za-z0-9_.-]{1,100}$/;
137
+ /** O host do GitHub Enterprise guardado no retrato: rotulos alfanumericos separados por ponto, como na forja. */
138
+ const HOST = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)+$/;
131
139
  /** Texto curto vindo de fora: sem controle, num teto. */
132
140
  function curto(v, teto = 120) {
133
141
  if (typeof v !== 'string')
@@ -215,30 +223,87 @@ function prDaForja(bruto, base) {
215
223
  return { numero: numero, branch: p.headRefName, head, estado, rascunho: p.isDraft, url,
216
224
  criadoEm: instante(p.createdAt), mescladoEm: instante(p.mergedAt), checks: [...porChave.values()].map((x) => x.check) };
217
225
  }
226
+ /**
227
+ * RM-037 (fatia 5, A5): o host e o `dono/nome` do remoto quando ele pode ser um GitHub (o github.com ou um host
228
+ * proprio), so pelo git local, sem rede nem `gh`. O GitLab fica de fora.
229
+ */
230
+ function githubDoRemoto(raiz, remoto) {
231
+ // RM-047: o remoto vem do manifesto versionado; so nome de remoto chega ao git, e depois do `--`.
232
+ if (!(0, branch_de_estado_1.remotoValido)(remoto))
233
+ return null;
234
+ const url = git(raiz, ['remote', 'get-url', '--', remoto]);
235
+ const forja = url.ok ? (0, forja_1.repositorioGithubNoHost)(url.stdout.trim()) : null;
236
+ // `ssh.github.com` e o SSH do github.com pela porta 443, e nao um GitHub Enterprise (sugestao da rodada 1 do CHECK).
237
+ return forja && REPOSITORIO.test(forja.repo) ? { host: forja.host === 'ssh.github.com' ? 'github.com' : forja.host, repositorio: forja.repo }
238
+ : null;
239
+ }
218
240
  /** O repositorio `dono/nome` do remoto, so quando ele e do github.com (o host ancorado, nao so citado no caminho). */
219
241
  function repositorioDoRemoto(raiz, remoto) {
220
- const url = git(raiz, ['remote', 'get-url', remoto]);
221
- const forja = url.ok ? (0, forja_1.identidadeDaForja)(url.stdout.trim()) : null;
222
- return forja && forja.tipo === 'github' && forja.host === 'github.com' && REPOSITORIO.test(forja.repo) ? forja.repo : null;
242
+ const github = githubDoRemoto(raiz, remoto);
243
+ return github && github.host === 'github.com' ? github.repositorio : null;
244
+ }
245
+ /**
246
+ * RM-037 (fatia 5, A5): de onde o pulse le os PRs do remoto. O github.com, como sempre; o host proprio em que o `gh`
247
+ * esta autenticado, que e um GitHub Enterprise (`gh auth status --hostname <host>`); ou nenhum: GitLab, caminho local,
248
+ * ou host em que o `gh` nao tem login. Ficar sem leitura nao e falha desta batida (o "PR nao lido"): e o estado da
249
+ * forja, que o pulse diz uma vez.
250
+ */
251
+ function forjaDosPrs(raiz, remoto, executor, prazoMs) {
252
+ // RM-047: remoto que nao e nome de remoto nao chega ao git; para o pulse, e forja sem leitura.
253
+ if (!(0, branch_de_estado_1.remotoValido)(remoto)) {
254
+ return { leitura: false, host: null, motivo: `o remoto ${(0, branch_de_estado_1.remotoRedigido)(remoto)} não é nome de remoto do git`, semLeitura: true };
255
+ }
256
+ const url = git(raiz, ['remote', 'get-url', '--', remoto]);
257
+ if (!url.ok)
258
+ return { leitura: false, host: null, motivo: `o remoto ${remoto} não está configurado neste checkout`, semLeitura: true };
259
+ const endereco = (0, forja_1.enderecoDoRemoto)(url.stdout.trim());
260
+ // Caminho local, ou apelido de SSH sem dominio (`git@github-trabalho:dono/repo.git`): nao ha host para ler.
261
+ if (!endereco) {
262
+ return { leitura: false, host: null, motivo: `o remoto ${remoto} não tem host de forja (caminho local ou apelido de SSH)`, semLeitura: true };
263
+ }
264
+ if ((0, forja_1.identidadeDaForja)(url.stdout.trim())?.tipo === 'gitlab') {
265
+ return { leitura: false, host: endereco.host, motivo: `a forja de ${remoto} (${endereco.host}) é um GitLab, e o ork só lê PR do GitHub`,
266
+ semLeitura: true };
267
+ }
268
+ const github = githubDoRemoto(raiz, remoto);
269
+ if (!github) {
270
+ return { leitura: false, host: endereco.host, motivo: `o remoto ${remoto} (${endereco.host}) não aponta um repositório dono/nome`,
271
+ semLeitura: true };
272
+ }
273
+ if (github.host === 'github.com')
274
+ return { leitura: true, ...github };
275
+ const auth = executor(['auth', 'status', '--hostname', github.host], prazoMs);
276
+ if (auth.status === 0)
277
+ return { leitura: true, ...github };
278
+ // So a resposta de login ausente (o `gh` 2.46 diz "You are not logged into any accounts on <host>") e o estado da
279
+ // forja; prazo estourado, `gh` ausente ou host fora do ar sao falha desta batida.
280
+ const semLogin = auth.status === 1 && /\bnot logged in/i.test(`${auth.stdout}\n${auth.stderr}`);
281
+ return { leitura: false, host: github.host, semLeitura: semLogin, motivo: semLogin
282
+ ? `o gh não está autenticado em ${github.host}, a forja de ${remoto}`
283
+ : `gh auth status --hostname ${github.host} falhou (${auth.status === null ? 'sem código de saída' : `código ${auth.status}`}): ` +
284
+ (curto((0, hitl_1.redigirSegredos)(auth.stderr || auth.stdout || ''), 120) ?? 'sem detalhe') };
223
285
  }
224
286
  /**
225
287
  * Le os PRs da base do repositorio do remoto em duas chamadas: os abertos (ate `LIMITE_DE_PRS`; e so a
226
288
  * lista deles que pode dizer "sem PR") e os recentes de qualquer estado (para achar o mesclado e o fechado).
227
- * O `gh` usa a autenticacao dele; nenhum token passa por aqui. Remoto que nao e do github.com nao chama nada.
289
+ * O `gh` usa a autenticacao dele; nenhum token passa por aqui. RM-037 (fatia 5, A5): no GitHub Enterprise, o host
290
+ * vai no `--repo`; forja sem leitura de PR nao chama o `gh pr list`.
228
291
  */
229
292
  function lerPrsDaForja(carregado, opcoes = {}) {
230
293
  const lidoEm = opcoes.quando ?? new Date().toISOString();
231
294
  const remoto = opcoes.remoto ?? carregado.manifesto.fabrica.remoto;
232
295
  const base = carregado.manifesto.worktree.base_branch;
233
- const repositorio = repositorioDoRemoto(carregado.raiz, remoto);
234
- if (!repositorio)
235
- return { ok: false, lidoEm, erro: `o remoto ${remoto} não é um repositório do github.com` };
236
296
  const orcamento = opcoes.orcamentoMs ?? ORCAMENTO_DA_LEITURA_MS, fimDaLeitura = Date.now() + orcamento;
237
297
  const resta = () => fimDaLeitura - Date.now();
298
+ const executor = opcoes.executor ?? ghPadrao;
299
+ const forja = forjaDosPrs(carregado.raiz, remoto, executor, Math.min(PRAZO_DO_GH_MS, Math.max(1, resta())));
300
+ if (!forja.leitura)
301
+ return { ok: false, lidoEm, erro: forja.motivo, ...(forja.semLeitura ? { semLeitura: { remoto, host: forja.host } } : {}) };
302
+ const { host, repositorio } = forja;
238
303
  const pedir = (args) => {
239
304
  if (resta() <= 0)
240
305
  return `gh pr list: orçamento de ${Math.round(orcamento / 1000)} s da leitura esgotado`;
241
- const r = (opcoes.executor ?? ghPadrao)(['pr', 'list', `--repo=github.com/${repositorio}`, `--base=${base}`, ...args], Math.min(PRAZO_DO_GH_MS, resta()));
306
+ const r = executor(['pr', 'list', `--repo=${host}/${repositorio}`, `--base=${base}`, ...args], Math.min(PRAZO_DO_GH_MS, resta()));
242
307
  if (r.status !== 0) {
243
308
  const detalhe = curto((0, hitl_1.redigirSegredos)(r.stderr || r.stdout || ''), 160) ?? 'sem detalhe';
244
309
  return `gh pr list falhou (${r.status === null ? 'sem código de saída' : `código ${r.status}`}): ${detalhe}`;
@@ -296,8 +361,8 @@ function lerPrsDaForja(carregado, opcoes = {}) {
296
361
  semConferir.push(branch);
297
362
  }
298
363
  }
299
- return { ok: true, retrato: { contrato: exports.CONTRATO_PRS, lidoEm, repositorio, base, parcial: abertos.length >= exports.LIMITE_DE_PRS,
300
- ...(semConferir.length ? { semConferir } : {}), prs: [...porNumero.values()] } };
364
+ return { ok: true, retrato: { contrato: exports.CONTRATO_PRS, lidoEm, repositorio, ...(host !== 'github.com' ? { host } : {}), base,
365
+ parcial: abertos.length >= exports.LIMITE_DE_PRS, ...(semConferir.length ? { semConferir } : {}), prs: [...porNumero.values()] } };
301
366
  }
302
367
  catch (e) {
303
368
  return { ok: false, lidoEm, erro: `resposta do gh pr list fora do formato: ${curto((0, hitl_1.redigirSegredos)(e.message), 120) ?? 'sem detalhe'}` };
@@ -321,6 +386,64 @@ function gravarRetratoDePrs(raiz, retrato) {
321
386
  throw e;
322
387
  }
323
388
  }
389
+ const ARQUIVO_DA_FORJA_SEM_LEITURA = 'forja-sem-leitura.json';
390
+ exports.CONTRATO_FORJA_SEM_LEITURA = 'ork.forja-sem-leitura/v1';
391
+ function lerAvisoDeForjaSemLeitura(raiz) {
392
+ try {
393
+ const a = JSON.parse(fs.readFileSync(path.join(dirDoMonitor(raiz), ARQUIVO_DA_FORJA_SEM_LEITURA), 'utf8'));
394
+ const ditoEm = instante(a.ditoEm), motivo = curto(a.motivo, 200);
395
+ if (a.contrato !== exports.CONTRATO_FORJA_SEM_LEITURA || typeof a.remoto !== 'string' || !/^[A-Za-z0-9._-]{1,100}$/.test(a.remoto) ||
396
+ !(a.host === null || (typeof a.host === 'string' && HOST.test(a.host))) || !ditoEm || !motivo)
397
+ return null;
398
+ return { contrato: exports.CONTRATO_FORJA_SEM_LEITURA, remoto: a.remoto, host: a.host, motivo, ditoEm };
399
+ }
400
+ catch {
401
+ return null;
402
+ }
403
+ }
404
+ /**
405
+ * RM-037 (fatia 5, A5): a forja sem leitura de PR e dita uma vez por remoto e host. Com o remoto fora do github.com,
406
+ * o pulse dizia "PR nao lido" a cada batida, para um estado que nao muda de uma batida para a outra. A marca em
407
+ * `.orkastery/monitor/` guarda o que ja foi dito; devolve true quando e novidade (e grava a marca).
408
+ */
409
+ function avisarForjaSemLeitura(raiz, semLeitura, motivo, quando) {
410
+ const atual = lerAvisoDeForjaSemLeitura(raiz);
411
+ if (atual && atual.remoto === semLeitura.remoto && atual.host === semLeitura.host)
412
+ return false;
413
+ const dir = dirDoMonitor(raiz);
414
+ fs.mkdirSync(dir, { recursive: true });
415
+ const arquivo = path.join(dir, ARQUIVO_DA_FORJA_SEM_LEITURA), tmp = `${arquivo}.${process.pid}.${Date.now()}.tmp`;
416
+ const aviso = { contrato: exports.CONTRATO_FORJA_SEM_LEITURA, ...semLeitura, motivo: curto(motivo, 200) ?? exports.FORJA_SEM_LEITURA,
417
+ ditoEm: quando };
418
+ try {
419
+ fs.writeFileSync(tmp, JSON.stringify(aviso, null, 2) + '\n', { mode: 0o600, flag: 'wx' });
420
+ fs.renameSync(tmp, arquivo);
421
+ }
422
+ catch (e) {
423
+ try {
424
+ fs.unlinkSync(tmp);
425
+ }
426
+ catch { /* ja saiu */ }
427
+ throw e;
428
+ }
429
+ return true;
430
+ }
431
+ /** A leitura voltou: a marca sai, e a forja que perder a leitura de novo volta a ser dita. */
432
+ function esquecerForjaSemLeitura(raiz) {
433
+ fs.rmSync(path.join(dirDoMonitor(raiz), ARQUIVO_DA_FORJA_SEM_LEITURA), { force: true });
434
+ }
435
+ /**
436
+ * A forja sem leitura que o pulse ja disse, so quando ela e a do remoto de agora (mesmo nome e mesmo host): o status
437
+ * do roadmap a le sem rede e sem `gh`.
438
+ */
439
+ function forjaSemLeituraDoRemoto(raiz, remoto) {
440
+ const aviso = lerAvisoDeForjaSemLeitura(raiz);
441
+ if (!aviso || aviso.remoto !== remoto || !(0, branch_de_estado_1.remotoValido)(remoto))
442
+ return null;
443
+ const url = git(raiz, ['remote', 'get-url', '--', remoto]);
444
+ const host = url.ok ? (0, forja_1.enderecoDoRemoto)(url.stdout.trim())?.host ?? null : null;
445
+ return host === aviso.host ? aviso : null;
446
+ }
324
447
  /** O ultimo retrato gravado pelo pulse, conferido campo a campo e com os nomes de check limpos de novo. */
325
448
  function lerRetratoDePrs(raiz) {
326
449
  try {
@@ -328,6 +451,7 @@ function lerRetratoDePrs(raiz) {
328
451
  const lidoEm = instante(r.lidoEm);
329
452
  if (r.contrato !== exports.CONTRATO_PRS || !lidoEm || typeof r.repositorio !== 'string' || !REPOSITORIO.test(r.repositorio) ||
330
453
  typeof r.base !== 'string' || typeof r.parcial !== 'boolean' || !Array.isArray(r.prs) ||
454
+ (r.host !== undefined && !(typeof r.host === 'string' && HOST.test(r.host))) ||
331
455
  (r.semConferir !== undefined && !(Array.isArray(r.semConferir) && r.semConferir.every((b) => typeof b === 'string' && BRANCH.test(b)))))
332
456
  return null;
333
457
  const opcional = (v) => (v === null || v === undefined ? null : instante(v) ?? undefined);
@@ -351,8 +475,8 @@ function lerRetratoDePrs(raiz) {
351
475
  prs.push({ numero: p.numero, branch: p.branch, head: p.head, estado, rascunho: p.rascunho,
352
476
  url: typeof p.url === 'string' && /^https:\/\/[^\s]+$/.test(p.url) ? curto(p.url, 300) : null, criadoEm, mescladoEm, checks });
353
477
  }
354
- return { contrato: exports.CONTRATO_PRS, lidoEm, repositorio: r.repositorio, base: r.base, parcial: r.parcial,
355
- ...(Array.isArray(r.semConferir) && r.semConferir.length ? { semConferir: r.semConferir } : {}), prs };
478
+ return { contrato: exports.CONTRATO_PRS, lidoEm, repositorio: r.repositorio, ...(typeof r.host === 'string' ? { host: r.host } : {}),
479
+ base: r.base, parcial: r.parcial, ...(Array.isArray(r.semConferir) && r.semConferir.length ? { semConferir: r.semConferir } : {}), prs };
356
480
  }
357
481
  catch {
358
482
  return null;
@@ -411,13 +535,25 @@ const doRadar = (e) => e.tipo === 'sessao_bloqueada' && e.fonte === 'ork session
411
535
  * intervalo do despacho. A prova do ork no SHIP e so o `ship_done` (`provaDoOrk`): o que falta e mergear o PR
412
536
  * ou registrar a entrega, e isso e do condutor, como o fim de turno em `blocked`. Dos 4 `human.pending` do SHIP
413
537
  * nos ledgers de 30/09, 2 tem este formato (ork-rm037noite e ork-pacotedeexpe). O CHECK em `done` sem o veredito
414
- * e outra coisa, e continua com o dono (A3 da rodada 5, pendente).
538
+ * e outra coisa: `checkSemVeredito`.
415
539
  */
416
540
  function shipSemRegistro(e) {
417
541
  const { ok: provou } = (e.provaOrk ?? {});
418
542
  return e.motivo === 'human.pending' && e.origem === 'sessions.watch' && e.fase === 'SHIP' && e.estadoNativo === 'done' &&
419
543
  !!e.stop && provou === false;
420
544
  }
545
+ /**
546
+ * RM-037 (fatia 5, A3): o CHECK que o observador viu terminar em `done`, com o Stop, sem exatamente um veredito
547
+ * legivel no `docs/check.md` (`provaDoOrk`). No #Auto, com o bloco sem pausa ao fim, ninguem perguntou nada ao dono:
548
+ * o passo e do condutor, que redespacha o CHECK. Nos dois casos reais (ork-i35horariodo em 20/09 e ork-pacotedeexpe
549
+ * em 30/09, as duas #Auto) o condutor seguiu sozinho minutos depois, sem pergunta ao dono. Fora do #Auto, segue com
550
+ * o dono, como antes.
551
+ */
552
+ function checkSemVeredito(e) {
553
+ const { ok: provou } = (e.provaOrk ?? {});
554
+ return e.motivo === 'human.pending' && e.origem === 'sessions.watch' && e.fase === 'CHECK' && e.estadoNativo === 'done' &&
555
+ !!e.stop && provou === false;
556
+ }
421
557
  /**
422
558
  * O bloqueio que pede o dono: o prompt de permissao do hook e o que o radar leu com pergunta na tela. O
423
559
  * carimbo do radar no fim de turno (`blocked` lido sem menu nem pergunta) nao e prompt nenhum.
@@ -473,7 +609,8 @@ function atividadeDaSessao(e) {
473
609
  return !(e.sensor === 'notification' && e.notificationType === 'idle_prompt');
474
610
  return e.tipo === 'commit' || e.tipo === 'runtime_stop' || (e.tipo === 'sessao_bloqueada' && !doRadar(e));
475
611
  }
476
- function fimDoTurno(eventos, despacho) {
612
+ /** `modo`: o da thread. Sem ele, o CHECK sem o veredito fica com o dono, como fora do #Auto. */
613
+ function fimDoTurno(eventos, despacho, modo) {
477
614
  const i = eventos.lastIndexOf(despacho);
478
615
  const depois = eventos.slice(i + 1);
479
616
  const sid = texto(despacho.sessionId);
@@ -497,12 +634,13 @@ function fimDoTurno(eventos, despacho) {
497
634
  // S-a do CHECK (rodada 3): no bloco com pausa ao fim, todo `human.pending` do resultado e a fase entregue ao dono.
498
635
  return { em: resultado.ts, tipo: 'pausa-do-bloco', sessionId: sid, fase, comProva: true };
499
636
  }
500
- else if (resultado.motivo === 'human.pending' && resultado.stop && (resultado.estadoNativo === 'blocked' || shipSemRegistro(resultado))) {
637
+ else if (resultado.motivo === 'human.pending' && resultado.stop && (resultado.estadoNativo === 'blocked' || shipSemRegistro(resultado) ||
638
+ (modo === 'auto' && checkSemVeredito(resultado)))) {
501
639
  if (!retomou) {
502
640
  const { ts: doStop } = resultado.stop;
503
641
  const { ok: provou } = (resultado.provaOrk ?? {});
504
- return { em: instante(doStop) ?? resultado.ts, tipo: 'espera-do-observador', sessionId: sid, fase,
505
- comProva: typeof provou === 'boolean' ? provou : null };
642
+ return { em: instante(doStop) ?? resultado.ts, tipo: checkSemVeredito(resultado) ? 'check-sem-veredito' : 'espera-do-observador',
643
+ sessionId: sid, fase, comProva: typeof provou === 'boolean' ? provou : null };
506
644
  }
507
645
  }
508
646
  else
@@ -544,17 +682,17 @@ function pendenciaDoDono(t, eventos, quando) {
544
682
  return 'prompt de permissão pendente';
545
683
  // Aviso da rodada 6 do CHECK: o gate do observador so muda de dono com o fim de turno provado. A sessao que voltou a
546
684
  // trabalhar depois do resultado, sem Stop novo, segue do dono, e a thread nao sai tambem como linha do condutor.
547
- const fim = fimDoTurno(eventos, despacho);
685
+ const fim = fimDoTurno(eventos, despacho, t.modo);
548
686
  const turnoEncerrado = !!fim && fim.tipo !== 'outro';
549
687
  for (let k = 0; k < depois.length; k++) {
550
688
  const e = depois[k];
551
689
  if (e.tipo !== 'gate_blocked')
552
690
  continue;
553
691
  const motivo = String(e.motivo ?? '');
554
- // O fim de turno que o observador viu em `blocked`, e o SHIP em `done` sem o `ship_done` (B1 da rodada 5), nao sao
555
- // escalacao: sao justamente o que muda de dono.
692
+ // O fim de turno que o observador viu em `blocked`, o SHIP em `done` sem o `ship_done` (B1 da rodada 5) e, no #Auto,
693
+ // o CHECK em `done` sem o veredito (A3, fatia 5) nao sao escalacao: sao justamente o que muda de dono.
556
694
  if (turnoEncerrado && motivo === 'human.pending' && e.origem === 'sessions.watch' &&
557
- (e.estadoNativo === 'blocked' || shipSemRegistro(e)))
695
+ (e.estadoNativo === 'blocked' || shipSemRegistro(e) || (t.modo === 'auto' && checkSemVeredito(e))))
558
696
  continue;
559
697
  const doDono = motivo === 'human.pending' ||
560
698
  (hitl_gates_1.MOTIVOS_DE_ESCALACAO_HUMANA.includes(motivo) && (0, hitl_classificacao_1.quemDecide)(motivo) === 'dono');
@@ -570,7 +708,7 @@ function esperaDoCondutor(t, eventos, quando) {
570
708
  const despacho = ultimoDespacho(eventos);
571
709
  if (!despacho || despacho.pausaAoFim !== false)
572
710
  return null;
573
- const fim = fimDoTurno(eventos, despacho);
711
+ const fim = fimDoTurno(eventos, despacho, t.modo);
574
712
  if (!fim || fim.tipo === 'outro' || fim.tipo === 'pausa-do-bloco' || pendenciaDoDono(t, eventos, quando))
575
713
  return null;
576
714
  return { thread: t.id, fase: fim.fase, sessionId: fim.sessionId, fimDoTurnoEm: fim.em, tipo: fim.tipo, comProva: fim.comProva };
@@ -699,9 +837,13 @@ function entregasDoProjeto(carregado, opcoes) {
699
837
  // R4-4 do CHECK (rodada 4): o `ship_done` cobre a entrega do mesmo head (`shaDe`) ou do mesmo merge; a entrega
700
838
  // nova depois dele ainda nao foi registrada. O legado sem o sha cobre tudo, como antes.
701
839
  const cobre = (campo, sha) => ships.some((e) => typeof e[campo] !== 'string' || !SHA.test(e[campo]) || e[campo] === sha);
840
+ // RM-037 (fatia 5, A1): ate esta fatia, o `ork ship` com a branch ja incorporada gravava a ponta da base no
841
+ // `mergeSha`. Esse `ship_done` registra o merge `ship(<thread>)` que a ponta gravada contem.
842
+ const contemOMerge = (sha) => ships.some((e) => typeof e.mergeSha === 'string' && SHA.test(e.mergeSha) &&
843
+ e.mergeSha !== sha && git(raiz, ['merge-base', '--is-ancestor', sha, e.mergeSha]).code === 0);
702
844
  return { t, eventos, branch: cabeca ? branch : null, cabeca, comProduto, publicada, temRemota: !!remota,
703
845
  shipNoLedger: ships.length > 0, entregueNaPonta: cabeca ? cobre('shaDe', cabeca) : ships.length > 0,
704
- mergeRegistrado: !!merge && cobre('mergeSha', merge.sha), merge };
846
+ mergeRegistrado: !!merge && (cobre('mergeSha', merge.sha) || contemOMerge(merge.sha)), merge };
705
847
  });
706
848
  // D2: a forja so e lida quando alguma thread tem o que mostrar la: produto numa branch que ja foi ao remoto
707
849
  // (inteira, ou com commit novo ainda sem push, que pode ter PR aberto).
@@ -710,11 +852,15 @@ function entregasDoProjeto(carregado, opcoes) {
710
852
  if (opcoes.lerPrs && candidatas.length)
711
853
  prs = opcoes.lerPrs(candidatas);
712
854
  // O retrato so vale do repositorio e da base de agora, e lido ha menos de `VALIDADE_DO_RETRATO_MIN`.
713
- let retrato = null, semRetrato = prs && !prs.ok ? prs.erro : 'sem leitura dos PRs';
855
+ // RM-037 (fatia 5, A5): a forja sem leitura de PR e o estado dela, dito uma vez pelo pulse; a linha nunca diz "PR nao lido".
856
+ const semLeituraDaForja = !!prs && !prs.ok && !!prs.semLeitura;
857
+ let retrato = null, semRetrato = prs && !prs.ok ? (semLeituraDaForja ? exports.FORJA_SEM_LEITURA : prs.erro) : 'sem leitura dos PRs';
714
858
  if (prs?.ok) {
715
- const r = prs.retrato;
716
- if (r.base !== baseBranch || r.repositorio !== repositorioDoRemoto(raiz, remoto))
859
+ const r = prs.retrato, atual = githubDoRemoto(raiz, remoto);
860
+ // O retrato vale do mesmo host: o do GitHub Enterprise (`host`) ou, sem ele, o github.com.
861
+ if (r.base !== baseBranch || !atual || r.repositorio !== atual.repositorio || (r.host ?? 'github.com') !== atual.host) {
717
862
  semRetrato = 'retrato de PRs de outro repositório ou base';
863
+ }
718
864
  else if (minutosDesde(r.lidoEm, quando) > exports.VALIDADE_DO_RETRATO_MIN)
719
865
  semRetrato = 'retrato de PRs velho';
720
866
  else if (Date.parse(r.lidoEm) - Date.parse(quando) > FOLGA_DO_RELOGIO_MIN * 60000)
@@ -727,7 +873,7 @@ function entregasDoProjeto(carregado, opcoes) {
727
873
  for (const f of fatos) {
728
874
  const { t, eventos } = f;
729
875
  const despacho = ultimoDespacho(eventos);
730
- const fim = despacho ? fimDoTurno(eventos, despacho) : null;
876
+ const fim = despacho ? fimDoTurno(eventos, despacho, t.modo) : null;
731
877
  const terminou = !!fim && fim.tipo !== 'outro';
732
878
  const fimEm = terminou ? fim.em : null;
733
879
  // A1 do CHECK (rodada 3): o fim de turno vem do ledger (Stop sem atividade depois); a tela da sessao depois
@@ -771,7 +917,15 @@ function entregasDoProjeto(carregado, opcoes) {
771
917
  let caso = null, desde = null, passo = '', peloGit = false;
772
918
  const evidencia = [];
773
919
  if (!conduzidaAgora && !doDono) {
774
- if (f.comProduto && publicada === false && terminou) {
920
+ if (espera?.tipo === 'check-sem-veredito') {
921
+ // RM-037 (fatia 5, A3): antes de qualquer caso de entrega. Publicar ou mergear sem o veredito do CHECK pularia
922
+ // a revisao; e o `ork retry run` nao redespacha `human.pending` (`escalar-humano`), entao o passo e o despacho.
923
+ caso = 'check-sem-veredito';
924
+ desde = espera.fimDoTurnoEm;
925
+ passo = `redespachar o CHECK (ork phase run ${t.id} CHECK --prompt "<pedido da fase>")`;
926
+ evidencia.push('ledger: CHECK em done com o Stop, sem exatamente um veredito no docs/check.md');
927
+ }
928
+ else if (f.comProduto && publicada === false && terminou) {
775
929
  desde = fimOuVeredito;
776
930
  if (antesDaEntrega && !aberto) {
777
931
  caso = 'fase-seguinte';
@@ -782,7 +936,8 @@ function entregasDoProjeto(carregado, opcoes) {
782
936
  // N2 da seguranca (rodada 2): branch que ja foi ao remoto pode ter PR; sem a leitura, nunca "abrir o PR". A4 da
783
937
  // rodada 5: publicar e abrir o PR pedem a mesma autorizacao de push que o merge.
784
938
  passo = (aberto ? `publicar os commits novos da branch ${f.branch} no PR #${aberto.numero}`
785
- : f.temRemota && !prSabido ? `publicar os commits novos da branch ${f.branch} (PR não lido)`
939
+ // RM-037 (fatia 5, A5): a forja sem leitura de PR ja foi dita uma vez; a linha so diz o passo.
940
+ : f.temRemota && !prSabido ? `publicar os commits novos da branch ${f.branch}${semLeituraDaForja ? '' : ' (PR não lido)'}`
786
941
  : `publicar a branch ${f.branch} e abrir o PR`) + autorizacao;
787
942
  }
788
943
  evidencia.push(`refs/heads/${f.branch} tem commit fora de refs/remotes/${remoto}/${f.branch}`);
@@ -850,7 +1005,8 @@ function entregasDoProjeto(carregado, opcoes) {
850
1005
  caso = 'sessao-sem-pergunta';
851
1006
  passo = f.shipNoLedger && !f.comProduto ? `fechar o MASTER da thread (ork master ${t.id})`
852
1007
  : aberto ? `acompanhar os checks do PR #${aberto.numero}, que seguem em andamento`
853
- : precisaDePr && !usouPr ? `conferir o PR da branch ${f.branch} (PR não lido) e seguir`
1008
+ : precisaDePr && !usouPr ? (semLeituraDaForja ? `conferir na forja o PR da branch ${f.branch} e seguir`
1009
+ : `conferir o PR da branch ${f.branch} (PR não lido) e seguir`)
854
1010
  : `ler o fim da sessão ${id} (ork sessions logs ${id}) e seguir a thread`;
855
1011
  }
856
1012
  }
@@ -880,7 +1036,7 @@ function entregasDoProjeto(carregado, opcoes) {
880
1036
  : daBranch.mesclado && !f.entregueNaPonta ? (f.merge && !f.mergeRegistrado ? `PR #${daBranch.mesclado.numero} mesclado, falta registrar a entrega (${registrar})`
881
1037
  : `PR #${daBranch.mesclado.numero} mesclado sem o assunto ship(${t.id}), falta registrar a entrega`)
882
1038
  : !f.comProduto && f.merge && !f.mergeRegistrado ? `merge ${f.merge.sha.slice(0, 7)} na base, falta registrar a entrega (${registrar})`
883
- : precisaDePr ? (!usouPr ? 'branch publicada, PR não lido'
1039
+ : precisaDePr ? (!usouPr ? (semLeituraDaForja ? `branch publicada, ${exports.FORJA_SEM_LEITURA}` : 'branch publicada, PR não lido')
884
1040
  : daBranch.fechado ? `branch publicada, PR #${daBranch.fechado.numero} fechado sem merge` : 'branch publicada sem PR')
885
1041
  : null;
886
1042
  estados.push({ thread: t.id, branch: f.branch, comProduto: f.comProduto, publicada, pr: prDaEntrega,
package/dist/phase.js CHANGED
@@ -35,6 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.REGRAS_DE_EVIDENCIA = exports.CONTRATO_DAS_FASES = void 0;
37
37
  exports.escolherPerfil = escolherPerfil;
38
+ exports.perfilPedido = perfilPedido;
38
39
  exports.perfilParaDespacho = perfilParaDespacho;
39
40
  exports.runtimeCruzadoParaDespacho = runtimeCruzadoParaDespacho;
40
41
  exports.estadoParaDespacho = estadoParaDespacho;
@@ -71,12 +72,15 @@ const board_1 = require("./board");
71
72
  const verify_1 = require("./verify");
72
73
  const session_watcher_1 = require("./session-watcher");
73
74
  const session_watcher_claude_1 = require("./session-watcher-claude");
75
+ const manifest_1 = require("./manifest");
76
+ const mcp_grafo_1 = require("./mcp-grafo");
74
77
  const runtimes_1 = require("./runtimes");
75
78
  const setup_1 = require("./setup");
76
79
  const modos_1 = require("./modos");
77
80
  const gates_1 = require("./gates");
78
81
  const policies_1 = require("./policies");
79
82
  const ratelimit_1 = require("./ratelimit");
83
+ const impedimento_1 = require("./impedimento");
80
84
  const claude_bg_1 = require("./adapters/claude-bg");
81
85
  const codex_1 = require("./adapters/codex");
82
86
  const memoria_1 = require("./memoria");
@@ -89,6 +93,7 @@ const ledger_1 = require("./ledger");
89
93
  const estado_thread_1 = require("./estado-thread");
90
94
  const horario_1 = require("./horario");
91
95
  const runtime_profiles_1 = require("./runtime-profiles");
96
+ const sessoes_contas_1 = require("./sessoes-contas");
92
97
  const maquina_1 = require("./maquina");
93
98
  const fabrica_publicar_1 = require("./fabrica-publicar");
94
99
  const conducao_1 = require("./conducao");
@@ -102,18 +107,14 @@ function escolherPerfil(raiz, runtime, opcoes = {}) {
102
107
  // I-49: o despacho enxerga o estado que as outras fabricas viram na mesma conta.
103
108
  const store = (0, runtime_profiles_1.lerPerfisComContas)(raiz, opcoes.agoraMs);
104
109
  const doRuntime = (0, runtime_profiles_1.perfisDoRuntime)(store, runtime);
105
- if (opcoes.perfil !== undefined) {
106
- const pedido = doRuntime.find(p => p.id === opcoes.perfil);
107
- if (!pedido)
108
- return { perfil: null, configurados: doRuntime.length, erro: `perfil "${opcoes.perfil}" nao existe para o runtime ${runtime}` };
109
- if (!(0, runtime_profiles_1.perfilDisponivel)(pedido, opcoes.agoraMs))
110
- return { perfil: null, configurados: doRuntime.length,
111
- erro: `perfil "${pedido.id}" esta ${pedido.estado}${pedido.esgotadoAte ? ` ate ${pedido.esgotadoAte}` : ''}` };
112
- return { perfil: (0, runtime_profiles_1.perfilDeDespacho)(pedido), configurados: doRuntime.length };
113
- }
110
+ if (opcoes.perfil !== undefined)
111
+ return perfilPedido(store, runtime, opcoes.perfil, opcoes.agoraMs);
114
112
  if (doRuntime.length === 0)
115
113
  return { perfil: null, configurados: 0 };
116
- const proximo = (0, runtime_profiles_1.proximoPerfilDisponivel)(store, runtime, opcoes.politica ?? runtime_profiles_1.POLITICA_PADRAO, opcoes);
114
+ const politica = opcoes.politica ?? runtime_profiles_1.POLITICA_PADRAO;
115
+ const proximo = politica.distribuir === 'carga' && opcoes.carga
116
+ ? (0, runtime_profiles_1.proximoPerfilPorCarga)(store, runtime, politica, opcoes.carga, opcoes)
117
+ : (0, runtime_profiles_1.proximoPerfilDisponivel)(store, runtime, politica, opcoes);
117
118
  if (proximo)
118
119
  return { perfil: (0, runtime_profiles_1.perfilDeDespacho)(proximo), configurados: doRuntime.length };
119
120
  const agoraMs = opcoes.agoraMs ?? Date.now();
@@ -125,6 +126,41 @@ function escolherPerfil(raiz, runtime, opcoes = {}) {
125
126
  return { perfil: null, configurados: doRuntime.length, motivo,
126
127
  erro: `nenhum perfil disponivel do runtime ${runtime} (${doRuntime.map(p => `${p.id}: ${p.estado}`).join(', ')})` };
127
128
  }
129
+ /**
130
+ * RM-056 (D1): o perfil PEDIDO (`--perfil`), validado contra o store. Recusa com motivo tipado e nunca
131
+ * troca de perfil: inexistente ou de outro runtime e `runtime.profile-invalid`; esgotado, a cota; sem
132
+ * login (ou de provider pago), o motivo do estado. Funcao propria, para o despacho da RM-055 rebasar limpo.
133
+ */
134
+ function perfilPedido(store, runtime, id, agoraMs = Date.now()) {
135
+ const doRuntime = (0, runtime_profiles_1.perfisDoRuntime)(store, runtime);
136
+ const pedido = doRuntime.find(p => p.id === id);
137
+ if (!pedido) {
138
+ const outro = store.perfis.find(p => p.id === id);
139
+ return { perfil: null, configurados: doRuntime.length, motivo: 'runtime.profile-invalid',
140
+ erro: outro ? `perfil "${id}" e do runtime ${outro.runtime}, nao do ${runtime}`
141
+ : `perfil "${id}" nao existe no store (${doRuntime.length ? `perfis do ${runtime}: ${doRuntime.map(p => p.id).join(', ')}` : `nenhum perfil do ${runtime}`})` };
142
+ }
143
+ if ((0, runtime_profiles_1.perfilDisponivel)(pedido, agoraMs))
144
+ return { perfil: (0, runtime_profiles_1.perfilDeDespacho)(pedido), configurados: doRuntime.length };
145
+ const motivo = pedido.estado === 'esgotado' ? 'runtime.quota-exhausted' : pedido.estado === 'sem-auth'
146
+ ? 'runtime.auth-missing' : pedido.estado === 'provider-pago' ? 'cost.violation' : 'runtime.profile-invalid';
147
+ return { perfil: null, configurados: doRuntime.length, motivo,
148
+ erro: `perfil "${pedido.id}" esta ${pedido.estado}${pedido.esgotadoAte ? ` ate ${pedido.esgotadoAte}` : ''}` };
149
+ }
150
+ /**
151
+ * RM-056 (D3): a carga dos perfis do runtime, so quando o manifesto pede `distribuir: carga` e o
152
+ * dono nao pediu um perfil. Consulta falha vale carga desconhecida (zero): decide o desempate.
153
+ */
154
+ function cargaDoDespacho(carregado, runtime, politica, pedido) {
155
+ if (politica.distribuir !== 'carga' || pedido !== undefined || !(0, runtime_profiles_1.runtimeComPerfil)(runtime))
156
+ return undefined;
157
+ try {
158
+ return (0, sessoes_contas_1.sessoesVivasPorPerfil)(carregado.raiz, runtime, carregado.manifesto.concurrency.stale_after_min);
159
+ }
160
+ catch {
161
+ return new Map();
162
+ }
163
+ }
128
164
  /**
129
165
  * I-33 (D7, D13): preflight de auth antes do despacho. O perfil escolhido tem o login conferido
130
166
  * pelo proprio CLI com o env dele; reprovado, vira `sem-auth` no store (ou `provider-pago`, quando
@@ -136,8 +172,9 @@ function perfilParaDespacho(carregado, thread, fase, runtime, opcoes) {
136
172
  const { raiz } = carregado;
137
173
  const excluir = [...(opcoes.excluir ?? [])];
138
174
  const politica = (0, runtime_profiles_1.politicaDeRotacao)(carregado.manifesto);
175
+ const carga = cargaDoDespacho(carregado, runtime, politica, opcoes.perfil);
139
176
  for (;;) {
140
- const escolha = escolherPerfil(raiz, runtime, { perfil: opcoes.perfil, excluir, politica });
177
+ const escolha = escolherPerfil(raiz, runtime, { perfil: opcoes.perfil, excluir, politica, carga });
141
178
  if (escolha.erro || !escolha.perfil || opcoes.dryRun)
142
179
  return escolha;
143
180
  const auth = runtime === 'claude-bg' ? (0, claude_bg_1.conferirAuth)(escolha.perfil) : runtime === 'codex' ? (0, codex_1.conferirAuth)(escolha.perfil)
@@ -164,7 +201,7 @@ function perfilParaDespacho(carregado, thread, fase, runtime, opcoes) {
164
201
  razao = 'o preflight de auth reprovou o perfil antes do despacho (D7): perfil sem login nunca recebe despacho';
165
202
  }
166
203
  excluir.push(escolha.perfil.id);
167
- const proximo = opcoes.perfil === undefined ? escolherPerfil(raiz, runtime, { excluir, politica }).perfil : null;
204
+ const proximo = opcoes.perfil === undefined ? escolherPerfil(raiz, runtime, { excluir, politica, carga }).perfil : null;
168
205
  (0, ledger_1.registrar)((0, thread_1.dirThread)(raiz, thread.id), thread.id, ledger_1.TIPOS_DE_EVENTO.perfilRotacionado, {
169
206
  fase, motivo, origem: `${opcoes.origem}.preflight`,
170
207
  de: { runtime, perfil: escolha.perfil.id }, para: proximo ? { runtime, perfil: proximo.id } : null,
@@ -314,8 +351,10 @@ function montarPrompt(thread, fase, pedido, raiz, memoriaInjetada = '') {
314
351
  */
315
352
  function montarPromptComMemoria(carregado, thread, fase, pedido, memoria) {
316
353
  const injecao = (0, memoria_1.injecaoDaFase)(carregado, memoria, thread, fase);
354
+ // Mesma raiz usada pelo MCP e runtime-context; o manifesto da WT nao habilita a dica.
355
+ const ligado = (0, mcp_grafo_1.grafoLigado)((0, manifest_1.exigirManifesto)((0, estado_thread_1.raizDoEstado)(carregado.raiz)).manifesto);
317
356
  return {
318
- prompt: montarPrompt(thread, fase, pedido, carregado.raiz, injecao.texto),
357
+ prompt: montarPrompt(thread, fase, (0, prompts_1.pedidoComDicaDoGrafo)(pedido, thread.id, ligado), carregado.raiz, injecao.texto),
319
358
  injecao,
320
359
  };
321
360
  }
@@ -1039,6 +1078,15 @@ tomadaPrevia) {
1039
1078
  erro,
1040
1079
  };
1041
1080
  }
1081
+ // RM-055: o que so o dono resolve no terminal vira a pausa dele, com o comando exato e o que o ork
1082
+ // faz depois. O prompt ja esta gravado com o sha256 acima, e e ele que o retry re-despacha.
1083
+ const impedimento = (0, impedimento_1.registrarImpedimentoDoDono)(dir, thread, { fase, slug, runtime, cwd, origem: 'phase.run',
1084
+ promptPath, promptSha256: sha, perfil, saida: `${resultado.stderr}\n${erro}` });
1085
+ if (impedimento) {
1086
+ return { thread, slug, promptPath, promptSha256: sha, comando: resultado.comando, controlador: resultado.controlador,
1087
+ sessionId: null, verificada: false, pausaAoFim, dryRun: false, runtime, model, effort, bloqueado: false,
1088
+ motivo: impedimento.motivo, violacoes, naFila: null, erro: `${impedimento.motivo}: ${impedimento.trecho}` };
1089
+ }
1042
1090
  return {
1043
1091
  thread,
1044
1092
  slug,
@@ -1130,8 +1178,13 @@ function tabelaDoLedger(raiz, threadId) {
1130
1178
  detalhe.push(`acao ${String(e.acao)}`);
1131
1179
  if (e.playbook)
1132
1180
  detalhe.push(`playbook ${String(e.playbook)}`);
1133
- if (e.pedido)
1134
- detalhe.push(`pedido ${String(e.pedido)}`);
1181
+ // Ensaio de 03/10/2026 (RM-049): na decisao autonoma o pedido e o objeto `ork.hitl/v2`, e a tabela
1182
+ // mostrava "pedido [object Object]"; do objeto sai o id curto.
1183
+ if (typeof e.pedido === 'string' && e.pedido !== '')
1184
+ detalhe.push(`pedido ${e.pedido}`);
1185
+ else if (e.pedido && typeof e.pedido === 'object' && typeof e.pedido.id === 'string') {
1186
+ detalhe.push(`pedido ${e.pedido.id.slice(0, 8)}`);
1187
+ }
1135
1188
  if (e.rodada !== undefined)
1136
1189
  detalhe.push(`rodada ${String(e.rodada)}`);
1137
1190
  if (e.cobertura)