@orkastery/cli 0.5.1 → 0.5.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (260) hide show
  1. package/adapters/claude-code/README.md +158 -0
  2. package/adapters/claude-code/agents/ork-master.md +2 -0
  3. package/adapters/claude-code/commands/master.md +8 -0
  4. package/adapters/claude-code/commands/ork.md +9 -0
  5. package/adapters/claude-code/mods/autoridade.json +56 -0
  6. package/adapters/claude-code/mods/ork-clima/.claude-plugin/plugin.json +14 -0
  7. package/adapters/claude-code/mods/ork-clima/hooks/contas.ts +283 -0
  8. package/adapters/claude-code/mods/ork-clima/hooks/hooks.json +3 -0
  9. package/adapters/claude-code/mods/ork-clima/hooks/previsao.ts +393 -0
  10. package/adapters/claude-code/mods/ork-clima/hooks/register.ts +205 -0
  11. package/adapters/claude-code/mods/ork-clima/orkastery-mod.json +5 -0
  12. package/adapters/claude-code/mods/ork-clima/types/index.d.ts +80 -0
  13. package/adapters/claude-code/mods/ork-freio/.claude-plugin/plugin.json +14 -0
  14. package/adapters/claude-code/mods/ork-freio/hooks/carga.ts +160 -0
  15. package/adapters/claude-code/mods/ork-freio/hooks/conferencia.ts +309 -0
  16. package/adapters/claude-code/mods/ork-freio/hooks/hooks.json +3 -0
  17. package/adapters/claude-code/mods/ork-freio/hooks/lote.ts +270 -0
  18. package/adapters/claude-code/mods/ork-freio/hooks/register.ts +285 -0
  19. package/adapters/claude-code/mods/ork-freio/orkastery-mod.json +5 -0
  20. package/adapters/claude-code/mods/ork-freio/types/index.d.ts +127 -0
  21. package/adapters/claude-code/mods/ork-mesa/.claude-plugin/plugin.json +14 -0
  22. package/adapters/claude-code/mods/ork-mesa/hooks/cartoes.tsx +246 -0
  23. package/adapters/claude-code/mods/ork-mesa/hooks/decidir.ts +110 -0
  24. package/adapters/claude-code/mods/ork-mesa/hooks/fila.ts +253 -0
  25. package/adapters/claude-code/mods/ork-mesa/hooks/hooks.json +3 -0
  26. package/adapters/claude-code/mods/ork-mesa/hooks/obvia.ts +98 -0
  27. package/adapters/claude-code/mods/ork-mesa/hooks/register.tsx +493 -0
  28. package/adapters/claude-code/mods/ork-mesa/orkastery-mod.json +5 -0
  29. package/adapters/claude-code/mods/ork-mesa/types/index.d.ts +131 -0
  30. package/adapters/claude-code/mods/ork-pista/.claude-plugin/plugin.json +14 -0
  31. package/adapters/claude-code/mods/ork-pista/hooks/acoes.ts +71 -0
  32. package/adapters/claude-code/mods/ork-pista/hooks/ci.ts +117 -0
  33. package/adapters/claude-code/mods/ork-pista/hooks/hooks.json +3 -0
  34. package/adapters/claude-code/mods/ork-pista/hooks/pista.tsx +356 -0
  35. package/adapters/claude-code/mods/ork-pista/hooks/register.tsx +439 -0
  36. package/adapters/claude-code/mods/ork-pista/orkastery-mod.json +5 -0
  37. package/adapters/claude-code/mods/ork-pista/types/index.d.ts +85 -0
  38. package/adapters/claude-code/mods/ork-ponte/.claude-plugin/plugin.json +13 -0
  39. package/adapters/claude-code/mods/ork-ponte/hooks/faixa.tsx +57 -0
  40. package/adapters/claude-code/mods/ork-ponte/hooks/guarda.ts +410 -0
  41. package/adapters/claude-code/mods/ork-ponte/hooks/hooks.json +3 -0
  42. package/adapters/claude-code/mods/ork-ponte/hooks/mostrado.ts +55 -0
  43. package/adapters/claude-code/mods/ork-ponte/hooks/nucleo.ts +157 -0
  44. package/adapters/claude-code/mods/ork-ponte/hooks/papel.ts +42 -0
  45. package/adapters/claude-code/mods/ork-ponte/hooks/register.ts +331 -0
  46. package/adapters/claude-code/mods/ork-ponte/hooks/vigia.ts +76 -0
  47. package/adapters/claude-code/mods/ork-ponte/orkastery-mod.json +5 -0
  48. package/adapters/claude-code/mods/ork-ponte/types/index.d.ts +178 -0
  49. package/adapters/claude-code/mods/ork-portao/.claude-plugin/plugin.json +14 -0
  50. package/adapters/claude-code/mods/ork-portao/hooks/hooks.json +3 -0
  51. package/adapters/claude-code/mods/ork-portao/hooks/lease.ts +201 -0
  52. package/adapters/claude-code/mods/ork-portao/hooks/plano.ts +304 -0
  53. package/adapters/claude-code/mods/ork-portao/hooks/register.ts +339 -0
  54. package/adapters/claude-code/mods/ork-portao/hooks/verify.ts +152 -0
  55. package/adapters/claude-code/mods/ork-portao/orkastery-mod.json +5 -0
  56. package/adapters/claude-code/mods/ork-portao/types/index.d.ts +60 -0
  57. package/adapters/claude-code/mods/ork-radar/.claude-plugin/plugin.json +14 -0
  58. package/adapters/claude-code/mods/ork-radar/hooks/colisao.ts +420 -0
  59. package/adapters/claude-code/mods/ork-radar/hooks/hooks.json +3 -0
  60. package/adapters/claude-code/mods/ork-radar/hooks/mapa.tsx +322 -0
  61. package/adapters/claude-code/mods/ork-radar/hooks/register.tsx +470 -0
  62. package/adapters/claude-code/mods/ork-radar/orkastery-mod.json +5 -0
  63. package/adapters/claude-code/mods/ork-radar/types/index.d.ts +72 -0
  64. package/adapters/claude-code/mods/ork-sentinela/.claude-plugin/plugin.json +14 -0
  65. package/adapters/claude-code/mods/ork-sentinela/hooks/alertas.ts +85 -0
  66. package/adapters/claude-code/mods/ork-sentinela/hooks/despacho.ts +156 -0
  67. package/adapters/claude-code/mods/ork-sentinela/hooks/hooks.json +3 -0
  68. package/adapters/claude-code/mods/ork-sentinela/hooks/register.ts +511 -0
  69. package/adapters/claude-code/mods/ork-sentinela/hooks/vigia.ts +186 -0
  70. package/adapters/claude-code/mods/ork-sentinela/orkastery-mod.json +5 -0
  71. package/adapters/claude-code/mods/ork-sentinela/types/index.d.ts +65 -0
  72. package/adapters/claude-code/mods/ork-torre/.claude-plugin/plugin.json +14 -0
  73. package/adapters/claude-code/mods/ork-torre/hooks/agenda.ts +60 -0
  74. package/adapters/claude-code/mods/ork-torre/hooks/detalhe.tsx +236 -0
  75. package/adapters/claude-code/mods/ork-torre/hooks/frases.ts +42 -0
  76. package/adapters/claude-code/mods/ork-torre/hooks/hooks.json +3 -0
  77. package/adapters/claude-code/mods/ork-torre/hooks/painel.tsx +344 -0
  78. package/adapters/claude-code/mods/ork-torre/hooks/rede.ts +97 -0
  79. package/adapters/claude-code/mods/ork-torre/hooks/register.tsx +437 -0
  80. package/adapters/claude-code/mods/ork-torre/orkastery-mod.json +5 -0
  81. package/adapters/claude-code/mods/ork-torre/types/index.d.ts +108 -0
  82. package/adapters/codex/skills/ork/SKILL.md +18 -3
  83. package/adapters/hermes/README.md +1 -0
  84. package/adapters/hermes/bin/ork-network-status.sh +10 -0
  85. package/adapters/hermes/hermes.plugin.json +1 -0
  86. package/adapters/hermes/hitl-ingress/__init__.py +2 -2
  87. package/adapters/hermes/skills/orkastery-devmaster/SKILL.md +7 -6
  88. package/adapters/openclaw/README.md +26 -5
  89. package/adapters/openclaw/dist/hitl-ingress.js +2 -2
  90. package/adapters/openclaw/dist/index.js +46 -11
  91. package/adapters/openclaw/openclaw.plugin.json +5 -1
  92. package/adapters/openclaw/package.json +2 -2
  93. package/adapters/openclaw/src/hitl-ingress.ts +2 -2
  94. package/adapters/openclaw/src/index.ts +49 -11
  95. package/assets/docs-en/markdownlint-cli2.jsonc +22 -0
  96. package/assets/docs-en/padroes/documentacao-de-produto.md +135 -0
  97. package/assets/docs-en/padroes/roadmap-de-produto.md +100 -0
  98. package/assets/docs-en/produto/README.md +14 -0
  99. package/assets/docs-en/produto/_modelo-feature.md +62 -0
  100. package/assets/docs-en/roadmap/README.md +14 -0
  101. package/assets/docs-en/roadmap/_modelo-item.md +75 -0
  102. package/assets/orkmind_bridge.py +117 -9
  103. package/dist/adapters/claude-bg.js +116 -18
  104. package/dist/adapters/codex.js +14 -4
  105. package/dist/agents-md.js +24 -14
  106. package/dist/auditrun.js +22 -3
  107. package/dist/board.js +20 -17
  108. package/dist/branch-de-estado.js +88 -10
  109. package/dist/busca-semantica.js +52 -12
  110. package/dist/canarios-rm057.js +149 -0
  111. package/dist/canarios.js +2 -0
  112. package/dist/catalogo.js +33 -0
  113. package/dist/ci.js +21 -8
  114. package/dist/claims-conferir.js +342 -0
  115. package/dist/claims.js +41 -1
  116. package/dist/company-brain-client.js +36 -1
  117. package/dist/company-brain-context.js +158 -22
  118. package/dist/company-brain-journal.js +2 -1
  119. package/dist/company-brain-migration.js +36 -1
  120. package/dist/conducao-sentinela.js +388 -0
  121. package/dist/conducao-texto.js +12 -18
  122. package/dist/conducao.js +122 -30
  123. package/dist/conhecimento.js +490 -0
  124. package/dist/docs.js +70 -17
  125. package/dist/doctor-pulse-cron.js +349 -0
  126. package/dist/doctor-rede.js +150 -0
  127. package/dist/doctor.js +195 -111
  128. package/dist/entrega-pr.js +42 -29
  129. package/dist/escopo-escrita.js +10 -1
  130. package/dist/esgotamentos.js +181 -0
  131. package/dist/estado-thread.js +23 -1
  132. package/dist/experiencia-instalacao.js +45 -25
  133. package/dist/experiencia.js +12 -2
  134. package/dist/fabrica-batida.js +310 -0
  135. package/dist/fabrica-estado.js +52 -14
  136. package/dist/fabrica-publicar.js +9 -1
  137. package/dist/fechamento.js +20 -66
  138. package/dist/fila-pesada.js +444 -0
  139. package/dist/fix.js +3 -2
  140. package/dist/forja.js +31 -7
  141. package/dist/gates.js +3 -24
  142. package/dist/handoff.js +10 -6
  143. package/dist/hitl-contract.js +59 -4
  144. package/dist/hitl-curto.js +11 -6
  145. package/dist/hitl-gates.js +5 -0
  146. package/dist/hitl-local-receipt.js +2 -0
  147. package/dist/hitl-lote.js +5 -2
  148. package/dist/hitl-resumo.js +38 -3
  149. package/dist/hitl-tempo-parado.js +201 -0
  150. package/dist/hitl-texto-livre.js +5 -5
  151. package/dist/hitl.js +29 -33
  152. package/dist/horario.js +41 -7
  153. package/dist/hosts.js +104 -11
  154. package/dist/impedimento.js +299 -0
  155. package/dist/index.js +961 -542
  156. package/dist/indice-vetorial.js +87 -10
  157. package/dist/indice.js +9 -1
  158. package/dist/init.js +56 -43
  159. package/dist/instabilidade.js +144 -0
  160. package/dist/integracoes-locais.js +4 -0
  161. package/dist/intelligence-graph-cli.js +170 -16
  162. package/dist/intelligence-graph-contexto.js +254 -0
  163. package/dist/intelligence-graph-contract.js +2 -1
  164. package/dist/intelligence-graph-extract-md.js +56 -4
  165. package/dist/intelligence-graph-extract-ts.js +71 -3
  166. package/dist/intelligence-graph-parsers.js +6 -4
  167. package/dist/intelligence-graph-query.js +66 -19
  168. package/dist/leases.js +664 -87
  169. package/dist/ledger-stats.js +15 -1
  170. package/dist/ledger.js +18 -1
  171. package/dist/licoes.js +1 -0
  172. package/dist/liveness.js +6 -3
  173. package/dist/locale-ajuda.js +761 -0
  174. package/dist/locale-gerados.js +160 -0
  175. package/dist/locale-master.js +99 -0
  176. package/dist/locale-onboarding.js +92 -0
  177. package/dist/locale-phase.js +151 -0
  178. package/dist/locale-pulse.js +211 -0
  179. package/dist/locale-retry.js +385 -0
  180. package/dist/locale-roadmap.js +102 -0
  181. package/dist/locale-ship.js +494 -0
  182. package/dist/locale-thread.js +277 -0
  183. package/dist/locale-verify.js +125 -0
  184. package/dist/locale.js +714 -0
  185. package/dist/maestro-runtime.js +4 -1
  186. package/dist/manifest.js +149 -4
  187. package/dist/maquina.js +23 -4
  188. package/dist/master-digest.js +6 -1
  189. package/dist/master-nota.js +18 -11
  190. package/dist/master.js +145 -31
  191. package/dist/mcp-artifacts.js +43 -3
  192. package/dist/mcp-git.js +42 -8
  193. package/dist/mcp-grafo-worker.js +5 -3
  194. package/dist/mcp-grafo.js +55 -6
  195. package/dist/mcp-server.js +22 -3
  196. package/dist/mcp-ship.js +3 -1
  197. package/dist/mcp-verify.js +6 -1
  198. package/dist/memoria.js +48 -6
  199. package/dist/modos.js +3 -6
  200. package/dist/network-roadmap.js +196 -18
  201. package/dist/noctum-arco.js +911 -0
  202. package/dist/noctum-capacidade.js +141 -0
  203. package/dist/noctum-envelope.js +810 -0
  204. package/dist/noctum-execucao.js +304 -0
  205. package/dist/noctum-fila.js +280 -0
  206. package/dist/ocupacao.js +40 -5
  207. package/dist/onboarding.js +40 -21
  208. package/dist/orkmind.js +99 -7
  209. package/dist/orquestracao.js +24 -21
  210. package/dist/parado-no-condutor.js +219 -38
  211. package/dist/phase.js +160 -52
  212. package/dist/policies.js +56 -5
  213. package/dist/portfolio.js +8 -3
  214. package/dist/postura-local.js +234 -0
  215. package/dist/preflight.js +11 -10
  216. package/dist/procedencia.js +225 -0
  217. package/dist/projeto-alvo.js +132 -5
  218. package/dist/prompts.js +40 -1
  219. package/dist/prova-ativacao.js +75 -6
  220. package/dist/pulse-delivery.js +47 -4
  221. package/dist/pulse.js +98 -19
  222. package/dist/ratelimit.js +24 -6
  223. package/dist/recall.js +3 -1
  224. package/dist/redacao-saida.js +63 -0
  225. package/dist/rede-adesao.js +399 -0
  226. package/dist/rede-forja.js +289 -0
  227. package/dist/rede-projetos.js +239 -0
  228. package/dist/rede-status.js +337 -0
  229. package/dist/rede.js +980 -0
  230. package/dist/retry.js +291 -140
  231. package/dist/roadmap-reservas.js +20 -3
  232. package/dist/roadmap-status.js +150 -34
  233. package/dist/runtime-context.js +181 -3
  234. package/dist/runtime-profiles.js +22 -1
  235. package/dist/runtimes.js +2 -1
  236. package/dist/saida-segura.js +58 -0
  237. package/dist/session-events.js +7 -3
  238. package/dist/session-watcher-claude.js +120 -24
  239. package/dist/session-watcher.js +185 -33
  240. package/dist/sessoes-contas.js +187 -0
  241. package/dist/sessoes-inventario.js +6 -18
  242. package/dist/sessoes.js +83 -2
  243. package/dist/ship-publicar.js +800 -0
  244. package/dist/ship.js +157 -88
  245. package/dist/steal.js +77 -0
  246. package/dist/thread-close.js +3 -2
  247. package/dist/thread.js +299 -49
  248. package/dist/util.js +8 -1
  249. package/dist/verify.js +141 -52
  250. package/dist/vida-da-thread.js +254 -0
  251. package/dist/worktree-local.js +165 -0
  252. package/dist/worktree.js +25 -2
  253. package/dist/yaml.js +27 -8
  254. package/eval/fixtures/fx-pedido-colado/caso.json +20 -0
  255. package/package.json +7 -3
  256. package/references/definition-of-done.md +1 -1
  257. package/schemas/code-artifact-graph.v1.schema.json +2 -1
  258. package/skills/README.md +1 -1
  259. package/skills/core/orkastery-bootstrap/SKILL.md +2 -2
  260. package/skills/phases/master-metrics/SKILL.md +8 -3
