@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
@@ -57,7 +57,9 @@ const hitl_gates_1 = require("./hitl-gates");
57
57
  const hitl_contract_1 = require("./hitl-contract");
58
58
  const hitl_presentation_1 = require("./hitl-presentation");
59
59
  const roadmap_status_1 = require("./roadmap-status");
60
+ const pulse_1 = require("./pulse");
60
61
  const network_roadmap_1 = require("./network-roadmap");
62
+ const rede_status_1 = require("./rede-status");
61
63
  const hitl_sessions_1 = require("./hitl-sessions");
62
64
  const hitl_local_1 = require("./hitl-local");
63
65
  const mcp_artifacts_1 = require("./mcp-artifacts");
@@ -67,6 +69,7 @@ const modos_1 = require("./modos");
67
69
  const versao_1 = require("./versao");
68
70
  const mcp_verify_1 = require("./mcp-verify");
69
71
  const conducao_1 = require("./conducao");
72
+ const fila_pesada_1 = require("./fila-pesada");
70
73
  const decisao_autonoma_1 = require("./decisao-autonoma");
71
74
  const conducao_texto_1 = require("./conducao-texto");
72
75
  const mcp_ship_1 = require("./mcp-ship");
@@ -107,7 +110,9 @@ const nova = zod_1.z.object({ nome: zod_1.z.string().trim().min(1).max(200),
107
110
  const fase = daThread.extend({ fase: zod_1.z.enum(['GOAL', 'PLAN', 'GO', 'CHECK', 'SHIP', 'MASTER']),
108
111
  prompt: zod_1.z.string().min(1).max(65536), runtime: zod_1.z.enum(['claude-bg', 'codex']).optional(),
109
112
  model: zod_1.z.string().trim().min(1).max(200).optional(),
110
- effort: zod_1.z.enum(['low', 'medium', 'high', 'xhigh', 'max']).optional(), dryRun: zod_1.z.boolean().optional() }).strict();
113
+ effort: zod_1.z.enum(['low', 'medium', 'high', 'xhigh', 'max']).optional(),
114
+ // RM-056 (D1): o perfil pedido, validado contra o store pelo mesmo caminho do CLI.
115
+ perfil: zod_1.z.string().trim().regex(/^[a-z0-9][a-z0-9._-]{0,62}$/i).optional(), dryRun: zod_1.z.boolean().optional() }).strict();
111
116
  // Limite de tipos do conversor externo: validacao continua no schema Zod strict.
112
117
  const converterSchema = zod_to_json_schema_1.zodToJsonSchema;
113
118
  const resposta = (valor) => ({ content: [{ type: 'text', text: JSON.stringify(valor) }] });
@@ -408,6 +413,9 @@ function criarServidorMcp(opcoes) {
408
413
  // I-36 (T18): a mesma recusa tipada do CLI, com o mesmo texto, sem reescrever nada no host.
409
414
  if (e instanceof conducao_1.ErroDeConducao)
410
415
  return { ...resposta(e.recusa), isError: true };
416
+ // RM-058 N2 (A6 do CHECK): a fila pesada que nao deu a vez no prazo responde como a conducao.
417
+ if (e instanceof fila_pesada_1.ErroDaFilaPesada)
418
+ return { ...resposta(e.recusa), isError: true };
411
419
  throw e;
412
420
  }
413
421
  });
@@ -417,7 +425,7 @@ function criarServidorMcp(opcoes) {
417
425
  annotations: { readOnlyHint: false, destructiveHint: false } }, async (args, extra) => {
418
426
  thread(args.threadId);
419
427
  livre(args.threadId);
420
- const r = await (0, mcp_git_1.commitMcp)(raiz, args, extra.signal);
428
+ const r = await (0, mcp_git_1.commitMcp)(raiz, args, extra.signal, opcoes.dispatchId);
421
429
  return { ...resposta(r), ...(r.ok ? {} : { isError: true }) };
422
430
  });
423
431
  const documento = daThread.extend({ tipo: zod_1.z.enum(['goal', 'plan', 'check']) }).strict();
@@ -489,7 +497,8 @@ function criarServidorMcp(opcoes) {
489
497
  // RM-052: origem `instalacao`; o servidor fixado nao revela os outros projetos da maquina.
490
498
  const consulta = (0, projeto_alvo_1.consultaDoProjeto)(c, { origem: 'instalacao', outrosProjetos: false,
491
499
  lido: ['roadmap (docs/roadmap)', projeto_alvo_1.FORA_DA_CONSULTA.threadsDaMaquina], naoLido: [projeto_alvo_1.FORA_DA_CONSULTA.reservas, 'threads de outras máquinas (ork network roadmap)'] });
492
- const status = (0, roadmap_status_1.montarStatusDoRoadmap)(raiz, { projeto: c.manifesto.project.name, consulta });
500
+ // RM-063 (F2): a mesma fila do `ork pulse`; a consulta que falha e dita no fecho, nunca "Nada agora".
501
+ const status = (0, roadmap_status_1.montarStatusDoRoadmap)(raiz, { projeto: c.manifesto.project.name, consulta, fila: (quando) => (0, pulse_1.filaDoDono)(c, { quando }) });
493
502
  return resposta({ texto: (0, roadmap_status_1.textoDoStatusDoRoadmap)(status), status });
494
503
  });
495
504
  /**
@@ -502,6 +511,16 @@ function criarServidorMcp(opcoes) {
502
511
  const panorama = (0, network_roadmap_1.montarPanoramaDaRede)({ fixado: raiz });
503
512
  return resposta({ texto: (0, network_roadmap_1.textoDoPanoramaDaRede)(panorama), panorama });
504
513
  });
514
+ /**
515
+ * RM-054 (fatia 3): as maquinas da pessoa (RM-053, `ork.rede-status/v1`), com a fonte, a batida e as
516
+ * lacunas. O retrato de cada maquina fica so com o projeto servido (D5 da RM-052).
517
+ */
518
+ registrarTool('ork_network_status', { description: 'Status da Orkastery Network, somente leitura: as maquinas da pessoa (casa privada na forja), com a batida de cada uma, a forja e o login, os runtimes e hosts com versao e o projeto servido em cada uma, mais a fonte e as lacunas. Transporte o texto como vem. Lacuna e "Nao consultado" sao o que nao foi lido: nunca conclua "nenhuma maquina" nem "rede vazia" a partir deles. Nao le roadmap nem threads: para isso, ork_network_roadmap.',
519
+ inputSchema: zod_1.z.object({}).strict(), annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: true } }, async () => {
520
+ carregar();
521
+ const status = (0, network_roadmap_1.redeDoProjetoFixado)(raiz);
522
+ return resposta({ texto: (0, rede_status_1.textoDaRede)(status), status: JSON.parse((0, rede_status_1.jsonDaRede)(status)) });
523
+ });
505
524
  registrarTool('ork_observe', { description: 'Observa uma vez o progresso canonico e pedidos da thread. Nao cria monitor ou despacho.',
506
525
  inputSchema: daThread, annotations: { readOnlyHint: true } }, async ({ threadId }) => {
507
526
  return resposta(observar(threadId));
package/dist/mcp-ship.js CHANGED
@@ -353,7 +353,9 @@ function executar(f, p) {
353
353
  sandbox.fechar();
354
354
  }
355
355
  };
356
+ // RM-058 N2 (B1 do CHECK): a fila pesada espera so o que cabe no prazo do worker, para o finally soltar as leases.
356
357
  return (0, ship_1.ship)(c, t.id, { de, para, remoto: 'origin', executorVerify: executor, ttlLeaseMs: PRAZO + RESERVA, retomarLeaseVencido: false,
358
+ esperarFilaPesadaMs: Math.max(0, Math.min(20000, deadline - Date.now() - RESERVA)),
357
359
  revalidarGit: (etapa, dir) => { if (!leasesIntactas())
358
360
  erro('lease.changed'); guard(etapa, dir); } });
359
361
  }
