@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
@@ -0,0 +1,388 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.PRAZO_DA_PRESENCA_MS = exports.CONTRATO_DA_SENTINELA = exports.CANAL_DA_SENTINELA = void 0;
37
+ exports.caminhoDaPresenca = caminhoDaPresenca;
38
+ exports.presencaDaSentinela = presencaDaSentinela;
39
+ exports.baterSentinela = baterSentinela;
40
+ exports.desligarSentinela = desligarSentinela;
41
+ exports.faseJaFeita = faseJaFeita;
42
+ exports.bloqueioDepoisDoDespacho = bloqueioDepoisDoDespacho;
43
+ exports.passoDaThread = passoDaThread;
44
+ exports.passosDoCondutor = passosDoCondutor;
45
+ exports.statusDaSentinela = statusDaSentinela;
46
+ exports.textoDoStatusDaSentinela = textoDoStatusDaSentinela;
47
+ /**
48
+ * RM-059 (fatia F6, T6.4; DD5 do plano dos mods): a conducao pela Sentinela do Condutor.
49
+ *
50
+ * A Sentinela (`adapters/claude-code/mods/ork-sentinela`) roda na sessao do dono no Claude Code e despacha a fase
51
+ * seguinte das threads #Auto pelo proprio nucleo (`ork phase run`, que toma o lease `exec:<thread>` e e idempotente).
52
+ * Dois fatos moram aqui, e so aqui:
53
+ *
54
+ * - a PRESENCA dela nesta maquina, com prazo curto renovado pela batida: enquanto ela vale, o condutor externo cede
55
+ * o despacho e o retry; vencida (a sessao do dono fechou), ele volta a conduzir sozinho, como reserva;
56
+ * - o PASSO DO CONDUTOR de cada thread aberta, calculado com as mesmas leituras do pulse (`entregasDoProjeto`,
57
+ * `esperaDoCondutor`), sem o limiar de 30 min e sem ler a forja. No #Auto, depois de uma fase concluida, sai a
58
+ * fase seguinte a prova mais adiantada que a sessao do bloco deixou (artefato gravado depois do despacho, produto
59
+ * na branch, `ship_done`, `master_done`), e nada sai quando o bloco ja passou do CHECK: a sessao que fez o bloco
60
+ * inteiro nao ganha um PLAN repetido. A pendencia do dono (thread pausada, pergunta aberta) segura tudo.
61
+ *
62
+ * Nada aqui despacha nem toma conducao: quem despacha e o `ork phase run`, e a Sentinela nunca assume a conducao.
63
+ */
64
+ const fs = __importStar(require("node:fs"));
65
+ const path = __importStar(require("node:path"));
66
+ const node_crypto_1 = require("node:crypto");
67
+ const conducao_1 = require("./conducao");
68
+ const estado_thread_1 = require("./estado-thread");
69
+ const ledger_1 = require("./ledger");
70
+ const manifest_1 = require("./manifest");
71
+ const maquina_1 = require("./maquina");
72
+ const parado_no_condutor_1 = require("./parado-no-condutor");
73
+ const thread_1 = require("./thread");
74
+ var conducao_2 = require("./conducao");
75
+ Object.defineProperty(exports, "CANAL_DA_SENTINELA", { enumerable: true, get: function () { return conducao_2.CANAL_DA_SENTINELA; } });
76
+ Object.defineProperty(exports, "CONTRATO_DA_SENTINELA", { enumerable: true, get: function () { return conducao_2.CONTRATO_DA_SENTINELA; } });
77
+ Object.defineProperty(exports, "PRAZO_DA_PRESENCA_MS", { enumerable: true, get: function () { return conducao_2.PRAZO_DA_PRESENCA_MS; } });
78
+ /** O id da sessao do Claude Code que conduz pela Sentinela (o UUID dela, ou outro id curto sem espaco). */
79
+ const SESSAO_VALIDA = /^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,127}$/;
80
+ /** Uma trava de presenca mais velha que isto e de um processo que morreu no meio da escrita. */
81
+ const TRAVA_VELHA_MS = 10 * 1000;
82
+ const ESPERA_DA_TRAVA_MS = 3 * 1000;
83
+ /** O nome da maquina como pedaco de nome de arquivo. */
84
+ function nomeSeguro(maquina) {
85
+ return maquina.replace(/[^a-zA-Z0-9._-]/g, '-').replace(/^[.-]+/, '').slice(0, 64) || 'maquina';
86
+ }
87
+ function exigirSessao(sessao) {
88
+ if (typeof sessao !== 'string' || !SESSAO_VALIDA.test(sessao)) {
89
+ throw new Error('conducao.sentinela.sessao: --sessao exige o id da sessao do Claude Code (letras, digitos, ponto, hifen ou dois-pontos, ate 128)');
90
+ }
91
+ return sessao;
92
+ }
93
+ /** O ledger do projeto e o da raiz do estado, chamado da raiz ou de qualquer worktree (como o do setup). */
94
+ function dirDoLedgerDoProjeto(raiz) {
95
+ return (0, manifest_1.dirEstado)((0, estado_thread_1.raizDoEstado)(raiz));
96
+ }
97
+ /** O arquivo da presenca desta maquina, no estado canonico do projeto. */
98
+ function caminhoDaPresenca(raiz, maquina = (0, maquina_1.nomeDaMaquina)()) {
99
+ return path.join(dirDoLedgerDoProjeto(raiz), 'conducao', `sentinela-${nomeSeguro(maquina)}.json`);
100
+ }
101
+ function lerPresencaGravada(arquivo) {
102
+ let bruto;
103
+ try {
104
+ bruto = JSON.parse(fs.readFileSync(arquivo, 'utf8'));
105
+ }
106
+ catch {
107
+ return null;
108
+ }
109
+ const p = bruto;
110
+ if (!p || p.contrato !== conducao_1.CONTRATO_DA_SENTINELA || p.canal !== conducao_1.CANAL_DA_SENTINELA)
111
+ return null;
112
+ for (const campo of ['maquina', 'sessao', 'desde', 'batidaEm', 'expiraEm']) {
113
+ if (typeof p[campo] !== 'string' || p[campo] === '')
114
+ return null;
115
+ }
116
+ if (!Number.isFinite(Date.parse(p.expiraEm)))
117
+ return null;
118
+ return p;
119
+ }
120
+ function comoLeitura(p, agora) {
121
+ return { ...p, ativa: Date.parse(p.expiraEm) > agora.getTime() };
122
+ }
123
+ function dormirSincrono(ms) {
124
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
125
+ }
126
+ /** Uma escrita da presenca por vez: a trava e um arquivo criado com O_EXCL ao lado dela. */
127
+ function comTrava(arquivo, fn) {
128
+ fs.mkdirSync(path.dirname(arquivo), { recursive: true });
129
+ const trava = `${arquivo}.trava`;
130
+ const limite = Date.now() + ESPERA_DA_TRAVA_MS;
131
+ for (;;) {
132
+ try {
133
+ fs.closeSync(fs.openSync(trava, 'wx'));
134
+ break;
135
+ }
136
+ catch (e) {
137
+ if (e.code !== 'EEXIST')
138
+ throw e;
139
+ const st = fs.statSync(trava, { throwIfNoEntry: false });
140
+ if (st && Date.now() - st.mtimeMs > TRAVA_VELHA_MS) {
141
+ // So a mesma trava velha sai: se outra sessao a trocou no meio (outro inode ou outro mtime), ela fica.
142
+ const deNovo = fs.statSync(trava, { throwIfNoEntry: false });
143
+ if (deNovo && deNovo.ino === st.ino && deNovo.mtimeMs === st.mtimeMs)
144
+ fs.rmSync(trava, { force: true });
145
+ continue;
146
+ }
147
+ if (Date.now() > limite)
148
+ throw new Error('conducao.sentinela.trava: outra escrita da presenca nao terminou; tente de novo');
149
+ dormirSincrono(25);
150
+ }
151
+ }
152
+ try {
153
+ return fn();
154
+ }
155
+ finally {
156
+ fs.rmSync(trava, { force: true });
157
+ }
158
+ }
159
+ function gravarPresenca(arquivo, p) {
160
+ const temporario = `${arquivo}.${process.pid}.${(0, node_crypto_1.randomUUID)().slice(0, 8)}.tmp`;
161
+ fs.writeFileSync(temporario, JSON.stringify(p, null, 2) + '\n', { encoding: 'utf8', mode: 0o644 });
162
+ fs.renameSync(temporario, arquivo);
163
+ }
164
+ /** A presenca da Sentinela nesta maquina, viva ou vencida; `null` quando nao ha uma valida gravada. */
165
+ function presencaDaSentinela(raiz, opcoes = {}) {
166
+ const gravada = lerPresencaGravada(caminhoDaPresenca(raiz, opcoes.maquina));
167
+ return gravada ? comoLeitura(gravada, opcoes.agora ?? new Date()) : null;
168
+ }
169
+ /**
170
+ * A batida da Sentinela: renova a presenca da propria sessao, ou a toma quando nao ha outra viva. Com outra
171
+ * sessao viva nesta maquina, devolve `ok: false` e a presenca dela, sem tocar em nada: uma Sentinela por maquina.
172
+ * So a troca (presenca nova, inclusive depois de uma vencida) vai ao ledger do projeto.
173
+ */
174
+ function baterSentinela(raiz, opcoes) {
175
+ const sessao = exigirSessao(opcoes.sessao);
176
+ const agora = opcoes.agora ?? new Date();
177
+ const maquina = opcoes.maquina ?? (0, maquina_1.nomeDaMaquina)();
178
+ const arquivo = caminhoDaPresenca(raiz, maquina);
179
+ return comTrava(arquivo, () => {
180
+ const atual = lerPresencaGravada(arquivo);
181
+ const viva = !!atual && Date.parse(atual.expiraEm) > agora.getTime();
182
+ if (atual && viva && atual.sessao !== sessao)
183
+ return { ok: false, ligou: false, presenca: comoLeitura(atual, agora) };
184
+ const quando = agora.toISOString();
185
+ const nova = {
186
+ contrato: conducao_1.CONTRATO_DA_SENTINELA, canal: conducao_1.CANAL_DA_SENTINELA, maquina, sessao,
187
+ desde: atual && viva ? atual.desde : quando, batidaEm: quando,
188
+ expiraEm: new Date(agora.getTime() + conducao_1.PRAZO_DA_PRESENCA_MS).toISOString(),
189
+ };
190
+ gravarPresenca(arquivo, nova);
191
+ const ligou = !(atual && viva);
192
+ if (ligou) {
193
+ (0, ledger_1.registrar)(dirDoLedgerDoProjeto(raiz), 'projeto', ledger_1.TIPOS_DE_EVENTO.sentinelaLigada, {
194
+ canal: conducao_1.CANAL_DA_SENTINELA, sessao, maquina, prazoMs: conducao_1.PRAZO_DA_PRESENCA_MS,
195
+ ...(atual ? { anterior: { sessao: atual.sessao, expiraEm: atual.expiraEm } } : {}),
196
+ });
197
+ }
198
+ return { ok: true, ligou, presenca: comoLeitura(nova, agora) };
199
+ });
200
+ }
201
+ /** Tira a presenca da propria sessao; a de outra sessao fica como esta. */
202
+ function desligarSentinela(raiz, opcoes) {
203
+ const sessao = exigirSessao(opcoes.sessao);
204
+ const agora = opcoes.agora ?? new Date();
205
+ const maquina = opcoes.maquina ?? (0, maquina_1.nomeDaMaquina)();
206
+ const arquivo = caminhoDaPresenca(raiz, maquina);
207
+ return comTrava(arquivo, () => {
208
+ const atual = lerPresencaGravada(arquivo);
209
+ if (!atual || atual.sessao !== sessao)
210
+ return { desligada: false, presenca: atual ? comoLeitura(atual, agora) : null };
211
+ fs.rmSync(arquivo, { force: true });
212
+ (0, ledger_1.registrar)(dirDoLedgerDoProjeto(raiz), 'projeto', ledger_1.TIPOS_DE_EVENTO.sentinelaDesligada, {
213
+ canal: conducao_1.CANAL_DA_SENTINELA, sessao, maquina, motivo: (opcoes.motivo ?? 'pedido da sessao').slice(0, 200),
214
+ vivaAte: atual.expiraEm,
215
+ });
216
+ return { desligada: true, presenca: null };
217
+ });
218
+ }
219
+ /** As fases que, com produto ja na branch, a sessao do bloco ja fez (ha produto, entao houve GO). */
220
+ const FASES_ANTES_DO_PRODUTO = ['PLAN', 'GO'];
221
+ /** O artefato de cada fase que tem um, na pasta `docs/` da thread. */
222
+ const ARTEFATO_DA_FASE = { GOAL: 'goal.md', PLAN: 'plan.md', CHECK: 'check.md' };
223
+ /** O evento que prova a fase sem artefato. */
224
+ const EVENTO_DA_FASE = { SHIP: 'ship_done', MASTER: 'master_done' };
225
+ const BLOQUEIOS = [ledger_1.TIPOS_DE_EVENTO.gateBloqueado, ledger_1.TIPOS_DE_EVENTO.despachoFalhou];
226
+ /**
227
+ * Por que a sessao do ultimo despacho ja fez a fase, quando fez: no #Auto ela roda o bloco inteiro, e o
228
+ * artefato da fase gravado depois do despacho, o produto na branch (PLAN e GO) ou o evento da fase (SHIP e
229
+ * MASTER) depois dele provam que a fase seguinte ja aconteceu. `null`: nada prova, e a fase pode sair.
230
+ */
231
+ function faseJaFeita(raiz, t, fase, estado, eventos) {
232
+ const despacho = [...eventos].reverse().find((e) => e.tipo === ledger_1.TIPOS_DE_EVENTO.faseDespachada);
233
+ const desde = despacho ? Date.parse(despacho.ts) : Number.NEGATIVE_INFINITY;
234
+ const artefato = ARTEFATO_DA_FASE[fase];
235
+ if (artefato) {
236
+ const st = fs.statSync(path.join((0, thread_1.dirThread)(raiz, t.id), 'docs', artefato), { throwIfNoEntry: false });
237
+ if (st && st.mtimeMs >= desde)
238
+ return `o ${artefato} foi gravado depois do despacho`;
239
+ }
240
+ if (FASES_ANTES_DO_PRODUTO.includes(fase) && estado.comProduto)
241
+ return `a branch ja tem produto, e o ${fase} vem antes dele`;
242
+ const evento = EVENTO_DA_FASE[fase];
243
+ if (evento && eventos.some((e) => e.tipo === evento && Date.parse(e.ts) >= desde))
244
+ return `o ${evento} ja esta no ledger depois do despacho`;
245
+ return null;
246
+ }
247
+ /** O bloqueio que sobrou do ultimo despacho: o ultimo evento de gate depois dele, quando reprova. */
248
+ function bloqueioDepoisDoDespacho(eventos) {
249
+ let inicio = -1;
250
+ for (let i = eventos.length - 1; i >= 0; i--)
251
+ if (eventos[i].tipo === ledger_1.TIPOS_DE_EVENTO.faseDespachada) {
252
+ inicio = i;
253
+ break;
254
+ }
255
+ for (let i = eventos.length - 1; i > inicio; i--) {
256
+ const e = eventos[i];
257
+ if (e.tipo === ledger_1.TIPOS_DE_EVENTO.gateLiberado)
258
+ return null;
259
+ if (BLOQUEIOS.includes(e.tipo)) {
260
+ return { motivo: typeof e.motivo === 'string' && e.motivo ? e.motivo : e.tipo, em: e.ts,
261
+ deDespacho: e.tipo === ledger_1.TIPOS_DE_EVENTO.despachoFalhou || e.gate === 'phase.dispatch' };
262
+ }
263
+ }
264
+ return null;
265
+ }
266
+ /** As fases da entrega: depois de um bloco que ja passou do CHECK, quem publica e entrega e o condutor externo e o dono. */
267
+ const FASES_DA_ENTREGA = ['SHIP', 'MASTER'];
268
+ /**
269
+ * O passo de uma thread, pelas leituras que o pulse ja faz dela (`EstadoDaEntrega`). `jaFeita` diz por que a
270
+ * sessao do bloco ja fez uma fase (`faseJaFeita`), ou `null`; `doDono` e a pendencia do dono (`pendenciaDoDono`:
271
+ * thread pausada, pergunta aberta, escalacao), que segura qualquer passo automatico.
272
+ *
273
+ * No #Auto, depois de uma fase concluida, vale a prova mais adiantada que a sessao do bloco deixou: a fase seguinte a
274
+ * ela sai, e as do meio sem prova contam como feitas (nao ha CHECK sem GO). Se o bloco ja passou do CHECK, o passo e o
275
+ * da entrega (SHIP e MASTER ficam com o condutor externo e o dono) e vira aviso.
276
+ */
277
+ function passoDaThread(t, estado, eventos, pedidoGuardado, jaFeita = () => null, doDono = null) {
278
+ const espera = estado.espera;
279
+ const base = {
280
+ thread: t.id, modo: t.modo, faseAtual: t.faseAtual, conduzida: estado.conduzidaAgora, fase: null,
281
+ caso: estado.parado?.caso ?? null, proximoPasso: estado.parado?.proximoPasso ?? null,
282
+ desdeEm: estado.parado?.desdeEm ?? espera?.fimDoTurnoEm ?? null,
283
+ bloqueio: bloqueioDepoisDoDespacho(eventos), pedidoGuardado,
284
+ };
285
+ const avisarOuNada = (porque) => ({ ...base, acao: base.caso ? 'avisar' : 'nada', porque });
286
+ if (estado.conduzidaAgora)
287
+ return { ...base, acao: 'nada', porque: 'conduzida agora: quem conduz segue' };
288
+ if (doDono)
289
+ return avisarOuNada(`o passo e do dono (${doDono}): nada sai sozinho`);
290
+ // Como o cron: so o #Auto anda sem o dono. Nos outros modos o caso do nucleo vira aviso.
291
+ if (t.modo !== 'auto')
292
+ return avisarOuNada(`modo ${t.modo}: so o #Auto e conduzido sem o dono`);
293
+ // Um despacho tentado depois do fim do turno falhou (a sessao nem abriu): quem decide o proximo passo e o retry plan,
294
+ // com a ordem de fallback do runtime, e nao um despacho repetido.
295
+ if (espera && base.bloqueio?.deDespacho && Date.parse(base.bloqueio.em) > Date.parse(espera.fimDoTurnoEm)) {
296
+ return { ...base, acao: 'retomar', desdeEm: base.bloqueio.em,
297
+ porque: `o despacho depois do fim do turno falhou (${base.bloqueio.motivo}): o retry plan diz se a retomada e automatica` };
298
+ }
299
+ if (espera?.tipo === 'check-sem-veredito') {
300
+ return { ...base, acao: 'despachar', fase: 'CHECK', desdeEm: espera.fimDoTurnoEm,
301
+ porque: 'CHECK em done sem exatamente um veredito: o passo do nucleo e redespachar o CHECK' };
302
+ }
303
+ if (espera?.tipo === 'concluida' && espera.comProva !== false) {
304
+ const rodou = espera.fase;
305
+ const i = rodou ? t.fases.indexOf(rodou) : -1;
306
+ if (i < 0 || i + 1 >= t.fases.length)
307
+ return avisarOuNada(`fase ${rodou ?? '-'} concluida e nao ha fase depois dela no ciclo`);
308
+ // A prova mais adiantada que a sessao deixou depois da fase do resultado.
309
+ let maisAdiantada = -1;
310
+ let prova = null;
311
+ for (let k = i + 1; k < t.fases.length; k++) {
312
+ const feita = jaFeita(t.fases[k]);
313
+ if (feita) {
314
+ maisAdiantada = k;
315
+ prova = `${t.fases[k]}: ${feita}`;
316
+ }
317
+ }
318
+ const proxima = t.fases[maisAdiantada >= 0 ? maisAdiantada + 1 : i + 1] ?? null;
319
+ if (!proxima)
320
+ return avisarOuNada(`fase ${rodou} concluida, e a sessao do bloco ja fez ate o fim do ciclo (${prova})`);
321
+ if (maisAdiantada >= 0 && FASES_DA_ENTREGA.includes(proxima)) {
322
+ return avisarOuNada(`fase ${rodou} concluida, e a sessao do bloco ja passou do CHECK (${prova}): o passo e o da entrega`);
323
+ }
324
+ return { ...base, acao: 'despachar', fase: proxima, desdeEm: espera.fimDoTurnoEm,
325
+ porque: prova ? `fase ${rodou} concluida, e a sessao do bloco ja fez ate ${prova}: a seguinte e ${proxima}`
326
+ : `fase ${rodou} concluida: a seguinte e ${proxima}` };
327
+ }
328
+ if (!espera && base.bloqueio) {
329
+ return { ...base, acao: 'retomar', desdeEm: base.bloqueio.em,
330
+ porque: `bloqueio ${base.bloqueio.motivo} depois do ultimo despacho: o retry plan diz se a retomada e automatica` };
331
+ }
332
+ return avisarOuNada(espera ? `fim do turno ${espera.tipo}: o passo e do dono ou da entrega` : 'nada a conduzir agora');
333
+ }
334
+ function temPedidoGuardado(raiz, t) {
335
+ try {
336
+ return (0, thread_1.pedidoDaThread)(raiz, t) !== null;
337
+ }
338
+ catch {
339
+ return false;
340
+ }
341
+ }
342
+ /** O passo do condutor em cada thread aberta deste checkout, em ordem de id. */
343
+ function passosDoCondutor(carregado, opcoes = {}) {
344
+ const quando = opcoes.quando ?? new Date().toISOString();
345
+ const entregas = (0, parado_no_condutor_1.entregasDoProjeto)(carregado, { quando, limiarMin: 0 });
346
+ const passos = [];
347
+ for (const estado of entregas.estados) {
348
+ let t;
349
+ try {
350
+ t = (0, thread_1.lerThread)(carregado.raiz, estado.thread);
351
+ }
352
+ catch {
353
+ continue;
354
+ }
355
+ let eventos = [];
356
+ try {
357
+ eventos = (0, ledger_1.lerLedger)((0, thread_1.dirThread)(carregado.raiz, t.id));
358
+ }
359
+ catch { /* ledger ilegivel: sem bloqueio */ }
360
+ passos.push(passoDaThread(t, estado, eventos, temPedidoGuardado(carregado.raiz, t), (fase) => faseJaFeita(carregado.raiz, t, fase, estado, eventos), (0, parado_no_condutor_1.pendenciaDoDono)(t, eventos, quando)));
361
+ }
362
+ return passos.sort((a, b) => (a.thread < b.thread ? -1 : a.thread > b.thread ? 1 : 0));
363
+ }
364
+ /** O `ork conducao sentinela status`: a presenca desta maquina e o passo do condutor em cada thread aberta. */
365
+ function statusDaSentinela(carregado, opcoes = {}) {
366
+ const agora = opcoes.agora ?? new Date();
367
+ const maquina = opcoes.maquina ?? (0, maquina_1.nomeDaMaquina)();
368
+ return {
369
+ contrato: conducao_1.CONTRATO_DA_SENTINELA, consultadoEm: agora.toISOString(), maquina,
370
+ presenca: presencaDaSentinela(carregado.raiz, { agora, maquina }),
371
+ threads: passosDoCondutor(carregado, { quando: agora.toISOString() }),
372
+ };
373
+ }
374
+ /** O status em texto, para o terminal. */
375
+ function textoDoStatusDaSentinela(s) {
376
+ const linhas = [];
377
+ const p = s.presenca;
378
+ linhas.push(!p ? `Sentinela: desligada na maquina ${s.maquina}; o condutor externo conduz.`
379
+ : p.ativa ? `Sentinela: ligada na maquina ${s.maquina}, canal ${p.canal}, sessao ${p.sessao.slice(0, 8)}; o condutor externo cede o despacho e o retry.`
380
+ : `Sentinela: a presenca da sessao ${p.sessao.slice(0, 8)} venceu; o condutor externo conduz.`);
381
+ if (s.threads.length === 0)
382
+ linhas.push(' nenhuma thread aberta neste checkout');
383
+ for (const t of s.threads) {
384
+ const acao = t.acao === 'despachar' ? `despachar ${t.fase}` : t.acao;
385
+ linhas.push(` ${t.thread.padEnd(20)} ${t.modo.padEnd(8)} ${acao.padEnd(16)} ${t.porque}`);
386
+ }
387
+ return linhas.join('\n');
388
+ }
@@ -12,17 +12,9 @@ exports.linhaDeConducao = linhaDeConducao;
12
12
  * `thread.ts` e o `leases.ts` possam usa-lo sem ciclo de importacao.