@@ -33,17 +33,24 @@ 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.ghPadrao = 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
+ exports.prDaForja = prDaForja;
39
+ exports.githubDoRemoto = githubDoRemoto;
38
40
  exports.repositorioDoRemoto = repositorioDoRemoto;
39
41
  exports.lerPrsDaForja = lerPrsDaForja;
40
42
  exports.gravarRetratoDePrs = gravarRetratoDePrs;
43
+ exports.avisarForjaSemLeitura = avisarForjaSemLeitura;
44
+ exports.esquecerForjaSemLeitura = esquecerForjaSemLeitura;
45
+ exports.forjaSemLeituraDoRemoto = forjaSemLeituraDoRemoto;
41
46
  exports.lerRetratoDePrs = lerRetratoDePrs;
42
47
  exports.perguntaAberta = perguntaAberta;
43
48
  exports.fimDoTurno = fimDoTurno;
44
49
  exports.pendenciaDoDono = pendenciaDoDono;
50
+ exports.conducaoAgora = conducaoAgora;
45
51
  exports.esperaDoCondutor = esperaDoCondutor;
46
52
  exports.sessaoSemPergunta = sessaoSemPergunta;
53
+ exports.refsLocais = refsLocais;
47
54
  exports.entregasDoProjeto = entregasDoProjeto;