@@ -433,6 +435,6 @@ if (require.main === module && process.argv[2] === '--worker') {
433
435
  }
434
436
  catch (e) {
435
437
  const m = e.message;
436
- process.stdout.write(JSON.stringify({ ok: false, erro: m.startsWith('mcp.') ? m : 'mcp.ship.operation.failed: conferir estado antes de repetir' }));
438
+ process.stdout.write(JSON.stringify({ ok: false, erro: m.startsWith('mcp.') || m.startsWith('fila-pesada.') ? m : 'mcp.ship.operation.failed: conferir estado antes de repetir' }));
437
439
  }
438
440
  }
@@ -33,6 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.ESPERA_DA_FILA_PESADA_MCP_MS = void 0;
36
37
  exports.verificarMcp = verificarMcp;
37
38
  /** Operação estruturada: argumentos só identificam a thread; execução sempre confinada. */
38
39
  const fs = __importStar(require("node:fs"));
@@ -46,6 +47,8 @@ const mcp_artifacts_1 = require("./mcp-artifacts");
46
47
  const verify_1 = require("./verify");
47
48
  const verify_sandbox_1 = require("./verify-sandbox");
48
49
  const worktree_1 = require("./worktree");
50
+ /** RM-058 N2 (D10 da ork-rm058n2): a espera maxima do MCP pela fila de recursos pesados, dentro do prazo total. */
51
+ exports.ESPERA_DA_FILA_PESADA_MCP_MS = 20_000;
49
52
  const PRAZO_TOTAL_MS = 180_000;
50
53
  const RESERVA_MS = 10_000;
51
54
  // I-37 (D4): o teto por comando cabe o `test:ci` medido (196 s); quem limita e o prazo total.
@@ -154,7 +157,9 @@ function verificarMcp(raizEntrada, threadId, conducao = {}) {
154
157
  }
155
158
  };
156
159
  // I-36 (T8): o MESMO lease de execucao do CLI; a sessao filha reentra pela identidade do despacho.
157
- const r = (0, verify_1.verificar)(carregado, threadId, { executor, operacao: 'mcp.verify', conducao });
160
+ // RM-058 N2 (D10): o MCP tem prazo total curto; espera a fila pesada no maximo ESPERA_DA_FILA_PESADA_MCP_MS.
161
+ const r = (0, verify_1.verificar)(carregado, threadId, { executor, operacao: 'mcp.verify',
162
+ conducao: { ...conducao, filaPesada: { esperarMs: Math.min(exports.ESPERA_DA_FILA_PESADA_MCP_MS, conducao.filaPesada?.esperarMs ?? exports.ESPERA_DA_FILA_PESADA_MCP_MS) } } });
158
163
  const execucoes = r.claims.flatMap(c => c.execucoes).concat(r.comandos);