13
13
  */
14
14
  const horario_1 = require("./horario");
15
- /** O que cada operacao de conducao e, em portugues de gente. */
16
- exports.NOME_DA_OPERACAO = {
17
- 'phase.run': 'despacho de fase',
18
- 'retry.run': 'retomada de fase',
19
- verify: 'ork verify',
20
- baseline: 'ork verify --baseline',
21
- 'fix.open': 'ork fix open',
22
- 'fix.reverify': 'ork fix reverify',
23
- 'mcp.verify': 'ork_verify (MCP)',
24
- handoff: 'reserva de quem assumiu',
25
- };
15
+ const locale_1 = require("./locale");
16
+ /** O que cada operacao de conducao e, em portugues de gente (o texto gravado das recusas usa este). */
17
+ exports.NOME_DA_OPERACAO = (0, locale_1.msg)('pt-BR').thread.operacoes;
26
18
  /** O objeto de leitura a partir do lease, sem consultar nada. */
27
19
  function conducaoDoLease(lease) {
28
20
  const c = lease.conducao;
@@ -43,19 +35,21 @@ function conducaoDoLease(lease) {
43
35
  }
44
36
  /** Quem segura a conducao, em poucas palavras: a sessao, o processo ou a reserva. */
45
37
  function quemConduz(c) {
38
+ // RM-061 (F2a): no locale do comando; fora dos comandos cobertos, o pt-BR de sempre.
39
+ const m = (0, locale_1.msg)().thread;
46
40
  if (c.sessao)
47
- return `sessao ${c.sessao.slice(0, 8)}`;
41
+ return m.quemSessao(c.sessao.slice(0, 8));
48
42
  if (c.dono.tipo === 'processo')
49
- return `processo ${c.dono.pid} (${exports.NOME_DA_OPERACAO[c.operacao]})`;
43
+ return m.quemProcesso(c.dono.pid, m.operacoes[c.operacao]);
50
44
  if (c.dono.tipo === 'reserva')
51
- return `reserva de ${c.dono.por}`;
52
- return exports.NOME_DA_OPERACAO[c.operacao];
45
+ return m.quemReserva(c.dono.por);
46
+ return m.operacoes[c.operacao];
53
47
  }
54
48
  /**
55
- * "conduzido agora por CANAL, sessao ID, fase F, desde HORARIO": a linha unica (CS7). O horario
49
+ * A linha unica de quem conduz (CS7): canal, quem, fase e desde, na redacao do catalogo (`msg().thread.conduzido`,
50
+ * RM-061), no locale do comando; fora dos comandos cobertos, o pt-BR de sempre. O horario
56
51
  * sai no fuso do dono; a legenda do fuso fica com a superficie, uma vez por tela.
57
52
  */
58
53
  function linhaDeConducao(c, opcoes = {}) {
59
- return `conduzido agora por ${c.canal}, ${quemConduz(c)}, fase ${c.fase ?? '-'}, ` +
60
- `desde ${(0, horario_1.formatarDataHora)(c.desde, opcoes.agora ? { agora: opcoes.agora } : {})}`;
54
+ return (0, locale_1.msg)().thread.conduzido(c.canal, quemConduz(c), c.fase ?? '-', (0, horario_1.formatarDataHora)(c.desde, opcoes.agora ? { agora: opcoes.agora } : {}));
61
55
  }