48
55
  /**
49
56
  * RM-037 (fatia 4): o trabalho parado no condutor.
@@ -72,6 +79,7 @@ exports.entregasDoProjeto = entregasDoProjeto;
72
79
  const fs = __importStar(require("node:fs"));
73
80
  const path = __importStar(require("node:path"));
74
81
  const node_child_process_1 = require("node:child_process");
82
+ const branch_de_estado_1 = require("./branch-de-estado");
75
83
  const conducao_1 = require("./conducao");
76
84
  const estado_thread_1 = require("./estado-thread");
77
85
  const forja_1 = require("./forja");
@@ -102,7 +110,8 @@ const PRAZO_DO_GH_MS = 30000;
102
110
  /** A leitura inteira da forja numa batida: cabe com folga nos 240 s que a varredura da ao subprocesso do pulse. */
103
111
  const ORCAMENTO_DA_LEITURA_MS = 60000;
104
112
  const CAMPOS_DOS_RECENTES = 'number,state,headRefName,headRefOid,baseRefName,isDraft,isCrossRepository,url,createdAt,mergedAt';
105
- const CAMPOS_DO_GH = `${CAMPOS_DOS_RECENTES},statusCheckRollup`;
113
+ /** RM-059 (F7, D11 da ork-rm059pista): o estado de merge do PR aberto, que a pista de entrega mostra como conflito. */
114
+ const CAMPOS_DO_GH = `${CAMPOS_DOS_RECENTES},statusCheckRollup,mergeable`;
106
115
  /** Quantas branches candidatas a "sem PR" sao conferidas uma a uma por batida; o resto fica "PR nao lido". */