159
164
  return {
160
165
  threadId, ok: r.ok, commit: r.commit, motivos: r.motivos,
package/dist/memoria.js CHANGED
@@ -172,7 +172,7 @@ function abrirMemoria(carregado, opcoes = {}) {
172
172
  const { estado, driver } = (0, orkmind_1.resolverRegime)(carregado.manifesto, opcoes.driver ?? null);
173
173
  const ativo = estado.efetivo === 'orkmind' && driver !== null;
174
174
  const config = (0, orkmind_1.configDoManifesto)(carregado.manifesto);
175
- const embeddings = (universo) => estadoDeEmbeddings(carregado.manifesto, ativo ? driver?.disponivel().saude ?? null : null, carregado.raiz, estado.tenant, config.dsn, { universo });
175
+ const embeddings = (leitura = {}) => estadoDeEmbeddings(carregado.manifesto, ativo ? driver?.disponivel().saude ?? null : null, carregado.raiz, estado.tenant, config.dsn, leitura);
176
176
  if (estado.pedido === 'orkmind')
177
177
  estado.embeddings = embeddings();
178
178
  const consultarRestrito = (collection, tags) => {
@@ -250,10 +250,30 @@ function abrirMemoria(carregado, opcoes = {}) {
250
250
  return null;
251
251
  return driver.exportar(colecao).find((e) => e.id === id) ?? null;
252
252
  },
253
+ universo(tenant) {
254
+ // A fronteira do manifesto vale antes de qualquer leitura: tenant e so o configurado.
255
+ if (tenant !== estado.tenant)
256
+ throw new Error('memory.tenant.mismatch');
257
+ if (leituraRestrita)
258
+ throw new Error('memory.query.invalid');
259
+ if (!ativo || !driver)
260
+ throw new Error('memory.universo.indisponivel');
261
+ if (typeof driver.universo !== 'function')
262
+ throw new Error('memory.universo.unsupported');
263
+ return driver.universo(tenant);
264
+ },
253
265
  };
254
- // A cobertura precisa do universo do tenant (um export por colecao): so quando pedida.
266
+ // RM-038: a cobertura precisa do universo da busca (uma leitura pela ponte): so quando pedida. Falha
267
+ // de leitura (janela saturada, fronteira violada) vira motivo tipado no estado, nunca cobertura inventada.
255
268
  if (opcoes.embeddings === 'detalhado' && ativo && !leituraRestrita) {
256
- estado.embeddings = embeddings((0, indice_vetorial_1.universoDoTenant)(memoria, estado.tenant));
269
+ let leitura;
270
+ try {
271
+ leitura = { universo: (0, indice_vetorial_1.universoDaBusca)(memoria, estado.tenant) };
272
+ }
273
+ catch (erro) {
274
+ leitura = { falhaDoUniverso: (0, indice_vetorial_1.codigoDaFalha)(erro) };
275
+ }
276
+ estado.embeddings = embeddings(leitura);
257
277
  }
258
278
  return memoria;
259
279
  }
@@ -300,6 +320,7 @@ function estadoDeEmbeddings(manifesto, saude, raiz, tenant, dsn, opcoes = {}) {
300
320
  const estado = {
301
321
  configurado, provider: config.provider, modelo: configurado ? config.model : null, dim: configurado ? config.dim : null,
302
322
  variavelDaChave: variavel, chavePresente, fallback, indices: [], entradas: null, cobertura: null,
323
+ universo: null, aviso: null, falhaDoUniverso: null,
303
324
  ativo: 'nenhum', sondado: saude !== null, motivo: null, detalhe: '', correcao: '',
304
325
  };
305
326
  if (!configurado) {
@@ -331,13 +352,24 @@ function estadoDeEmbeddings(manifesto, saude, raiz, tenant, dsn, opcoes = {}) {
331
352
  estado.correcao = `exporte ${variavel} com a chave dedicada ao Orkastery; o valor nunca vai ao manifesto`;
332
353
  }
333
354
  if (opcoes.universo) {
334
- const universo = opcoes.universo.filter(e => (e.tags.project ?? []).includes(tenant));
355
+ // RM-038: o universo vem de universoDaBusca; a fronteira vale de novo antes de contar.
356
+ if (opcoes.universo.tenant !== tenant)
357
+ throw new Error('memory.query.scope-violation');
358
+ (0, indice_vetorial_1.conferirUniverso)(opcoes.universo.entradas, tenant, opcoes.universo.lidoEm);
359
+ const universo = opcoes.universo.entradas;
335
360
  estado.indices = indicesDoTenant(raiz, tenant, base, universo);
336
361
  estado.entradas = universo.length;
362
+ estado.universo = { porColecao: { ...opcoes.universo.porColecao }, foraDaBusca: opcoes.universo.foraDaBusca,
363
+ ...(opcoes.universo.latenciaMs === undefined ? {} : { latenciaMs: opcoes.universo.latenciaMs }) };
337
364
  const referencia = estado.ativo === 'fallback' ? { modelo: fallback.modelo, dim: fallback.dim } : { modelo: config.model, dim: config.dim };
338
365
  const indice = estado.indices.find(i => i.modelo === referencia.modelo && i.dim === referencia.dim);
339
- estado.cobertura = universo.length === 0 ? 0 : Math.round(((indice?.coerentes ?? 0) / universo.length) * 10_000) / 10_000;
366
+ const coerentes = indice?.coerentes ?? 0;
367
+ estado.cobertura = universo.length === 0 ? 0 : Math.round((coerentes / universo.length) * 10_000) / 10_000;
368
+ // RM-038 (D7): o indice que a busca usa cobre menos do que ela enxerga: dito, nunca escondido.
369
+ estado.aviso = (0, indice_vetorial_1.avisoDeCobertura)(coerentes, universo, estado.ativo);
340
370
  }
371
+ else if (opcoes.falhaDoUniverso)
372
+ estado.falhaDoUniverso = opcoes.falhaDoUniverso;
341
373
  return estado;
342
374
  }
343
375
  /**
@@ -1098,12 +1130,22 @@ function textoDosEmbeddings(e) {
1098
1130
  `dependencias ${e.fallback.dependencias ? 'ok' : e.sondado ? 'ausentes' : 'nao sondadas'}` : '(nao declarado)'}`);
1099
1131
  linhas.push(` caminho ativo ${e.ativo}${e.sondado ? '' : ' (ponte nao sondada)'}`);
1100
1132
  }
1133
+ if (e.universo) {
1134
+ const colecoes = Object.entries(e.universo.porColecao).map(([c, n]) => `${c} ${n}`).join(', ');
1135
+ linhas.push(` universo da busca ${e.entradas} entrada(s) do tenant: ${colecoes}`);
1136
+ linhas.push(` fora da busca ${(0, indice_vetorial_1.textoForaDaBusca)(e.universo.foraDaBusca)}`);
1137
+ }
1138
+ else if (e.falhaDoUniverso) {
1139
+ linhas.push(` universo da busca nao lido (${e.falhaDoUniverso}); cobertura nao calculada`);
1140
+ }
1101
1141
  for (const i of e.indices) {
1102
1142
  linhas.push(` indice ${i.modelo} / ${i.dim} dim: ${i.vetores} vetor(es), ${i.coerentes ?? '?'} coerente(s), ${i.desatualizados ?? '?'} desatualizado(s)`);
1103
1143
  }
1104
1144
  if (e.entradas !== null) {
1105
- linhas.push(` cobertura ${e.cobertura === null ? '-' : `${Math.round(e.cobertura * 1000) / 10}%`} de ${e.entradas} entrada(s) do tenant`);
1145
+ linhas.push(` cobertura ${e.cobertura === null ? '-' : `${Math.round(e.cobertura * 1000) / 10}%`} de ${e.entradas} entrada(s) do universo da busca`);
1106
1146
  }
1147
+ if (e.aviso)
1148
+ linhas.push(` aviso ${e.aviso}`);
1107
1149
  if (e.sonda) {
1108
1150
  linhas.push(` sonda ${e.sonda.ok ? `${e.sonda.alvo} respondeu em ${e.sonda.latenciaMs} ms` : `sem resposta (${e.sonda.motivo ?? 'sem caminho ativo'})`}`);
1109
1151
  }
package/dist/modos.js CHANGED
@@ -24,6 +24,7 @@ exports.blocoDaFase = blocoDaFase;
24
24
  exports.fasePausa = fasePausa;
25
25
  exports.tabelaDeModos = tabelaDeModos;
26
26
  const types_1 = require("./types");
27
+ const locale_1 = require("./locale");
27
28
  /** Invariantes que valem em TODOS os modos, inclusive `#Auto`. */
28
29
  exports.INVARIANTES = [
29
30
  'Claims, verify e policies rodam identicos em todos os modos.',
@@ -190,10 +191,7 @@ exports.MOTIVO_MODO_APOSENTADO = 'modo.aposentado';
190
191
  * O texto e DERIVADO de `ORDEM_DOS_MODOS` (ver `substitutosVivos`), nunca escrito a mao.
191
192
  */
192
193
  function recusaDeModoAposentado(modo) {
193
- const tag = exports.MODOS[modo].tag;
194
- return `${exports.MOTIVO_MODO_APOSENTADO}: ${tag} foi aposentado e nao abre trabalho novo. `
195
- + `Use ${substitutosVivos().join(' ou ')}. `
196
- + `Thread ja gravada em ${tag} continua abrindo normalmente.`;
194
+ return (0, locale_1.msg)().thread.modoAposentado(exports.MOTIVO_MODO_APOSENTADO, exports.MODOS[modo].tag, substitutosVivos());
197
195
  }
198
196
  /**
199
197
  * O portao de ESCRITA de modo, com recusa tipada em vez de silencio.
@@ -209,8 +207,7 @@ function exigirModoVivo(entrada) {
209
207
  const cru = (entrada ?? '').trim().replace(/^--?modo[= ]/i, '').replace(/^#/, '').toLowerCase();
210
208
  if (modoAposentado(cru))
211
209
  throw new Error(recusaDeModoAposentado(cru));
212
- throw new Error(`modo.desconhecido: "${entrada ?? ''}" nao e um modo de conducao. `
213
- + `Use um de: ${exports.ORDEM_DOS_MODOS.map((m) => exports.MODOS[m].tag).join(', ')}.`);
210
+ throw new Error((0, locale_1.msg)().thread.modoDesconhecido(entrada ?? '', exports.ORDEM_DOS_MODOS.map((m) => exports.MODOS[m].tag).join(', ')));
214
211
  }
215
212
  /**
216
213
  * Parseia a #TAG de modo de conducao.
@@ -36,6 +36,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.ErroDoPedidoDeProjeto = exports.SAIDA_DO_PEDIDO = exports.LIMIAR_SEM_BATIDA_MS = exports.CONTRATO_REGISTRO_DE_PROJETOS = exports.CONTRATO_PANORAMA_DA_REDE = void 0;
37
37
  exports.projetosConhecidos = projetosConhecidos;
38
38
  exports.resolverProjeto = resolverProjeto;
39
+ exports.redeDoProjetoFixado = redeDoProjetoFixado;
39
40
  exports.montarPanoramaDaRede = montarPanoramaDaRede;
40
41
  exports.textoDoPanoramaDaRede = textoDoPanoramaDaRede;
41
42
  /**
@@ -62,6 +63,11 @@ exports.textoDoPanoramaDaRede = textoDoPanoramaDaRede;
62
63
  *
63
64
  * Na fatia 2, os hosts chegam aqui por dois modos: `host` (gateway sem cwd de projeto, a regra da
64
65
  * RM-052 no `--projeto` proprio do `network`) e `fixado` (o servidor MCP, que le so o projeto dele).
66
+ *
67
+ * Na fatia 3, a rede por pessoa da RM-053 (`ork.rede-status/v1`) entra como fonte: as maquinas da
68
+ * casa, com a batida de cada uma, os projetos que elas declaram (um projeto sem clone nem registro
69
+ * nesta maquina passa a ser conhecido pelo nome) e a maquina que esta na rede mas nao publicou na
70
+ * fabrica do projeto, dita como lacuna e nunca como "0 threads".
65
71
  */
66
72
  const node_child_process_1 = require("node:child_process");
67
73
  const fs = __importStar(require("node:fs"));
@@ -78,8 +84,12 @@ const manifest_1 = require("./manifest");
78
84
  const maquina_1 = require("./maquina");
79
85
  const modos_1 = require("./modos");
80
86
  const projeto_alvo_1 = require("./projeto-alvo");
87
+ const rede_1 = require("./rede");
88
+ const rede_projetos_1 = require("./rede-projetos");
89
+ const rede_status_1 = require("./rede-status");
81
90
  const roadmap_reservas_1 = require("./roadmap-reservas");
82
91
  const roadmap_status_1 = require("./roadmap-status");
92
+ const vida_da_thread_1 = require("./vida-da-thread");
83
93
  const thread_1 = require("./thread");
84
94
  const versao_1 = require("./versao");
85
95
  const yaml_1 = require("./yaml");
@@ -118,9 +128,8 @@ const lacuna = (tipo, parte, alvo, detalhe, correcao) => ({ tipo, parte, ...(alv
118
128
  /**
119
129
  * Nome de remoto do git, como `origin`. O valor vem do manifesto versionado (`fabrica.remoto`), e um
120
130
  * valor que comece com `-` viraria opcao do `git fetch` (`--upload-pack=...`): so este formato chega
121
- * ao git (achado 4 do CHECK).
131
+ * ao git (achado 4 do CHECK). O validador e o mesmo da branch de estado (RM-047).
122
132
  */
123
- const REMOTO = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
124
133
  /**
125
134
  * Nome de branch base, como `main`. Tambem vem do manifesto (`worktree.base_branch`), e o `git log`
126
135
  * da base o recebia cru: `--output=<arquivo>` gravava um arquivo (rodada 2 do CHECK).
@@ -128,7 +137,7 @@ const REMOTO = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
128
137
  const baseValida = (base) => typeof base === 'string' && /^[A-Za-z0-9][A-Za-z0-9._/-]{0,99}$/.test(base) && !base.includes('..');
129
138
  function projetoDoClone(c, origem) {
130
139
  const remoto = c.manifesto.fabrica.remoto;
131
- const url = REMOTO.test(remoto) ? (0, branch_de_estado_1.git)(c.raiz, ['remote', 'get-url', remoto]) : null;
140
+ const url = (0, branch_de_estado_1.remotoValido)(remoto) ? (0, branch_de_estado_1.git)(c.raiz, ['remote', 'get-url', '--', remoto]) : null;
132
141
  return { nome: c.manifesto.project.name, forja: url?.ok ? (0, forja_1.identidadeDaForja)(url.stdout.trim()) : null, raiz: c.raiz,
133
142
  base: c.manifesto.worktree.base_branch, remoto, origem };
134
143
  }
@@ -400,12 +409,14 @@ function retratoLegivel(v) {
400
409
  textoOuNulo(t.roadmap) && textoOuNulo(t.pergunta) && textoOuNulo(t.entregue) && textoOuNulo(t.paradaDesde) &&
401
410
  textoOuNulo(t.runtime) && textoOuNulo(t.modelo) && textoOuNulo(t.esforco));
402
411
  }
403
- function fatosDoRetrato(m, entregues, itemDaReserva) {
412
+ function fatosDoRetrato(m, entregues, itemDaReserva, quando) {
404
413
  return m.threads.map((t) => ({
405
414
  id: t.id, roadmap: t.roadmap ?? itemDaReserva.get(t.id) ?? null, aberta: t.status !== 'fechada', fase: t.fase,
406
415
  entregueHoje: () => entregues.has(t.id),
407
416
  espera: () => t.esperaVoce ? { thread: t.id, pergunta: pontuar(t.pergunta ?? 'o veredito da pausa'), maquina: m.maquina } : undefined,
408
417
  maquina: m.maquina,
418
+ // RM-063 (F2): a sessao que a outra maquina verificou ao publicar; retrato sem o campo, ou velho, nao diz "sigo".
419
+ vida: () => (0, vida_da_thread_1.vidaDoRetrato)(t.sessao, m.publicadoEm, quando),
409
420
  }));
410
421
  }
411
422
  function fatosDaMaquinaLocal(raiz, quando, fuso, maquina, entregues, itemDaReserva) {
@@ -429,12 +440,12 @@ function maquinasDoPanorama(retratos, local, ctx, lacunas) {
429
440
  }
430
441
  saida.push({ maquina: m.maquina, por: m.por, publicadoEm: m.publicadoEm, idadeMin: Math.floor(idadeMs / 60000), semBatida,
431
442
  estaMaquina: m.maquina === ctx.maquina, origem: 'retrato', versaoOrk: m.versaoOrk ?? null,
432
- ativas: m.threads.filter((t) => !t.entregue), entreguesSemMaster: m.threads.filter((t) => t.entregue).length });
443
+ ativas: m.threads.filter((t) => !t.entregue), entreguesSemMaster: m.threads.filter((t) => t.entregue).length, threadsLidas: true });
433
444
  }
434
445
  if (local) {
435
446
  saida.push({ maquina: local.maquina, por: local.por, publicadoEm: local.publicadoEm, idadeMin: 0, semBatida: false, estaMaquina: true,
436
447
  origem: 'estado-local', versaoOrk: local.versaoOrk, ativas: local.threads.filter((t) => !t.entregue),
437
- entreguesSemMaster: local.threads.filter((t) => t.entregue).length });
448
+ entreguesSemMaster: local.threads.filter((t) => t.entregue).length, threadsLidas: true });
438
449
  }
439
450
  return saida.sort((a, b) => a.maquina.localeCompare(b.maquina));
440
451
  }
@@ -450,7 +461,7 @@ function montarProjeto(p, d, ctx, fontes, lacunas) {
450
461
  fatosDaMaquinaLocal(d.local.raiz, ctx.quando, ctx.fuso, d.local.retrato.maquina, entregues, itemDaReserva).forEach(somar);
451
462
  for (const m of d.retratos ?? [])
452
463
  if (!d.local || m.maquina !== d.local.retrato.maquina)
453
- fatosDoRetrato(m, entregues, itemDaReserva).forEach(somar);
464
+ fatosDoRetrato(m, entregues, itemDaReserva, ctx.quando).forEach(somar);
454
465
  // Entregue hoje e ja fechada: nao esta em retrato nenhum, mas o merge na base e o fato do dia.
455
466
  for (const id of entregues)
456
467
  somar({ id, roadmap: itemDaReserva.get(id) ?? null, aberta: false, fase: 'SHIP', entregueHoje: () => true,
@@ -571,8 +582,8 @@ function lerProjetoDoClone(p, geral) {
571
582
  const carregado = (0, manifest_1.carregarManifesto)(raiz);
572
583
  // O fuso do dono deste projeto vale para o dia e os horarios dele (achado 5 do CHECK).
573
584
  const ctx = { ...geral, fuso: carregado?.manifesto.owner?.timezone ?? geral.fuso };
574
- if (!REMOTO.test(remoto)) {
575
- lacunas.push(lacuna('projeto.remoto-invalido', 'projeto', raizParaExibir(raiz), `fabrica.remoto do manifesto (${JSON.stringify(remoto).slice(0, 60)}) não é nome de remoto do git: nada foi lido pelo git`, 'corrija fabrica.remoto no orkastery.yaml (padrão: origin)'));
585
+ if (!(0, branch_de_estado_1.remotoValido)(remoto)) {
586
+ lacunas.push(lacuna('projeto.remoto-invalido', 'projeto', raizParaExibir(raiz), `fabrica.remoto do manifesto (${(0, branch_de_estado_1.remotoRedigido)(remoto)}) não é nome de remoto do git: nada foi lido pelo git`, 'corrija fabrica.remoto no orkastery.yaml (padrão: origin)'));
576
587
  return montarProjeto(p, { nome: p.nome, docs: null, reservas: null, retratos: null, local: null, commits: [] }, ctx, fontes, lacunas);
577
588
  }
578
589
  if (!baseValida(p.base ?? 'main') || (carregado && !baseValida(carregado.manifesto.worktree.base_branch))) {
@@ -784,6 +795,134 @@ function lerProjeto(p, ctx) {
784
795
  return vazio(lacuna('projeto.sem-leitura', 'projeto', p.raiz ? raizParaExibir(p.raiz) : p.nome, `a leitura de ${p.nome} parou: ${(0, forja_1.detalheSeguro)(e?.message ?? String(e))}`, 'confira o clone e o estado dele no próprio projeto; os outros projetos seguiram'));
785
796
  }
786
797
  }
798
+ // ---------------------------------------------------------------------------
799
+ // A rede por pessoa (fatia 3).
800
+ // ---------------------------------------------------------------------------
801
+ /** As lacunas do `ork.rede-status/v1` que sao da casa (forja, casa, retrato), e nao da fabrica de um projeto. */
802
+ const lacunaDaCasa = (tipo) => tipo.startsWith('forja.') || tipo.startsWith('rede.') || tipo === 'retrato.invalido' || tipo === 'maquina.nome-em-uso';
803
+ const CORRECAO_DA_CASA = {
804
+ 'forja.ausente': 'instale o gh (ou o glab) nesta máquina',
805
+ 'forja.sem-login': 'gh auth login (ou glab auth login) nesta máquina',
806
+ 'rede.sem-repositorio': 'ork network entrar numa máquina da pessoa',
807
+ 'maquina.nome-em-uso': 'ork network entrar --maquina NOME ou --forcar',
808
+ };
809
+ /** A rede lida para o panorama, ou o motivo de nao ter lido (vai ao `naoConsultado`). */
810
+ function lerRedeDoPanorama(opcoes, ctx, cwd) {
811
+ if (opcoes.rede === false)
812
+ return { status: null, motivo: 'não lida nesta chamada' };
813
+ if (opcoes.rede)
814
+ return { status: opcoes.rede, motivo: null };
815
+ if ((process.env.ORK_REDE_LER ?? '').trim() === '0')
816
+ return { status: null, motivo: 'leitura desligada (ORK_REDE_LER=0)' };
817
+ try {
818
+ // D-G6 da fatia 2: no host, o diretorio do gateway nao e projeto; a rede le so o registro e os retratos.
819
+ return { status: (0, rede_status_1.lerRede)({ semRemoto: ctx.semRemoto, maquina: opcoes.maquina, agora: ctx.quando, diretorio: opcoes.host ? null : cwd }),
820
+ motivo: null };
821
+ }
822
+ catch (e) {
823
+ return { status: null, motivo: `a leitura parou: ${(0, forja_1.detalheSeguro)(e?.message ?? String(e))}` };
824
+ }
825
+ }
826
+ /** O projeto que um retrato declara casa com o projeto do panorama: pela forja, ou pelo nome quando falta a forja. */
827
+ function declara(q, p, nome) {
828
+ const forja = q.remoto ? (0, forja_1.identidadeDaForja)(q.remoto) : null;
829
+ if (forja && p.forja)
830
+ return (0, forja_1.mesmaForja)(forja, p.forja);
831
+ return (0, rede_projetos_1.emUmaLinha)(q.nome) === nome;
832
+ }
833
+ /** Os projetos que as maquinas da rede declaram com remoto de forja e que esta maquina nao conhece. */
834
+ function projetosDaRede(status, conhecidos) {
835
+ const saida = [];
836
+ for (const m of status.membros) {
837
+ if (m.origem !== 'rede')
838
+ continue;
839
+ for (const q of m.projetos) {
840
+ const nome = (0, rede_projetos_1.emUmaLinha)(q.nome);
841
+ const forja = q.remoto ? (0, forja_1.identidadeDaForja)(q.remoto) : null;
842
+ if (!forja || !projeto_alvo_1.PADRAO_DO_NOME_DE_PROJETO.test(nome))
843
+ continue;
844
+ const p = { nome, forja, raiz: null, base: null, remoto: 'origin', origem: 'rede' };
845
+ if (![...conhecidos, ...saida].some((x) => mesmoProjeto(x, p)))
846
+ saida.push(p);
847
+ }
848
+ }
849
+ return saida;
850
+ }
851
+ /**
852
+ * Suspeitas da revisao de 03/10 (mesma raiz do defeito 3 da revisao): o retrato com o nome desta
853
+ * maquina e de outra instalacao quando o status diz `nomeEmUso`; entao nenhum retrato da rede e "esta
854
+ * maquina". `null` nesse caso; senao, o nome saneado desta maquina.
855
+ */
856
+ function euNaRede(status, ctx) {
857
+ const eu = (0, rede_1.nomeSeguro)(ctx.maquina);
858
+ return status.estaMaquina.maquina === eu && status.estaMaquina.nomeEmUso ? null : eu;
859
+ }
860
+ /** A rede no panorama; no MCP fixado, os projetos de cada maquina ficam so o servido (D5 da RM-052). */
861
+ function redeNoPanorama(status, ctx, servido) {
862
+ const agora = Date.parse(ctx.quando);
863
+ const fonte = status.fontes.find((f) => f.fonte === 'rede');
864
+ const eu = euNaRede(status, ctx);
865
+ const membros = status.membros.filter((m) => m.origem === 'rede').map((m) => {
866
+ const idadeMs = agora - Date.parse(m.publicadoEm);
867
+ const idadeMin = Number.isFinite(idadeMs) ? Math.floor(Math.max(0, idadeMs) / 60000) : null;
868
+ const projetos = m.projetos.filter((q) => !servido || declara(q, servido.p, servido.nome)).map((q) => (0, rede_projetos_1.emUmaLinha)(q.nome));
869
+ return { maquina: (0, rede_projetos_1.emUmaLinha)(m.maquina), estaMaquina: m.maquina === eu, publicadoEm: m.publicadoEm, idadeMin,
870
+ semBatida: idadeMin === null || idadeMin * 60000 > fabrica_estado_1.LIMIAR_SEM_BATIDA_MS, versaoOrk: m.versaoOrk ? (0, rede_projetos_1.emUmaLinha)(m.versaoOrk) : null,
871
+ projetos: [...new Set(projetos)] };
872
+ });
873
+ return { casa: status.casa ? (0, rede_1.refDaCasa)(status.casa) : null, ponta: fonte?.ponta ?? null, atualizado: fonte ? fonte.atualizado : null,
874
+ lidoEm: status.consultadoEm, membros };
875
+ }
876
+ /**
877
+ * A maquina da rede que declara o projeto e nao publicou na fabrica dele entra na lista com as
878
+ * threads NAO lidas e uma lacuna: antes ela sumia, e a lista parecia completa.
879
+ */
880
+ function completarComARede(x, p, status, ctx) {
881
+ if (x.maquinas === null)
882
+ return;
883
+ const agora = Date.parse(ctx.quando);
884
+ for (const m of status.membros) {
885
+ if (m.origem !== 'rede' || !m.projetos.some((q) => declara(q, p, x.projeto.nome)))
886
+ continue;
887
+ if (x.maquinas.some((y) => (0, rede_1.nomeSeguro)(y.maquina) === m.maquina))
888
+ continue;
889
+ const maquina = (0, rede_projetos_1.emUmaLinha)(m.maquina);
890
+ const idadeMs = agora - Date.parse(m.publicadoEm);
891
+ const legivel = Number.isFinite(idadeMs);
892
+ const idadeMin = legivel ? Math.floor(Math.max(0, idadeMs) / 60000) : 0;
893
+ const semBatida = !legivel || idadeMin * 60000 > fabrica_estado_1.LIMIAR_SEM_BATIDA_MS;
894
+ x.maquinas.push({ maquina, por: 'rede por pessoa', publicadoEm: m.publicadoEm, idadeMin, semBatida, estaMaquina: m.maquina === euNaRede(status, ctx),
895
+ origem: 'rede', versaoOrk: m.versaoOrk ? (0, rede_projetos_1.emUmaLinha)(m.versaoOrk) : null, ativas: [], entreguesSemMaster: 0, threadsLidas: false });
896
+ x.lacunas.push(lacuna('maquina.sem-fabrica', 'fabrica', maquina, `${maquina} está na rede por pessoa e declara ${x.projeto.nome}, mas não tem retrato em ${fabrica_estado_1.BRANCH_DA_FABRICA}: as threads dela neste projeto não foram lidas`, `na ${maquina}, rode ork fabrica entrar no clone de ${x.projeto.nome} (ou atualize o ork dela)`));
897
+ if (semBatida) {
898
+ x.lacunas.push(lacuna('maquina.sem-batida', 'rede', maquina, `${maquina} sem retrato novo na rede ${legivel
899
+ ? `há ${(0, horario_1.duracaoCurta)(idadeMin)}` : '(batida ilegível)'}`, `confira a ${maquina}: ela publica na batida do pulse`));
900
+ }
901
+ }
902
+ x.maquinas.sort((a, b) => a.maquina.localeCompare(b.maquina));
903
+ }
904
+ /**
905
+ * Fatia 3: o `ork network status` do servidor MCP fixado. A rede e da pessoa, mas o retrato de cada
906
+ * maquina lista todos os projetos dela: aqui fica so o projeto servido (D5 da RM-052), e o resto vai
907
+ * ao "nao consultado". Com `ORK_REDE_LER=0`, a casa e lida so da ultima copia desta maquina.
908
+ */
909
+ function redeDoProjetoFixado(raiz, opcoes = {}) {
910
+ const c = (0, manifest_1.carregarManifesto)(raiz);
911
+ if (!c) {
912
+ throw new ErroDoPedidoDeProjeto('projeto.sem-manifesto', `a raiz fixada não tem ${manifest_1.NOME_MANIFESTO}`, [], 'confira a raiz fixada na instalação do servidor MCP (ork mcp install --project <raiz>)');
913
+ }
914
+ const p = projetoDoClone(c, 'instalacao');
915
+ const s = opcoes.status ?? (0, rede_status_1.lerRede)({ semRemoto: opcoes.semRemoto === true || (process.env.ORK_REDE_LER ?? '').trim() === '0', diretorio: c.raiz,
916
+ soRaiz: c.raiz });
917
+ const membros = s.membros.map((m) => ({ ...m, projetos: m.projetos.filter((q) => declara(q, p, p.nome)) }))
918
+ .filter((m) => m.origem === 'rede' || m.projetos.length > 0);
919
+ // Revisao de 03/10 (D5): a lacuna de uma maquina que saiu dos membros (vista so em outro projeto) sai junto.
920
+ const nomes = new Set(membros.map((m) => m.maquina));
921
+ return { ...s, membros, fontes: s.fontes.filter((f) => !f.projeto || f.projeto === p.nome),
922
+ lacunas: s.lacunas.filter((l) => (!l.projeto || l.projeto === p.nome) &&
923
+ (!l.maquina || l.tipo === 'maquina.nome-em-uso' || nomes.has(l.maquina))),
924
+ naoConsultado: [...s.naoConsultado, `outros projetos das máquinas (este servidor MCP mostra só ${p.nome}; os outros vêm do CLI ork network status)`] };
925
+ }
787
926
  /**
788
927
  * `ork network roadmap`: os projetos pedidos (ou todos os conhecidos), cada um com roadmap, reservas,
789
928
  * threads por maquina, fontes e lacunas. Recusa o `--projeto` ambiguo ou desconhecido.
@@ -793,11 +932,21 @@ function montarPanoramaDaRede(opcoes = {}) {
793
932
  const cwd = opcoes.cwd ?? process.cwd();
794
933
  const ctx = { quando, maquina: (0, maquina_1.nomeDaMaquina)(opcoes.maquina), semRemoto: opcoes.semRemoto === true, executor: opcoes.executor,
795
934
  fuso: (0, horario_1.fusoDoDono)().fuso };
796
- const naoConsultado = [
797
- 'rede por pessoa (RM-053, ork.rede-status/v1): não lida nesta versão; as máquinas vêm da branch ork/fabrica-estado de cada projeto',
798
- ];
935
+ const naoConsultado = [];
799
936
  const lacunas = [];
937
+ const lida = lerRedeDoPanorama(opcoes, ctx, cwd);
938
+ const status = lida.status;
939
+ if (!status)
940
+ naoConsultado.push(`rede por pessoa (RM-053, ork.rede-status/v1): ${lida.motivo}; as máquinas vêm da branch ${fabrica_estado_1.BRANCH_DA_FABRICA} de cada projeto`);
941
+ else {
942
+ for (const l of status.lacunas) {
943
+ if (!lacunaDaCasa(l.tipo) || l.projeto)
944
+ continue;
945
+ lacunas.push(lacuna(l.tipo, 'rede', l.maquina, (0, forja_1.detalheSeguro)((0, rede_projetos_1.emUmaLinha)(l.detalhe)), CORRECAO_DA_CASA[l.tipo] ?? 'confira com ork network status'));
946
+ }
947
+ }
800
948
  let alvos;
949
+ let servido = null;
801
950
  if (opcoes.fixado !== undefined) {
802
951
  // D-G5 (fatia 2): o servidor MCP le so o projeto dele, em todas as maquinas; o registro nao e lido.
803
952
  const c = (0, manifest_1.carregarManifesto)(opcoes.fixado);
@@ -805,6 +954,7 @@ function montarPanoramaDaRede(opcoes = {}) {
805
954
  throw new ErroDoPedidoDeProjeto('projeto.sem-manifesto', `a raiz fixada não tem ${manifest_1.NOME_MANIFESTO}`, [], 'confira a raiz fixada na instalação do servidor MCP (ork mcp install --project <raiz>)');
806
955
  }
807
956
  alvos = [projetoDoClone(c, 'instalacao')];
957
+ servido = { p: alvos[0], nome: alvos[0].nome };
808
958
  naoConsultado.push('outros projetos desta máquina: este servidor MCP lê só o projeto fixado na instalação; ' +
809
959
  'os outros vêm do CLI `ork network roadmap`');
810
960
  }
@@ -812,17 +962,19 @@ function montarPanoramaDaRede(opcoes = {}) {
812
962
  const conhecidos = projetosConhecidos({ cwd, registro: opcoes.registro, host: opcoes.host });
813
963
  naoConsultado.push(...conhecidos.naoConsultado);
814
964
  lacunas.push(...conhecidos.lacunas);
965
+ // Fatia 3: o projeto que uma maquina da rede declara passa a ser conhecido, lido pela forja.
966
+ const todos = [...conhecidos.projetos, ...(status ? projetosDaRede(status, conhecidos.projetos) : [])];
815
967
  if (opcoes.pedido !== undefined) {
816
968
  if (opcoes.host)
817
- exigirPedidoDoHost(opcoes.pedido, conhecidos.projetos);
818
- const alvo = resolverProjeto(opcoes.pedido, conhecidos.projetos, cwd);
969
+ exigirPedidoDoHost(opcoes.pedido, todos);
970
+ const alvo = resolverProjeto(opcoes.pedido, todos, cwd);
819
971
  alvos = [alvo];
820
- for (const p of conhecidos.projetos)
972
+ for (const p of todos)
821
973
  if (!mesmoProjeto(p, alvo))
822
974
  naoConsultado.push(`projeto ${p.nome}: fora do pedido (--projeto)`);
823
975
  }
824
976
  else {
825
- alvos = conhecidos.projetos;
977
+ alvos = todos;
826
978
  }
827
979
  }
828
980
  if (alvos.length === 0) {
@@ -831,9 +983,15 @@ function montarPanoramaDaRede(opcoes = {}) {
831
983
  : 'nenhum projeto conhecido nesta máquina: sem manifesto no diretório atual e sem registro', opcoes.host ? 'peça o projeto pela forja (--projeto github:dono/repo) ou registre o clone com `ork projetos registrar <caminho>`'
832
984
  : 'peça o projeto: --projeto <caminho do clone> ou --projeto github:dono/repo'));
833
985
  }
834
- const projetos = alvos.map((p) => lerProjeto(p, ctx));
986
+ const projetos = alvos.map((p) => {
987
+ const x = lerProjeto(p, ctx);
988
+ if (status)
989
+ completarComARede(x, p, status, ctx);
990
+ return x;
991
+ });
835
992
  return { contrato: exports.CONTRATO_PANORAMA_DA_REDE, consultadoEm: quando, maquina: ctx.maquina, pedido: opcoes.pedido ?? null,
836
- limiarSemBatidaMin: fabrica_estado_1.LIMIAR_SEM_BATIDA_MS / 60000, fuso: projetos[0]?.projeto.fuso ?? ctx.fuso, projetos, naoConsultado, lacunas };
993
+ limiarSemBatidaMin: fabrica_estado_1.LIMIAR_SEM_BATIDA_MS / 60000, fuso: projetos[0]?.projeto.fuso ?? ctx.fuso, projetos,
994
+ rede: status ? redeNoPanorama(status, ctx, servido) : null, naoConsultado, lacunas };
837
995
  }
838
996
  // ---------------------------------------------------------------------------
839
997
  // O texto.
@@ -859,6 +1017,11 @@ function linhasDasMaquinas(x, fuso) {
859
1017
  return ['• nenhuma máquina publicou em ork/fabrica-estado'];
860
1018
  return x.maquinas.flatMap((m) => {
861
1019
  const quem = `${m.maquina}${m.estaMaquina ? ' (esta máquina)' : ''}`;
1020
+ // Fatia 3: a maquina so da rede por pessoa; as threads dela neste projeto nao foram lidas.
1021
+ if (m.origem === 'rede') {
1022
+ return [`• ${quem}: threads não lidas, na rede por pessoa (retrato de ${(0, horario_1.formatarDataHora)(m.publicadoEm, { fuso })}, ` +
1023
+ `há ${(0, horario_1.duracaoCurta)(m.idadeMin)}${m.semBatida ? ', SEM BATIDA' : ''}) e sem retrato em ${fabrica_estado_1.BRANCH_DA_FABRICA}`];
1024
+ }
862
1025
  const batida = m.origem === 'estado-local' ? 'estado local lido agora'
863
1026
  : `retrato de ${(0, horario_1.formatarDataHora)(m.publicadoEm, { fuso })} (há ${(0, horario_1.duracaoCurta)(m.idadeMin)})${m.semBatida ? ', SEM BATIDA' : ''}`;
864
1027
  const cabeca = `• ${quem}: ${m.ativas.length} ativa(s)${m.entreguesSemMaster ? `, ${m.entreguesSemMaster} entregue(s) sem MASTER` : ''}, ${batida}`;
@@ -875,6 +1038,19 @@ function linhasDasReservas(x, fuso) {
875
1038
  return ['• nenhuma reserva em ork/roadmap-reservas'];
876
1039
  return x.reservas.map((r) => ` ${r.item} · ${r.maquina} · ${r.thread ?? 'sem thread'} · desde ${(0, horario_1.formatarDataHora)(r.desdeEm, { fuso })}`);
877
1040
  }
1041
+ /** Fatia 3: a casa da rede por pessoa e as maquinas dela, com a batida e os projetos que declaram. */
1042
+ function linhasDaRede(r, quando, fuso) {
1043
+ const lido = r.lidoEm === quando ? 'lida agora' : `lida ${(0, horario_1.formatarDataHora)(r.lidoEm, { fuso })}`;
1044
+ const casa = !r.casa ? '• casa: nenhuma achada (veja as lacunas)'
1045
+ : r.atualizado === null ? `• casa: ${r.casa}, não lida (veja as lacunas)`
1046
+ : `• casa: ${r.casa} @ ${r.ponta ? r.ponta.slice(0, 7) : 'sem commit'}, ${r.atualizado ? lido : 'última cópia desta máquina, sem leitura nova'}`;
1047
+ if (r.membros.length === 0)
1048
+ return [casa, '• nenhum retrato lido na casa; isso não quer dizer que não há máquinas: veja as lacunas'];
1049
+ return [casa, ...r.membros.map((m) => `• ${m.maquina}${m.estaMaquina ? ' (esta máquina)' : ''}: retrato de ` +
1050
+ `${(0, horario_1.formatarDataHora)(m.publicadoEm, { fuso })}${m.idadeMin === null ? ' (batida ilegível)' : ` (há ${(0, horario_1.duracaoCurta)(m.idadeMin)})`}` +
1051
+ `${m.semBatida ? ', SEM BATIDA' : ''}${m.versaoOrk ? `, ork ${m.versaoOrk}` : ''}, ` +
1052
+ (m.projetos.length ? `projetos: ${m.projetos.join(', ')}` : 'nenhum projeto declarado aqui'))];
1053
+ }
878
1054
  const linhaDaLacuna = (l) => `• ${l.tipo}${l.alvo ? ` (${l.alvo})` : ''}: ${l.detalhe}. O que fazer: ${l.correcao}.`;
879
1055
  /** O texto de `ork network roadmap`: o consultado e o nao consultado no alto, e cada parte com a fonte e a hora. */
880
1056
  function textoDoPanoramaDaRede(p) {
@@ -886,6 +1062,8 @@ function textoDoPanoramaDaRede(p) {
886
1062
  p.projetos.length ? `Consultado: ${p.projetos.map((x) => descreverProjeto(x.projeto)).join('; ')}` : 'Consultado: nenhum projeto.',
887
1063
  'Não consultado:', ...(p.naoConsultado.length ? p.naoConsultado.map((n) => `• ${n}`) : ['• nada fora do pedido']),
888
1064
  ];
1065
+ if (p.rede)
1066
+ linhas.push('', 'Rede por pessoa (RM-053)', ...linhasDaRede(p.rede, p.consultadoEm, p.fuso));
889
1067
  if (p.lacunas.length)
890
1068
  linhas.push('', 'Lacunas da consulta', ...p.lacunas.map(linhaDaLacuna));
891
1069
  for (const x of p.projetos) {