107
116
  const LIMITE_DE_CANDIDATAS = 10;
108
117
  /** A conferencia de uma candidata so comeca com este tempo de sobra no orcamento; com menos, a branch fica sem conferir. */
@@ -111,6 +120,8 @@ const MINIMO_DA_CONFERENCIA_MS = 5000;
111
120
  const FASES_ANTES_DA_ENTREGA = ['GOAL', 'PLAN', 'GO', 'CHECK'];
112
121
  /** Os modos em que a #TAG ja autoriza o push (`autorizacaoDePush`, `core/src/ship.ts`). */
113
122
  const MODOS_COM_PUSH_AUTORIZADO = ['auto', 'maestro'];
123
+ /** RM-037 (fatia 5, A5): o que as linhas e o resumo dizem quando a forja nao tem leitura de PR. */
124
+ exports.FORJA_SEM_LEITURA = 'forja sem leitura de PR';
114
125
  /** O ambiente do `gh`: sem cor, sem prompt e sem host ou repositorio herdados (o host vai no `--repo`). */
115
126
  function ambienteDoGh() {
116
127
  const env = { ...process.env, GH_PROMPT_DISABLED: '1', NO_COLOR: '1', CLICOLOR: '0', CLICOLOR_FORCE: '0' };
@@ -123,11 +134,14 @@ const ghPadrao = (args, timeoutMs) => {
123
134
  const r = (0, node_child_process_1.spawnSync)('gh', [...args], { encoding: 'utf8', timeout: timeoutMs, maxBuffer: 16 * 1024 * 1024, env: ambienteDoGh() });
124
135
  return { status: r.status, stdout: r.stdout ?? '', stderr: r.error ? r.error.message : (r.stderr ?? '') };
125
136
  };
137
+ exports.ghPadrao = ghPadrao;
126
138
  /** Git local sem rede escondida: clone parcial nunca busca objeto durante uma leitura daqui. */
127
139
  const git = (raiz, args) => (0, util_1.exec)('git', args, raiz, 60000, { ...process.env, GIT_NO_LAZY_FETCH: '1' });
128
140
  const SHA = /^[0-9a-f]{40}$/;
129
141
  const BRANCH = /^[A-Za-z0-9._/-]{1,200}$/;
130
142
  const REPOSITORIO = /^[A-Za-z0-9_.-]{1,100}\/[A-Za-z0-9_.-]{1,100}$/;
143
+ /** O host do GitHub Enterprise guardado no retrato: rotulos alfanumericos separados por ponto, como na forja. */
144
+ const HOST = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)+$/;
131
145
  /** Texto curto vindo de fora: sem controle, num teto. */
132
146
  function curto(v, teto = 120) {
133
147
  if (typeof v !== 'string')
@@ -212,33 +226,95 @@ function prDaForja(bruto, base) {
212
226
  }
213
227
  }
214
228
  const url = typeof p.url === 'string' && /^https:\/\/[^\s]+$/.test(p.url) ? curto(p.url, 300) : null;
229
+ // RM-059 (F7): o estado de merge so vale do PR aberto, e so quando a leitura pediu o campo.
230
+ const conflito = estado !== 'aberto' || p.mergeable === undefined ? undefined
231
+ : p.mergeable === 'CONFLICTING' ? true : p.mergeable === 'MERGEABLE' ? false : null;
215
232
  return { numero: numero, branch: p.headRefName, head, estado, rascunho: p.isDraft, url,
216
- criadoEm: instante(p.createdAt), mescladoEm: instante(p.mergedAt), checks: [...porChave.values()].map((x) => x.check) };
233
+ criadoEm: instante(p.createdAt), mescladoEm: instante(p.mergedAt), checks: [...porChave.values()].map((x) => x.check),
234
+ ...(conflito !== undefined ? { conflito } : {}) };
235
+ }
236
+ /**
237
+ * 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
238
+ * proprio), so pelo git local, sem rede nem `gh`. O GitLab fica de fora.
239
+ */
240
+ function githubDoRemoto(raiz, remoto) {
241
+ // RM-047: o remoto vem do manifesto versionado; so nome de remoto chega ao git, e depois do `--`.
242
+ if (!(0, branch_de_estado_1.remotoValido)(remoto))
243
+ return null;
244
+ const url = git(raiz, ['remote', 'get-url', '--', remoto]);
245
+ const forja = url.ok ? (0, forja_1.repositorioGithubNoHost)(url.stdout.trim()) : null;
246
+ // `ssh.github.com` e o SSH do github.com pela porta 443, e nao um GitHub Enterprise (sugestao da rodada 1 do CHECK).
247
+ return forja && REPOSITORIO.test(forja.repo) ? { host: forja.host === 'ssh.github.com' ? 'github.com' : forja.host, repositorio: forja.repo }
248
+ : null;
217
249
  }
218
250
  /** O repositorio `dono/nome` do remoto, so quando ele e do github.com (o host ancorado, nao so citado no caminho). */
219
251
  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;
252
+ const github = githubDoRemoto(raiz, remoto);
253
+ return github && github.host === 'github.com' ? github.repositorio : null;
254
+ }
255
+ /**
256
+ * 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`
257
+ * esta autenticado, que e um GitHub Enterprise (`gh auth status --hostname <host>`); ou nenhum: GitLab, caminho local,
258
+ * 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
259
+ * forja, que o pulse diz uma vez.
260
+ */
261
+ function forjaDosPrs(raiz, remoto, executor, prazoMs) {
262
+ // RM-047: remoto que nao e nome de remoto nao chega ao git; para o pulse, e forja sem leitura.
263
+ if (!(0, branch_de_estado_1.remotoValido)(remoto)) {
264
+ return { leitura: false, host: null, motivo: `o remoto ${(0, branch_de_estado_1.remotoRedigido)(remoto)} não é nome de remoto do git`, semLeitura: true };
265
+ }
266
+ const url = git(raiz, ['remote', 'get-url', '--', remoto]);
267
+ if (!url.ok)
268
+ return { leitura: false, host: null, motivo: `o remoto ${remoto} não está configurado neste checkout`, semLeitura: true };
269
+ const endereco = (0, forja_1.enderecoDoRemoto)(url.stdout.trim());
270
+ // Caminho local, ou apelido de SSH sem dominio (`git@github-trabalho:dono/repo.git`): nao ha host para ler.
271
+ if (!endereco) {
272
+ return { leitura: false, host: null, motivo: `o remoto ${remoto} não tem host de forja (caminho local ou apelido de SSH)`, semLeitura: true };
273
+ }
274
+ if ((0, forja_1.identidadeDaForja)(url.stdout.trim())?.tipo === 'gitlab') {
275
+ return { leitura: false, host: endereco.host, motivo: `a forja de ${remoto} (${endereco.host}) é um GitLab, e o ork só lê PR do GitHub`,
276
+ semLeitura: true };
277
+ }
278
+ const github = githubDoRemoto(raiz, remoto);
279
+ if (!github) {
280
+ return { leitura: false, host: endereco.host, motivo: `o remoto ${remoto} (${endereco.host}) não aponta um repositório dono/nome`,
281
+ semLeitura: true };
282
+ }
283
+ if (github.host === 'github.com')
284
+ return { leitura: true, ...github };
285
+ const auth = executor(['auth', 'status', '--hostname', github.host], prazoMs);
286
+ if (auth.status === 0)
287
+ return { leitura: true, ...github };
288
+ // So a resposta de login ausente (o `gh` 2.46 diz "You are not logged into any accounts on <host>") e o estado da
289
+ // forja; prazo estourado, `gh` ausente ou host fora do ar sao falha desta batida.
290
+ const semLogin = auth.status === 1 && /\bnot logged in/i.test(`${auth.stdout}\n${auth.stderr}`);
291
+ return { leitura: false, host: github.host, semLeitura: semLogin, motivo: semLogin
292
+ ? `o gh não está autenticado em ${github.host}, a forja de ${remoto}`
293
+ : `gh auth status --hostname ${github.host} falhou (${auth.status === null ? 'sem código de saída' : `código ${auth.status}`}): ` +
294
+ (curto((0, hitl_1.redigirSegredos)(auth.stderr || auth.stdout || ''), 120) ?? 'sem detalhe') };
223
295
  }
224
296
  /**
225
297
  * Le os PRs da base do repositorio do remoto em duas chamadas: os abertos (ate `LIMITE_DE_PRS`; e so a
226
298
  * 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.
299
+ * O `gh` usa a autenticacao dele; nenhum token passa por aqui. RM-037 (fatia 5, A5): no GitHub Enterprise, o host
300
+ * vai no `--repo`; forja sem leitura de PR nao chama o `gh pr list`.
228
301
  */
229
302
  function lerPrsDaForja(carregado, opcoes = {}) {
230
303
  const lidoEm = opcoes.quando ?? new Date().toISOString();
231
304
  const remoto = opcoes.remoto ?? carregado.manifesto.fabrica.remoto;
232
305
  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
306
  const orcamento = opcoes.orcamentoMs ?? ORCAMENTO_DA_LEITURA_MS, fimDaLeitura = Date.now() + orcamento;
237
307
  const resta = () => fimDaLeitura - Date.now();
308
+ const executor = opcoes.executor ?? exports.ghPadrao;
309
+ const forja = opcoes.forja ? { leitura: true, ...opcoes.forja }
310
+ : forjaDosPrs(carregado.raiz, remoto, executor, Math.min(PRAZO_DO_GH_MS, Math.max(1, resta())));
311
+ if (!forja.leitura)
312
+ return { ok: false, lidoEm, erro: forja.motivo, ...(forja.semLeitura ? { semLeitura: { remoto, host: forja.host } } : {}) };
313
+ const { host, repositorio } = forja;
238
314
  const pedir = (args) => {
239
315
  if (resta() <= 0)
240
316
  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()));
317
+ const r = executor(['pr', 'list', `--repo=${host}/${repositorio}`, `--base=${base}`, ...args], Math.min(PRAZO_DO_GH_MS, resta()));
242
318
  if (r.status !== 0) {
243
319
  const detalhe = curto((0, hitl_1.redigirSegredos)(r.stderr || r.stdout || ''), 160) ?? 'sem detalhe';
244
320
  return `gh pr list falhou (${r.status === null ? 'sem código de saída' : `código ${r.status}`}): ${detalhe}`;
@@ -296,8 +372,8 @@ function lerPrsDaForja(carregado, opcoes = {}) {
296
372
  semConferir.push(branch);
297
373
  }
298
374
  }
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()] } };
375
+ return { ok: true, retrato: { contrato: exports.CONTRATO_PRS, lidoEm, repositorio, ...(host !== 'github.com' ? { host } : {}), base,
376
+ parcial: abertos.length >= exports.LIMITE_DE_PRS, ...(semConferir.length ? { semConferir } : {}), prs: [...porNumero.values()] } };
301
377
  }
302
378
  catch (e) {
303
379
  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 +397,64 @@ function gravarRetratoDePrs(raiz, retrato) {
321
397
  throw e;
322
398
  }
323
399
  }
400
+ const ARQUIVO_DA_FORJA_SEM_LEITURA = 'forja-sem-leitura.json';
401
+ exports.CONTRATO_FORJA_SEM_LEITURA = 'ork.forja-sem-leitura/v1';
402
+ function lerAvisoDeForjaSemLeitura(raiz) {
403
+ try {
404
+ const a = JSON.parse(fs.readFileSync(path.join(dirDoMonitor(raiz), ARQUIVO_DA_FORJA_SEM_LEITURA), 'utf8'));
405
+ const ditoEm = instante(a.ditoEm), motivo = curto(a.motivo, 200);
406
+ if (a.contrato !== exports.CONTRATO_FORJA_SEM_LEITURA || typeof a.remoto !== 'string' || !/^[A-Za-z0-9._-]{1,100}$/.test(a.remoto) ||
407
+ !(a.host === null || (typeof a.host === 'string' && HOST.test(a.host))) || !ditoEm || !motivo)
408
+ return null;
409
+ return { contrato: exports.CONTRATO_FORJA_SEM_LEITURA, remoto: a.remoto, host: a.host, motivo, ditoEm };
410
+ }
411
+ catch {
412
+ return null;
413
+ }
414
+ }
415
+ /**
416
+ * 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,
417
+ * o pulse dizia "PR nao lido" a cada batida, para um estado que nao muda de uma batida para a outra. A marca em
418
+ * `.orkastery/monitor/` guarda o que ja foi dito; devolve true quando e novidade (e grava a marca).
419
+ */
420
+ function avisarForjaSemLeitura(raiz, semLeitura, motivo, quando) {
421
+ const atual = lerAvisoDeForjaSemLeitura(raiz);
422
+ if (atual && atual.remoto === semLeitura.remoto && atual.host === semLeitura.host)
423
+ return false;
424
+ const dir = dirDoMonitor(raiz);
425
+ fs.mkdirSync(dir, { recursive: true });
426
+ const arquivo = path.join(dir, ARQUIVO_DA_FORJA_SEM_LEITURA), tmp = `${arquivo}.${process.pid}.${Date.now()}.tmp`;
427
+ const aviso = { contrato: exports.CONTRATO_FORJA_SEM_LEITURA, ...semLeitura, motivo: curto(motivo, 200) ?? exports.FORJA_SEM_LEITURA,
428
+ ditoEm: quando };
429
+ try {
430
+ fs.writeFileSync(tmp, JSON.stringify(aviso, null, 2) + '\n', { mode: 0o600, flag: 'wx' });
431
+ fs.renameSync(tmp, arquivo);
432
+ }
433
+ catch (e) {
434
+ try {
435
+ fs.unlinkSync(tmp);
436
+ }
437
+ catch { /* ja saiu */ }
438
+ throw e;
439
+ }
440
+ return true;
441
+ }
442
+ /** A leitura voltou: a marca sai, e a forja que perder a leitura de novo volta a ser dita. */
443
+ function esquecerForjaSemLeitura(raiz) {
444
+ fs.rmSync(path.join(dirDoMonitor(raiz), ARQUIVO_DA_FORJA_SEM_LEITURA), { force: true });
445
+ }
446
+ /**
447
+ * 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
448
+ * do roadmap a le sem rede e sem `gh`.
449
+ */
450
+ function forjaSemLeituraDoRemoto(raiz, remoto) {
451
+ const aviso = lerAvisoDeForjaSemLeitura(raiz);
452
+ if (!aviso || aviso.remoto !== remoto || !(0, branch_de_estado_1.remotoValido)(remoto))
453
+ return null;
454
+ const url = git(raiz, ['remote', 'get-url', '--', remoto]);
455
+ const host = url.ok ? (0, forja_1.enderecoDoRemoto)(url.stdout.trim())?.host ?? null : null;
456
+ return host === aviso.host ? aviso : null;
457
+ }
324
458
  /** O ultimo retrato gravado pelo pulse, conferido campo a campo e com os nomes de check limpos de novo. */
325
459
  function lerRetratoDePrs(raiz) {
326
460
  try {
@@ -328,6 +462,7 @@ function lerRetratoDePrs(raiz) {
328
462
  const lidoEm = instante(r.lidoEm);
329
463
  if (r.contrato !== exports.CONTRATO_PRS || !lidoEm || typeof r.repositorio !== 'string' || !REPOSITORIO.test(r.repositorio) ||
330
464
  typeof r.base !== 'string' || typeof r.parcial !== 'boolean' || !Array.isArray(r.prs) ||
465
+ (r.host !== undefined && !(typeof r.host === 'string' && HOST.test(r.host))) ||
331
466
  (r.semConferir !== undefined && !(Array.isArray(r.semConferir) && r.semConferir.every((b) => typeof b === 'string' && BRANCH.test(b)))))
332
467
  return null;
333
468
  const opcional = (v) => (v === null || v === undefined ? null : instante(v) ?? undefined);
@@ -351,8 +486,8 @@ function lerRetratoDePrs(raiz) {
351
486
  prs.push({ numero: p.numero, branch: p.branch, head: p.head, estado, rascunho: p.rascunho,
352
487
  url: typeof p.url === 'string' && /^https:\/\/[^\s]+$/.test(p.url) ? curto(p.url, 300) : null, criadoEm, mescladoEm, checks });
353
488
  }
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 };
489
+ return { contrato: exports.CONTRATO_PRS, lidoEm, repositorio: r.repositorio, ...(typeof r.host === 'string' ? { host: r.host } : {}),
490
+ base: r.base, parcial: r.parcial, ...(Array.isArray(r.semConferir) && r.semConferir.length ? { semConferir: r.semConferir } : {}), prs };
356
491
  }
357
492
  catch {
358
493
  return null;
@@ -411,13 +546,25 @@ const doRadar = (e) => e.tipo === 'sessao_bloqueada' && e.fonte === 'ork session
411
546
  * intervalo do despacho. A prova do ork no SHIP e so o `ship_done` (`provaDoOrk`): o que falta e mergear o PR
412
547
  * ou registrar a entrega, e isso e do condutor, como o fim de turno em `blocked`. Dos 4 `human.pending` do SHIP
413
548
  * 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).
549
+ * e outra coisa: `checkSemVeredito`.
415
550
  */
416
551
  function shipSemRegistro(e) {
417
552
  const { ok: provou } = (e.provaOrk ?? {});
418
553
  return e.motivo === 'human.pending' && e.origem === 'sessions.watch' && e.fase === 'SHIP' && e.estadoNativo === 'done' &&
419
554
  !!e.stop && provou === false;
420
555
  }
556
+ /**
557
+ * RM-037 (fatia 5, A3): o CHECK que o observador viu terminar em `done`, com o Stop, sem exatamente um veredito
558
+ * legivel no `docs/check.md` (`provaDoOrk`). No #Auto, com o bloco sem pausa ao fim, ninguem perguntou nada ao dono:
559
+ * o passo e do condutor, que redespacha o CHECK. Nos dois casos reais (ork-i35horariodo em 20/09 e ork-pacotedeexpe
560
+ * em 30/09, as duas #Auto) o condutor seguiu sozinho minutos depois, sem pergunta ao dono. Fora do #Auto, segue com
561
+ * o dono, como antes.
562
+ */
563
+ function checkSemVeredito(e) {
564
+ const { ok: provou } = (e.provaOrk ?? {});
565
+ return e.motivo === 'human.pending' && e.origem === 'sessions.watch' && e.fase === 'CHECK' && e.estadoNativo === 'done' &&
566
+ !!e.stop && provou === false;
567
+ }
421
568
  /**
422
569
  * O bloqueio que pede o dono: o prompt de permissao do hook e o que o radar leu com pergunta na tela. O
423
570
  * carimbo do radar no fim de turno (`blocked` lido sem menu nem pergunta) nao e prompt nenhum.
@@ -473,7 +620,8 @@ function atividadeDaSessao(e) {
473
620
  return !(e.sensor === 'notification' && e.notificationType === 'idle_prompt');
474
621
  return e.tipo === 'commit' || e.tipo === 'runtime_stop' || (e.tipo === 'sessao_bloqueada' && !doRadar(e));
475
622
  }
476
- function fimDoTurno(eventos, despacho) {
623
+ /** `modo`: o da thread. Sem ele, o CHECK sem o veredito fica com o dono, como fora do #Auto. */
624
+ function fimDoTurno(eventos, despacho, modo) {
477
625
  const i = eventos.lastIndexOf(despacho);
478
626
  const depois = eventos.slice(i + 1);
479
627
  const sid = texto(despacho.sessionId);
@@ -497,12 +645,13 @@ function fimDoTurno(eventos, despacho) {
497
645
  // S-a do CHECK (rodada 3): no bloco com pausa ao fim, todo `human.pending` do resultado e a fase entregue ao dono.
498
646
  return { em: resultado.ts, tipo: 'pausa-do-bloco', sessionId: sid, fase, comProva: true };
499
647
  }
500
- else if (resultado.motivo === 'human.pending' && resultado.stop && (resultado.estadoNativo === 'blocked' || shipSemRegistro(resultado))) {
648
+ else if (resultado.motivo === 'human.pending' && resultado.stop && (resultado.estadoNativo === 'blocked' || shipSemRegistro(resultado) ||
649
+ (modo === 'auto' && checkSemVeredito(resultado)))) {
501
650
  if (!retomou) {
502
651
  const { ts: doStop } = resultado.stop;
503
652
  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 };
653
+ return { em: instante(doStop) ?? resultado.ts, tipo: checkSemVeredito(resultado) ? 'check-sem-veredito' : 'espera-do-observador',
654
+ sessionId: sid, fase, comProva: typeof provou === 'boolean' ? provou : null };
506
655
  }
507
656
  }
508
657
  else
@@ -544,17 +693,17 @@ function pendenciaDoDono(t, eventos, quando) {
544
693
  return 'prompt de permissão pendente';
545
694
  // 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
695
  // 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);
696
+ const fim = fimDoTurno(eventos, despacho, t.modo);
548
697
  const turnoEncerrado = !!fim && fim.tipo !== 'outro';
549
698
  for (let k = 0; k < depois.length; k++) {
550
699
  const e = depois[k];
551
700
  if (e.tipo !== 'gate_blocked')
552
701
  continue;
553
702
  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.
703
+ // 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,
704
+ // o CHECK em `done` sem o veredito (A3, fatia 5) nao sao escalacao: sao justamente o que muda de dono.
556
705
  if (turnoEncerrado && motivo === 'human.pending' && e.origem === 'sessions.watch' &&
557
- (e.estadoNativo === 'blocked' || shipSemRegistro(e)))
706
+ (e.estadoNativo === 'blocked' || shipSemRegistro(e) || (t.modo === 'auto' && checkSemVeredito(e))))
558
707
  continue;
559
708
  const doDono = motivo === 'human.pending' ||
560
709
  (hitl_gates_1.MOTIVOS_DE_ESCALACAO_HUMANA.includes(motivo) && (0, hitl_classificacao_1.quemDecide)(motivo) === 'dono');
@@ -564,13 +713,26 @@ function pendenciaDoDono(t, eventos, quando) {
564
713
  }
565
714
  return null;
566
715
  }
716
+ /**
717
+ * Quem conduz a thread agora, ou `null`. P7: a sessao que ja encerrou o turno nao conduz mais nada, mesmo com o lease
718
+ * de pe. RM-059 (F7): a mesma regra do pulse decide se o `ork ship publicar` pode publicar a branch.
719
+ */
720
+ function conducaoAgora(raiz, t, eventos, quando) {
721
+ const conducao = (0, conducao_1.conducaoDaThread)(raiz, t.id, { agora: new Date(quando) });
722
+ if (!conducao)
723
+ return null;
724
+ const despacho = ultimoDespacho(eventos);
725
+ const fim = despacho ? fimDoTurno(eventos, despacho, t.modo) : null;
726
+ const terminou = !!fim && fim.tipo !== 'outro';
727
+ return conducao.dono.tipo === 'sessao' && terminou && conducao.dono.sessionId === fim.sessionId ? null : conducao;
728
+ }
567
729
  function esperaDoCondutor(t, eventos, quando) {
568
730
  if (t.status === 'fechada')
569
731
  return null;
570
732
  const despacho = ultimoDespacho(eventos);
571
733
  if (!despacho || despacho.pausaAoFim !== false)
572
734
  return null;
573
- const fim = fimDoTurno(eventos, despacho);
735
+ const fim = fimDoTurno(eventos, despacho, t.modo);
574
736
  if (!fim || fim.tipo === 'outro' || fim.tipo === 'pausa-do-bloco' || pendenciaDoDono(t, eventos, quando))
575
737
  return null;
576
738
  return { thread: t.id, fase: fim.fase, sessionId: fim.sessionId, fimDoTurnoEm: fim.em, tipo: fim.tipo, comProva: fim.comProva };
@@ -699,9 +861,13 @@ function entregasDoProjeto(carregado, opcoes) {
699
861
  // R4-4 do CHECK (rodada 4): o `ship_done` cobre a entrega do mesmo head (`shaDe`) ou do mesmo merge; a entrega
700
862
  // nova depois dele ainda nao foi registrada. O legado sem o sha cobre tudo, como antes.
701
863
  const cobre = (campo, sha) => ships.some((e) => typeof e[campo] !== 'string' || !SHA.test(e[campo]) || e[campo] === sha);
864
+ // RM-037 (fatia 5, A1): ate esta fatia, o `ork ship` com a branch ja incorporada gravava a ponta da base no
865
+ // `mergeSha`. Esse `ship_done` registra o merge `ship(<thread>)` que a ponta gravada contem.
866
+ const contemOMerge = (sha) => ships.some((e) => typeof e.mergeSha === 'string' && SHA.test(e.mergeSha) &&
867
+ e.mergeSha !== sha && git(raiz, ['merge-base', '--is-ancestor', sha, e.mergeSha]).code === 0);
702
868
  return { t, eventos, branch: cabeca ? branch : null, cabeca, comProduto, publicada, temRemota: !!remota,
703
869
  shipNoLedger: ships.length > 0, entregueNaPonta: cabeca ? cobre('shaDe', cabeca) : ships.length > 0,
704
- mergeRegistrado: !!merge && cobre('mergeSha', merge.sha), merge };
870
+ mergeRegistrado: !!merge && (cobre('mergeSha', merge.sha) || contemOMerge(merge.sha)), merge };
705
871
  });
706
872
  // D2: a forja so e lida quando alguma thread tem o que mostrar la: produto numa branch que ja foi ao remoto
707
873
  // (inteira, ou com commit novo ainda sem push, que pode ter PR aberto).
@@ -710,11 +876,15 @@ function entregasDoProjeto(carregado, opcoes) {
710
876
  if (opcoes.lerPrs && candidatas.length)
711
877
  prs = opcoes.lerPrs(candidatas);
712
878
  // 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';
879
+ // 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".
880
+ const semLeituraDaForja = !!prs && !prs.ok && !!prs.semLeitura;
881
+ let retrato = null, semRetrato = prs && !prs.ok ? (semLeituraDaForja ? exports.FORJA_SEM_LEITURA : prs.erro) : 'sem leitura dos PRs';
714
882
  if (prs?.ok) {
715
- const r = prs.retrato;
716
- if (r.base !== baseBranch || r.repositorio !== repositorioDoRemoto(raiz, remoto))
883
+ const r = prs.retrato, atual = githubDoRemoto(raiz, remoto);
884
+ // O retrato vale do mesmo host: o do GitHub Enterprise (`host`) ou, sem ele, o github.com.
885
+ if (r.base !== baseBranch || !atual || r.repositorio !== atual.repositorio || (r.host ?? 'github.com') !== atual.host) {
717
886
  semRetrato = 'retrato de PRs de outro repositório ou base';
887
+ }
718
888
  else if (minutosDesde(r.lidoEm, quando) > exports.VALIDADE_DO_RETRATO_MIN)
719
889
  semRetrato = 'retrato de PRs velho';
720
890
  else if (Date.parse(r.lidoEm) - Date.parse(quando) > FOLGA_DO_RELOGIO_MIN * 60000)
@@ -727,7 +897,7 @@ function entregasDoProjeto(carregado, opcoes) {
727
897
  for (const f of fatos) {
728
898
  const { t, eventos } = f;
729
899
  const despacho = ultimoDespacho(eventos);
730
- const fim = despacho ? fimDoTurno(eventos, despacho) : null;
900
+ const fim = despacho ? fimDoTurno(eventos, despacho, t.modo) : null;
731
901
  const terminou = !!fim && fim.tipo !== 'outro';
732
902
  const fimEm = terminou ? fim.em : null;
733
903
  // A1 do CHECK (rodada 3): o fim de turno vem do ledger (Stop sem atividade depois); a tela da sessao depois
@@ -740,9 +910,9 @@ function entregasDoProjeto(carregado, opcoes) {
740
910
  doCondutor.turnosEncerrados.add(espera.sessionId);
741
911
  }
742
912
  }
743
- const conducao = (0, conducao_1.conducaoDaThread)(raiz, t.id, { agora: new Date(quando) });
744
- // P7: a sessao que ja encerrou o turno nao conduz mais nada, mesmo com o lease de pe.
745
- const conduzidaAgora = !!conducao && !(conducao.dono.tipo === 'sessao' && terminou && conducao.dono.sessionId === fim.sessionId);
913
+ // A mesma regra do `ork ship publicar` (RM-059): quem conduz agora, ou ninguem.
914
+ const conducao = conducaoAgora(raiz, t, eventos, quando);
915
+ const conduzidaAgora = conducao !== null;
746
916
  const doDono = pendenciaDoDono(t, eventos, quando);
747
917
  // A2 do CHECK (rodada 3): depois do veredito do dono, o tempo do condutor conta a partir dele.
748
918
  const aprovacao = despacho ? eventos.slice(eventos.lastIndexOf(despacho) + 1).filter(ocupacao_1.ehAprovacaoHumana).at(-1) : undefined;
@@ -771,7 +941,15 @@ function entregasDoProjeto(carregado, opcoes) {
771
941
  let caso = null, desde = null, passo = '', peloGit = false;
772
942
  const evidencia = [];
773
943
  if (!conduzidaAgora && !doDono) {
774
- if (f.comProduto && publicada === false && terminou) {
944
+ if (espera?.tipo === 'check-sem-veredito') {
945
+ // RM-037 (fatia 5, A3): antes de qualquer caso de entrega. Publicar ou mergear sem o veredito do CHECK pularia
946
+ // a revisao; e o `ork retry run` nao redespacha `human.pending` (`escalar-humano`), entao o passo e o despacho.
947
+ caso = 'check-sem-veredito';
948
+ desde = espera.fimDoTurnoEm;
949
+ passo = `redespachar o CHECK (ork phase run ${t.id} CHECK --prompt "<pedido da fase>")`;
950
+ evidencia.push('ledger: CHECK em done com o Stop, sem exatamente um veredito no docs/check.md');
951
+ }
952
+ else if (f.comProduto && publicada === false && terminou) {
775
953
  desde = fimOuVeredito;
776
954
  if (antesDaEntrega && !aberto) {
777
955
  caso = 'fase-seguinte';
@@ -782,7 +960,8 @@ function entregasDoProjeto(carregado, opcoes) {
782
960
  // N2 da seguranca (rodada 2): branch que ja foi ao remoto pode ter PR; sem a leitura, nunca "abrir o PR". A4 da
783
961
  // rodada 5: publicar e abrir o PR pedem a mesma autorizacao de push que o merge.
784
962
  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)`
963
+ // RM-037 (fatia 5, A5): a forja sem leitura de PR ja foi dita uma vez; a linha so diz o passo.
964
+ : f.temRemota && !prSabido ? `publicar os commits novos da branch ${f.branch}${semLeituraDaForja ? '' : ' (PR não lido)'}`
786
965
  : `publicar a branch ${f.branch} e abrir o PR`) + autorizacao;
787
966
  }
788
967
  evidencia.push(`refs/heads/${f.branch} tem commit fora de refs/remotes/${remoto}/${f.branch}`);
@@ -850,7 +1029,8 @@ function entregasDoProjeto(carregado, opcoes) {
850
1029
  caso = 'sessao-sem-pergunta';
851
1030
  passo = f.shipNoLedger && !f.comProduto ? `fechar o MASTER da thread (ork master ${t.id})`
852
1031
  : 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`
1032
+ : precisaDePr && !usouPr ? (semLeituraDaForja ? `conferir na forja o PR da branch ${f.branch} e seguir`
1033
+ : `conferir o PR da branch ${f.branch} (PR não lido) e seguir`)
854
1034
  : `ler o fim da sessão ${id} (ork sessions logs ${id}) e seguir a thread`;
855
1035
  }
856
1036
  }
@@ -880,11 +1060,12 @@ function entregasDoProjeto(carregado, opcoes) {
880
1060
  : daBranch.mesclado && !f.entregueNaPonta ? (f.merge && !f.mergeRegistrado ? `PR #${daBranch.mesclado.numero} mesclado, falta registrar a entrega (${registrar})`
881
1061
  : `PR #${daBranch.mesclado.numero} mesclado sem o assunto ship(${t.id}), falta registrar a entrega`)
882
1062
  : !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'
1063
+ : precisaDePr ? (!usouPr ? (semLeituraDaForja ? `branch publicada, ${exports.FORJA_SEM_LEITURA}` : 'branch publicada, PR não lido')
884
1064
  : daBranch.fechado ? `branch publicada, PR #${daBranch.fechado.numero} fechado sem merge` : 'branch publicada sem PR')
885
1065
  : null;
886
1066
  estados.push({ thread: t.id, branch: f.branch, comProduto: f.comProduto, publicada, pr: prDaEntrega,
887
- prLidoEm: prLido && (usouPr || aberto) ? prLido.lidoEm : null, prNaoLido, conduzidaAgora, espera, parado, resumo });
1067
+ prLidoEm: prLido && (usouPr || aberto) ? prLido.lidoEm : null, prNaoLido, conduzidaAgora,
1068
+ donoDaConducao: conduzidaAgora && conducao ? conducao.dono.tipo : null, espera, parado, resumo });
888
1069
  }
889
1070
  // As sessoes `blocked` sem pergunta que sobraram: de thread fechada, ou de despacho antigo de thread aberta.
890
1071
  // A do despacho corrente fica com o predicado do ledger (`esperaDoCondutor`). Sem o carimbo do radar nao