@orkastery/cli 0.5.3 → 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 (212) hide show
  1. package/adapters/claude-code/README.md +158 -0
  2. package/adapters/claude-code/mods/autoridade.json +56 -0
  3. package/adapters/claude-code/mods/ork-clima/.claude-plugin/plugin.json +14 -0
  4. package/adapters/claude-code/mods/ork-clima/hooks/contas.ts +283 -0
  5. package/adapters/claude-code/mods/ork-clima/hooks/hooks.json +3 -0
  6. package/adapters/claude-code/mods/ork-clima/hooks/previsao.ts +393 -0
  7. package/adapters/claude-code/mods/ork-clima/hooks/register.ts +205 -0
  8. package/adapters/claude-code/mods/ork-clima/orkastery-mod.json +5 -0
  9. package/adapters/claude-code/mods/ork-clima/types/index.d.ts +80 -0
  10. package/adapters/claude-code/mods/ork-freio/.claude-plugin/plugin.json +14 -0
  11. package/adapters/claude-code/mods/ork-freio/hooks/carga.ts +160 -0
  12. package/adapters/claude-code/mods/ork-freio/hooks/conferencia.ts +309 -0
  13. package/adapters/claude-code/mods/ork-freio/hooks/hooks.json +3 -0
  14. package/adapters/claude-code/mods/ork-freio/hooks/lote.ts +270 -0
  15. package/adapters/claude-code/mods/ork-freio/hooks/register.ts +285 -0
  16. package/adapters/claude-code/mods/ork-freio/orkastery-mod.json +5 -0
  17. package/adapters/claude-code/mods/ork-freio/types/index.d.ts +127 -0
  18. package/adapters/claude-code/mods/ork-mesa/.claude-plugin/plugin.json +14 -0
  19. package/adapters/claude-code/mods/ork-mesa/hooks/cartoes.tsx +246 -0
  20. package/adapters/claude-code/mods/ork-mesa/hooks/decidir.ts +110 -0
  21. package/adapters/claude-code/mods/ork-mesa/hooks/fila.ts +253 -0
  22. package/adapters/claude-code/mods/ork-mesa/hooks/hooks.json +3 -0
  23. package/adapters/claude-code/mods/ork-mesa/hooks/obvia.ts +98 -0
  24. package/adapters/claude-code/mods/ork-mesa/hooks/register.tsx +493 -0
  25. package/adapters/claude-code/mods/ork-mesa/orkastery-mod.json +5 -0
  26. package/adapters/claude-code/mods/ork-mesa/types/index.d.ts +131 -0
  27. package/adapters/claude-code/mods/ork-pista/.claude-plugin/plugin.json +14 -0
  28. package/adapters/claude-code/mods/ork-pista/hooks/acoes.ts +71 -0
  29. package/adapters/claude-code/mods/ork-pista/hooks/ci.ts +117 -0
  30. package/adapters/claude-code/mods/ork-pista/hooks/hooks.json +3 -0
  31. package/adapters/claude-code/mods/ork-pista/hooks/pista.tsx +356 -0
  32. package/adapters/claude-code/mods/ork-pista/hooks/register.tsx +439 -0
  33. package/adapters/claude-code/mods/ork-pista/orkastery-mod.json +5 -0
  34. package/adapters/claude-code/mods/ork-pista/types/index.d.ts +85 -0
  35. package/adapters/claude-code/mods/ork-ponte/.claude-plugin/plugin.json +13 -0
  36. package/adapters/claude-code/mods/ork-ponte/hooks/faixa.tsx +57 -0
  37. package/adapters/claude-code/mods/ork-ponte/hooks/guarda.ts +410 -0
  38. package/adapters/claude-code/mods/ork-ponte/hooks/hooks.json +3 -0
  39. package/adapters/claude-code/mods/ork-ponte/hooks/mostrado.ts +55 -0
  40. package/adapters/claude-code/mods/ork-ponte/hooks/nucleo.ts +157 -0
  41. package/adapters/claude-code/mods/ork-ponte/hooks/papel.ts +42 -0
  42. package/adapters/claude-code/mods/ork-ponte/hooks/register.ts +331 -0
  43. package/adapters/claude-code/mods/ork-ponte/hooks/vigia.ts +76 -0
  44. package/adapters/claude-code/mods/ork-ponte/orkastery-mod.json +5 -0
  45. package/adapters/claude-code/mods/ork-ponte/types/index.d.ts +178 -0
  46. package/adapters/claude-code/mods/ork-portao/.claude-plugin/plugin.json +14 -0
  47. package/adapters/claude-code/mods/ork-portao/hooks/hooks.json +3 -0
  48. package/adapters/claude-code/mods/ork-portao/hooks/lease.ts +201 -0
  49. package/adapters/claude-code/mods/ork-portao/hooks/plano.ts +304 -0
  50. package/adapters/claude-code/mods/ork-portao/hooks/register.ts +339 -0
  51. package/adapters/claude-code/mods/ork-portao/hooks/verify.ts +152 -0
  52. package/adapters/claude-code/mods/ork-portao/orkastery-mod.json +5 -0
  53. package/adapters/claude-code/mods/ork-portao/types/index.d.ts +60 -0
  54. package/adapters/claude-code/mods/ork-radar/.claude-plugin/plugin.json +14 -0
  55. package/adapters/claude-code/mods/ork-radar/hooks/colisao.ts +420 -0
  56. package/adapters/claude-code/mods/ork-radar/hooks/hooks.json +3 -0
  57. package/adapters/claude-code/mods/ork-radar/hooks/mapa.tsx +322 -0
  58. package/adapters/claude-code/mods/ork-radar/hooks/register.tsx +470 -0
  59. package/adapters/claude-code/mods/ork-radar/orkastery-mod.json +5 -0
  60. package/adapters/claude-code/mods/ork-radar/types/index.d.ts +72 -0
  61. package/adapters/claude-code/mods/ork-sentinela/.claude-plugin/plugin.json +14 -0
  62. package/adapters/claude-code/mods/ork-sentinela/hooks/alertas.ts +85 -0
  63. package/adapters/claude-code/mods/ork-sentinela/hooks/despacho.ts +156 -0
  64. package/adapters/claude-code/mods/ork-sentinela/hooks/hooks.json +3 -0
  65. package/adapters/claude-code/mods/ork-sentinela/hooks/register.ts +511 -0
  66. package/adapters/claude-code/mods/ork-sentinela/hooks/vigia.ts +186 -0
  67. package/adapters/claude-code/mods/ork-sentinela/orkastery-mod.json +5 -0
  68. package/adapters/claude-code/mods/ork-sentinela/types/index.d.ts +65 -0
  69. package/adapters/claude-code/mods/ork-torre/.claude-plugin/plugin.json +14 -0
  70. package/adapters/claude-code/mods/ork-torre/hooks/agenda.ts +60 -0
  71. package/adapters/claude-code/mods/ork-torre/hooks/detalhe.tsx +236 -0
  72. package/adapters/claude-code/mods/ork-torre/hooks/frases.ts +42 -0
  73. package/adapters/claude-code/mods/ork-torre/hooks/hooks.json +3 -0
  74. package/adapters/claude-code/mods/ork-torre/hooks/painel.tsx +344 -0
  75. package/adapters/claude-code/mods/ork-torre/hooks/rede.ts +97 -0
  76. package/adapters/claude-code/mods/ork-torre/hooks/register.tsx +437 -0
  77. package/adapters/claude-code/mods/ork-torre/orkastery-mod.json +5 -0
  78. package/adapters/claude-code/mods/ork-torre/types/index.d.ts +108 -0
  79. package/adapters/hermes/skills/orkastery-devmaster/SKILL.md +1 -1
  80. package/adapters/openclaw/README.md +2 -1
  81. package/adapters/openclaw/openclaw.plugin.json +1 -1
  82. package/adapters/openclaw/package.json +1 -1
  83. package/assets/docs-en/markdownlint-cli2.jsonc +22 -0
  84. package/assets/docs-en/padroes/documentacao-de-produto.md +135 -0
  85. package/assets/docs-en/padroes/roadmap-de-produto.md +100 -0
  86. package/assets/docs-en/produto/README.md +14 -0
  87. package/assets/docs-en/produto/_modelo-feature.md +62 -0
  88. package/assets/docs-en/roadmap/README.md +14 -0
  89. package/assets/docs-en/roadmap/_modelo-item.md +75 -0
  90. package/dist/adapters/claude-bg.js +77 -13
  91. package/dist/adapters/codex.js +10 -1
  92. package/dist/agents-md.js +24 -15
  93. package/dist/auditrun.js +17 -2
  94. package/dist/board.js +4 -3
  95. package/dist/branch-de-estado.js +23 -1
  96. package/dist/catalogo.js +33 -0
  97. package/dist/ci.js +13 -7
  98. package/dist/claims-conferir.js +342 -0
  99. package/dist/claims.js +3 -1
  100. package/dist/company-brain-client.js +36 -1
  101. package/dist/company-brain-journal.js +2 -1
  102. package/dist/company-brain-migration.js +36 -1
  103. package/dist/conducao-sentinela.js +388 -0
  104. package/dist/conducao-texto.js +12 -18
  105. package/dist/conducao.js +122 -30
  106. package/dist/conhecimento.js +490 -0
  107. package/dist/docs.js +22 -8
  108. package/dist/doctor-pulse-cron.js +182 -27
  109. package/dist/doctor-rede.js +18 -17
  110. package/dist/doctor.js +115 -111
  111. package/dist/entrega-pr.js +22 -21
  112. package/dist/escopo-escrita.js +10 -1
  113. package/dist/estado-thread.js +23 -1
  114. package/dist/experiencia-instalacao.js +45 -25
  115. package/dist/fabrica-batida.js +310 -0
  116. package/dist/fabrica-estado.js +38 -8
  117. package/dist/fabrica-publicar.js +3 -1
  118. package/dist/fila-pesada.js +444 -0
  119. package/dist/fix.js +3 -2
  120. package/dist/forja.js +1 -1
  121. package/dist/gates.js +3 -28
  122. package/dist/handoff.js +10 -6
  123. package/dist/hitl-local-receipt.js +2 -0
  124. package/dist/hitl-resumo.js +29 -3
  125. package/dist/hitl-tempo-parado.js +4 -4
  126. package/dist/hitl.js +29 -33
  127. package/dist/horario.js +14 -7
  128. package/dist/hosts.js +52 -1
  129. package/dist/impedimento.js +108 -14
  130. package/dist/index.js +623 -521
  131. package/dist/indice.js +7 -1
  132. package/dist/init.js +43 -47
  133. package/dist/instabilidade.js +144 -0
  134. package/dist/integracoes-locais.js +2 -0
  135. package/dist/intelligence-graph-cli.js +92 -21
  136. package/dist/intelligence-graph-contexto.js +56 -17
  137. package/dist/intelligence-graph-contract.js +2 -1
  138. package/dist/intelligence-graph-extract-md.js +56 -4
  139. package/dist/intelligence-graph-extract-ts.js +71 -3
  140. package/dist/intelligence-graph-query.js +66 -19
  141. package/dist/ledger.js +15 -1
  142. package/dist/liveness.js +6 -3
  143. package/dist/locale-ajuda.js +761 -0
  144. package/dist/locale-gerados.js +160 -0
  145. package/dist/locale-master.js +99 -0
  146. package/dist/locale-onboarding.js +92 -0
  147. package/dist/locale-phase.js +151 -0
  148. package/dist/locale-pulse.js +211 -0
  149. package/dist/locale-retry.js +385 -0
  150. package/dist/locale-roadmap.js +102 -0
  151. package/dist/locale-ship.js +494 -0
  152. package/dist/locale-thread.js +277 -0
  153. package/dist/locale-verify.js +125 -0
  154. package/dist/locale.js +714 -0
  155. package/dist/manifest.js +129 -3
  156. package/dist/maquina.js +23 -4
  157. package/dist/master-digest.js +6 -1
  158. package/dist/master-nota.js +4 -6
  159. package/dist/master.js +21 -24
  160. package/dist/mcp-artifacts.js +11 -2
  161. package/dist/mcp-git.js +23 -6
  162. package/dist/mcp-grafo.js +7 -4
  163. package/dist/mcp-server.js +8 -2
  164. package/dist/mcp-ship.js +3 -1
  165. package/dist/mcp-verify.js +6 -1
  166. package/dist/modos.js +3 -6
  167. package/dist/network-roadmap.js +5 -2
  168. package/dist/noctum-arco.js +911 -0
  169. package/dist/noctum-capacidade.js +141 -0
  170. package/dist/noctum-envelope.js +810 -0
  171. package/dist/noctum-execucao.js +304 -0
  172. package/dist/noctum-fila.js +280 -0
  173. package/dist/ocupacao.js +29 -2
  174. package/dist/onboarding.js +40 -21
  175. package/dist/orkmind.js +9 -2
  176. package/dist/orquestracao.js +13 -12
  177. package/dist/parado-no-condutor.js +34 -9
  178. package/dist/phase.js +94 -39
  179. package/dist/policies.js +4 -4
  180. package/dist/portfolio.js +4 -2
  181. package/dist/postura-local.js +234 -0
  182. package/dist/preflight.js +11 -10
  183. package/dist/procedencia.js +225 -0
  184. package/dist/projeto-alvo.js +127 -4
  185. package/dist/prompts.js +6 -2
  186. package/dist/pulse-delivery.js +13 -2
  187. package/dist/pulse.js +52 -19
  188. package/dist/ratelimit.js +4 -2
  189. package/dist/redacao-saida.js +63 -0
  190. package/dist/rede-status.js +3 -2
  191. package/dist/retry.js +232 -148
  192. package/dist/roadmap-reservas.js +20 -3
  193. package/dist/roadmap-status.js +129 -33
  194. package/dist/runtime-context.js +181 -3
  195. package/dist/runtimes.js +2 -1
  196. package/dist/session-events.js +7 -3
  197. package/dist/session-watcher-claude.js +7 -1
  198. package/dist/session-watcher.js +185 -33
  199. package/dist/sessoes-contas.js +1 -1
  200. package/dist/ship-publicar.js +800 -0
  201. package/dist/ship.js +84 -81
  202. package/dist/steal.js +77 -0
  203. package/dist/thread-close.js +3 -2
  204. package/dist/thread.js +153 -82
  205. package/dist/util.js +8 -1
  206. package/dist/verify.js +141 -52
  207. package/dist/vida-da-thread.js +254 -0
  208. package/dist/worktree-local.js +165 -0
  209. package/dist/worktree.js +20 -0
  210. package/dist/yaml.js +27 -8
  211. package/package.json +1 -1
  212. package/schemas/code-artifact-graph.v1.schema.json +2 -1
@@ -0,0 +1,80 @@
1
+ // Contrato do Clima de Cota (RM-059, onda 1, F4): os tipos da serie e da previsao das janelas de limite
2
+ // da conta, e o estado do mod. O Clima nao acrescenta noun ao `$`: usa o `$.ork` da `ork-ponte`, que ele
3
+ // lista em `dependencies`. Sem import nem referencia, como o motor exige de um contrato.
4
+
5
+ /** Um ponto da serie: quando foi lido (ms desde a epoca, relogio do `$.clock`) e o percentual. */
6
+ export type ClimaPonto = {
7
+ em: number
8
+ pct: number
9
+ }
10
+
11
+ /** A serie de uma janela (`five_hour`, `seven_day` ou a de gasto de um gateway), desde que ela renovou. */
12
+ export type ClimaJanela = {
13
+ kind: string
14
+ /** Quando renova, em ms desde a epoca; `null` quando o motor nao disse. */
15
+ renovaEm: number | null
16
+ pontos: ClimaPonto[]
17
+ }
18
+
19
+ /** A serie `ork.clima-serie/v1` no `$.store` do mod, que dura entre sessoes. */
20
+ export type ClimaSerie = {
21
+ formato: 'ork.clima-serie/v1'
22
+ janelas: ClimaJanela[]
23
+ }
24
+
25
+ /**
26
+ * `esgotada`: 100% ou mais. `acaba-antes`: no ritmo atual, acaba antes de renovar. `folga`: o ritmo nao
27
+ * esgota antes de renovar. `sem-ritmo`: ainda sem leituras para medir o ritmo, ou com uma serie nova demais
28
+ * para prever tao longe (D15).
29
+ */
30
+ export type ClimaEstado = 'esgotada' | 'acaba-antes' | 'folga' | 'sem-ritmo'
31
+
32
+ /** A previsao de uma janela no ritmo atual. */
33
+ export type ClimaPrevisao = {
34
+ kind: string
35
+ /** `5 h`, `7 d`, `gasto` ou o proprio `kind`. */
36
+ rotulo: string
37
+ /** O percentual da ultima leitura. */
38
+ pct: number
39
+ /** Quando a ultima leitura chegou, em ms desde a epoca. */
40
+ lidaEm: number
41
+ renovaEm: number | null
42
+ /** Pontos percentuais por hora no intervalo recente; `null` sem leituras suficientes. */
43
+ ritmoPorHora: number | null
44
+ /** Quando acaba no ritmo atual, em ms desde a epoca; `null` sem ritmo que esgote. */
45
+ acabaEm: number | null
46
+ estado: ClimaEstado
47
+ }
48
+
49
+ /** Uma retomada da fila que nao cabe, como a ultima leitura da fila deixou. */
50
+ export type ClimaNaoCabe = {
51
+ id: string
52
+ thread: string
53
+ /** O que vai ao registro do mostrado da ponte (D8). */
54
+ texto: string
55
+ /** Se o registro ja aceitou; a que falhou e tentada de novo na medicao seguinte (D15). */
56
+ registrada: boolean
57
+ }
58
+
59
+ /** O ultimo problema de uma medicao, que o `/clima` mostra (D15). */
60
+ export type ClimaProblema = {
61
+ /** O que aconteceu, numa frase que comeca minuscula. */
62
+ motivo: string
63
+ /** Quando, em ms desde a epoca; `null` quando nem o relogio respondeu. */
64
+ em: number | null
65
+ /** Se veio da leitura: so a proxima leitura boa o limpa. */
66
+ daLeitura: boolean
67
+ }
68
+
69
+ declare module 'claude-code' {
70
+ interface PluginState {
71
+ 'ork-clima': {
72
+ /** A ultima linha que o Clima publicou na faixa da ponte; `null` depois de tira-la. */
73
+ faixa: string | null
74
+ /** As retomadas que nao cabiam na ultima leitura da fila, para as medicoes sem leitura nova (D13). */
75
+ naoCabem: ClimaNaoCabe[]
76
+ /** O ultimo problema de medicao, ate uma medicao boa limpa-lo (D15). */
77
+ problema: ClimaProblema | null
78
+ }
79
+ }
80
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "ork-freio",
3
+ "version": "0.1.0",
4
+ "description": "Freio de Lote do Orkastery no Claude Code: na sessão de fase, acha as claims que o lote de ferramentas mexeu, pede ao núcleo a conferência delas no sandbox do verify e devolve o que quebrou no lote seguinte.",
5
+ "author": {
6
+ "name": "Orkastery contributors",
7
+ "url": "https://github.com/orkastery"
8
+ },
9
+ "homepage": "https://orkastery.com/",
10
+ "repository": "https://github.com/orkastery/orkastery",
11
+ "license": "MIT",
12
+ "types": "./types/index.d.ts",
13
+ "dependencies": ["ork-ponte"]
14
+ }
@@ -0,0 +1,160 @@
1
+ // A carga do Freio de Lote (RM-059, F2, T2.1): o que ele le do nucleo para saber quais claims vigiar.
2
+ // Modulo puro: interpreta o que as ferramentas de leitura do servidor `orkastery` devolvem e o retrato
3
+ // da ponte. As chamadas ao `$` ficam no register.ts; nada aqui le `.orkastery/` do disco (D2).
4
+ import type { FreioCarga, FreioClaim } from '../types'
5
+
6
+ export const SERVIDOR = 'orkastery'
7
+ /**
8
+ * A lista fechada de ferramentas MCP do Freio (D2): as duas de leitura. O `$.mcp.call` nao pede permissao
9
+ * ao dono, entao o teste do nucleo confere pela AST que o mod so chama estas.
10
+ */
11
+ export const FERRAMENTA_DAS_CLAIMS = 'ork_claims_list'
12
+ export const FERRAMENTA_DO_PLANO = 'ork_artifact_read'
13
+ export const FERRAMENTAS_DO_FREIO: readonly string[] = [FERRAMENTA_DAS_CLAIMS, FERRAMENTA_DO_PLANO]
14
+ /** Claims com mais idade que isto sao relidas no lote que precisa delas. */
15
+ export const IDADE_DAS_CLAIMS_MS = 60000
16
+ /** O plano com mais idade que isto e relido no lote que precisa dele. */
17
+ export const IDADE_DO_PLANO_MS = 300000
18
+
19
+ const ID_DE_CLAIM = /^C\d+$/
20
+ const ID_DE_THREAD = /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,79}$/
21
+ /** O rotulo de `touch_paths` de uma tarefa nos formatos em uso nos planos; o grupo 1 e o resto da linha. */
22
+ const ROTULO_DE_TOUCH_PATHS = /^\s*(?:[-*+]\s+)?(?:\*\*touch_paths:?\*\*\s*:?|`touch_paths`\s*:|touch_paths\s*:)\s*(.*)$/i
23
+ /** Linha que abre outro item de lista, cabecalho ou cerca de codigo (a mesma regra do verify da tarefa no nucleo). */
24
+ const ABRE_BLOCO = /^\s*(?:[-*+]\s|\d+[.)]\s|#{1,6}\s|```|~~~)/
25
+ const TRECHO = /`([^`]+)`/g
26
+
27
+ export class ErroDeCarga extends Error {}
28
+
29
+ /** Os argumentos de cada ferramenta da lista fechada para a thread do despacho. */
30
+ export function argsDaFerramenta(ferramenta: string, thread: string): Record<string, unknown> {
31
+ if (!ID_DE_THREAD.test(thread)) throw new ErroDeCarga('ork-freio: id de thread invalido')
32
+ if (ferramenta === FERRAMENTA_DAS_CLAIMS) return { threadId: thread }
33
+ if (ferramenta === FERRAMENTA_DO_PLANO) return { threadId: thread, tipo: 'plan' }
34
+ throw new ErroDeCarga(`ork-freio: ferramenta fora da lista fechada: ${ferramenta}`)
35
+ }
36
+
37
+ /** O JSON do primeiro bloco de texto de um resultado MCP; resultado com erro vira excecao. */
38
+ export function jsonDoMcp(r: unknown, ferramenta: string): unknown {
39
+ const resultado = r as { content?: ReadonlyArray<{ type?: string; text?: unknown }>; isError?: boolean } | null
40
+ const texto = (resultado?.content ?? []).find(c => c?.type === 'text' && typeof c.text === 'string')?.text as string | undefined
41
+ if (resultado?.isError) throw new ErroDeCarga(`ork-freio: ${ferramenta} respondeu com erro${texto ? `: ${texto.slice(0, 200)}` : ''}`)
42
+ if (texto === undefined) throw new ErroDeCarga(`ork-freio: ${ferramenta} nao devolveu texto`)
43
+ try {
44
+ return JSON.parse(texto)
45
+ } catch {
46
+ throw new ErroDeCarga(`ork-freio: ${ferramenta} devolveu texto que nao e JSON`)
47
+ }
48
+ }
49
+
50
+ /** Caminho relativo normalizado: barras para a frente, sem `./` na frente nem barra no fim. */
51
+ export function normalizar(caminho: string): string {
52
+ return caminho.trim().replace(/\\/g, '/').replace(/^(?:\.\/)+/, '').replace(/\/+$/, '')
53
+ }
54
+
55
+ /**
56
+ * As claims nao retiradas, com o que o Freio precisa: id, arquivo e comandos. O `claims.jsonl` e append-only e
57
+ * o nucleo ja devolve a versao mais nova de cada claim; se vier repetida, a ultima vence do mesmo jeito.
58
+ */
59
+ export function claimsDoNucleo(bruto: unknown): FreioClaim[] {
60
+ if (!Array.isArray(bruto)) throw new ErroDeCarga('ork-freio: a lista de claims nao e lista')
61
+ const porId = new Map<string, FreioClaim | null>()
62
+ for (const item of bruto) {
63
+ const c = item as { id?: unknown; arquivo?: unknown; verificar?: unknown; estado?: unknown } | null
64
+ if (!c || typeof c.id !== 'string' || !ID_DE_CLAIM.test(c.id)) continue
65
+ if (c.estado === 'retirada') { porId.set(c.id, null); continue }
66
+ const comandos = Array.isArray(c.verificar) ? c.verificar.filter((v): v is string => typeof v === 'string' && v.trim() !== '') : []
67
+ const arquivo = typeof c.arquivo === 'string' && c.arquivo.trim() !== '' ? normalizar(c.arquivo) : null
68
+ porId.set(c.id, { id: c.id, arquivo, comandos })
69
+ }
70
+ return [...porId.values()].filter((c): c is FreioClaim => c !== null)
71
+ .sort((a, b) => Number(a.id.slice(1)) - Number(b.id.slice(1)))
72
+ }
73
+
74
+ /**
75
+ * O maior numero de claim que o nucleo ja deu na thread, retiradas incluidas: claim com numero acima dele foi
76
+ * cadastrada depois desta leitura, e a rodada nao a tira da fila por nao estar na lista.
77
+ */
78
+ export function maiorNumeroDeClaim(bruto: unknown): number {
79
+ if (!Array.isArray(bruto)) return 0
80
+ let maior = 0
81
+ for (const item of bruto) {
82
+ const id = (item as { id?: unknown } | null)?.id
83
+ if (typeof id === 'string' && ID_DE_CLAIM.test(id)) maior = Math.max(maior, Number(id.slice(1)))
84
+ }
85
+ return maior
86
+ }
87
+
88
+ /** O texto do plano que o `ork_artifact_read` devolve, ou `null` quando a thread ainda nao tem plano. */
89
+ export function textoDoPlano(bruto: unknown): string | null {
90
+ const documento = bruto as { tipo?: unknown; conteudo?: unknown } | null
91
+ if (!documento || typeof documento !== 'object') throw new ErroDeCarga('ork-freio: o plano lido nao e objeto')
92
+ return typeof documento.conteudo === 'string' ? documento.conteudo : null
93
+ }
94
+
95
+ function ehCaminho(texto: string): boolean {
96
+ return texto !== '' && !/\s/.test(texto) && !texto.startsWith('-') && !texto.split('/').includes('..')
97
+ }
98
+
99
+ /**
100
+ * A uniao dos `touch_paths` de todas as tarefas do plano (D9): os trechos entre crases do item do rotulo inteiro,
101
+ * como o Markdown o junta e como o nucleo le o verify da tarefa: a linha do rotulo, as linhas mais recuadas que
102
+ * ela (sub-itens e continuacao quebrada) e a continuacao logo abaixo que nao abre outro bloco. `null` sem plano
103
+ * ou sem nenhum caminho.
104
+ */
105
+ export function touchPathsDoPlano(conteudo: string | null): string[] | null {
106
+ if (typeof conteudo !== 'string') return null
107
+ const achados = new Set<string>()
108
+ const juntar = (texto: string) => {
109
+ for (const m of texto.matchAll(TRECHO)) {
110
+ const caminho = normalizar(m[1] as string)
111
+ if (ehCaminho(caminho)) achados.add(caminho)
112
+ }
113
+ }
114
+ let recuoDoRotulo = -1
115
+ let depoisDeBranco = false
116
+ for (const linha of conteudo.replace(/\r\n/g, '\n').split('\n')) {
117
+ const recuo = linha.length - linha.trimStart().length
118
+ const rotulo = ROTULO_DE_TOUCH_PATHS.exec(linha)
119
+ if (rotulo) {
120
+ juntar(rotulo[1] as string)
121
+ recuoDoRotulo = recuo
122
+ depoisDeBranco = false
123
+ continue
124
+ }
125
+ if (recuoDoRotulo < 0) continue
126
+ if (linha.trim() === '') { depoisDeBranco = true; continue }
127
+ if (recuo <= recuoDoRotulo && (depoisDeBranco || ABRE_BLOCO.test(linha))) { recuoDoRotulo = -1; continue }
128
+ juntar(linha)
129
+ depoisDeBranco = false
130
+ }
131
+ return achados.size > 0 ? [...achados].sort() : null
132
+ }
133
+
134
+ /** O status da thread na secao `threads` do retrato `ork.maestro-snapshot/v1`, ou `null` quando ele nao diz. */
135
+ export function statusNoRetrato(retrato: unknown, thread: string): string | null {
136
+ const itens = (retrato as { sections?: { threads?: { items?: unknown } } } | null)?.sections?.threads?.items
137
+ if (!Array.isArray(itens)) return null
138
+ const item = itens.find(i => (i as { id?: unknown } | null)?.id === thread) as { status?: unknown } | undefined
139
+ return typeof item?.status === 'string' ? item.status : null
140
+ }
141
+
142
+ /** O Freio age na thread? So deixa de agir com a thread fechada no retrato (D10). */
143
+ export function threadAtiva(status: string | null): boolean {
144
+ return status !== 'fechada'
145
+ }
146
+
147
+ /** A carga de uma thread antes da primeira leitura. */
148
+ export function cargaVazia(thread: string): FreioCarga {
149
+ return { thread, statusDaThread: null, claims: null, claimsEm: 0, maiorClaim: 0, touchPaths: null, planoLido: false, planoEm: 0, erros: [] }
150
+ }
151
+
152
+ /** As claims precisam ser relidas: nunca lidas, velhas, ou o lote mexeu nelas. */
153
+ export function claimsVelhas(carga: FreioCarga, agora: number, loteMexeuNasClaims: boolean): boolean {
154
+ return carga.claims === null || loteMexeuNasClaims || !(agora - carga.claimsEm >= 0 && agora - carga.claimsEm < IDADE_DAS_CLAIMS_MS)
155
+ }
156
+
157
+ /** O plano precisa ser relido: nunca lido, velho, ou o lote o regravou. */
158
+ export function planoVelho(carga: FreioCarga, agora: number, loteMexeuNoPlano: boolean): boolean {
159
+ return !carga.planoLido || loteMexeuNoPlano || !(agora - carga.planoEm >= 0 && agora - carga.planoEm < IDADE_DO_PLANO_MS)
160
+ }
@@ -0,0 +1,309 @@
1
+ // A conferencia pelo nucleo e a devolucao no lote seguinte (RM-059, F2, T2.3). Modulo puro: monta o argv do
2
+ // `ork claims conferir`, le o JSON `ork.claims-conferencia/v1`, cuida da rodada, apura o resultado e escreve o
3
+ // texto do lote e a linha de status. O Freio nunca roda o comando de uma claim: quem roda e o nucleo, no
4
+ // sandbox do verify (D12 do plano dos mods), e quem verifica de verdade continua sendo o `ork verify`.
5
+ import type { FreioEntrega, FreioFalha, FreioPedido, FreioRodada, FreioVigia } from '../types'
6
+
7
+ export const FORMATO_DA_CONFERENCIA = 'ork.claims-conferencia/v1'
8
+ /** O prazo total pedido ao nucleo, em segundos (o padrao do `ork claims conferir`, D6). */
9
+ export const PRAZO_DA_CONFERENCIA_S = 180
10
+ /** O vigia do mod: a conferencia que passar disto e encerrada e fica sem veredito (D6, D7). */
11
+ export const VIGIA_DA_CONFERENCIA_MS = 240000
12
+ /**
13
+ * Depois do vigia, quanto o laco espera o processo fechar; se ele seguir aberto, a conferencia fica indisponivel
14
+ * e nenhuma rodada nova abre enquanto ele nao fechar (duas conferencias nunca correm juntas).
15
+ */
16
+ export const ESPERA_DO_FIM_MS = 30000
17
+ /** O motivo de indisponivel quando o processo da rodada anterior nao fechou. */
18
+ export const MOTIVO_PROCESSO_ABERTO = 'o processo anterior do ork claims conferir nao fechou depois do vigia'
19
+ /** Claims por rodada; so uma rodada roda por vez (D5). */
20
+ export const TETO_POR_RODADA = 6
21
+ /** A pausa depois de uma conferencia indisponivel (D7). */
22
+ export const PAUSA_SEM_CONFERENCIA_MS = 600000
23
+ /** A pausa curta quando outra conducao segura a thread (saida 3): a rodada volta para a fila. */
24
+ export const PAUSA_DE_CONDUCAO_MS = 60000
25
+ /** Tetos do texto ao modelo (D8). */
26
+ export const TETO_DE_CLAIMS_NO_TEXTO = 3
27
+ export const TETO_DE_LINHAS_DA_SAIDA = 12
28
+ export const TETO_DE_CARACTERES_DA_SAIDA = 1200
29
+ export const TETO_DO_TEXTO = 6000
30
+ /** O que o mod guarda da saida do processo: o JSON inteiro cabe com folga; do stderr, so o fim. */
31
+ export const TETO_DA_SAIDA_LIDA = 4194304
32
+ export const TETO_DO_ERRO_LIDO = 2000
33
+
34
+ const ID_DE_CLAIM = /^C\d+$/
35
+ const ID_DE_THREAD = /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,79}$/
36
+ /** A recusa do nucleo por uma claim so (retirada ou que nao existe): as outras da rodada voltam para a fila. */
37
+ const RECUSA_POR_CLAIM = /^claims\.conferir\.(?:retirada|desconhecida): (C\d+)\b/
38
+ /** A saida do CLI quando outra conducao segura a thread. */
39
+ const SAIDA_DE_CONDUCAO = 3
40
+
41
+ /** Uma claim como a conferencia a devolveu, so com o que o Freio entrega. */
42
+ export type ItemConferido = {
43
+ id: string
44
+ alegacao: string
45
+ arquivo: string | null
46
+ verificado: boolean
47
+ motivo: string | null
48
+ comando: string
49
+ codigo: number | null
50
+ fim: string
51
+ }
52
+
53
+ /**
54
+ * A leitura de uma rodada: o resultado do nucleo; a recusa de uma claim so (retirada ou desconhecida); a thread
55
+ * conduzida por outro (saida 3); a conferencia indisponivel; ou o vigia do mod vencido.
56
+ */
57
+ export type Leitura =
58
+ | { tipo: 'resultado'; itens: ItemConferido[] }
59
+ | { tipo: 'recusada'; id: string; motivo: string }
60
+ | { tipo: 'ocupada'; motivo: string }
61
+ | { tipo: 'indisponivel'; motivo: string }
62
+ | { tipo: 'prazo' }
63
+
64
+ /** O argv do `ork claims conferir` de uma rodada; o comando das claims fica com o nucleo. */
65
+ export function argvDaConferencia(thread: string, ids: readonly string[]): string[] {
66
+ if (!ID_DE_THREAD.test(thread)) throw new Error('ork-freio: id de thread invalido')
67
+ if (ids.length === 0 || ids.some(id => !ID_DE_CLAIM.test(id))) throw new Error('ork-freio: ids de claim invalidos')
68
+ return ['ork', 'claims', 'conferir', thread, '--claims', ids.join(','), '--prazo', String(PRAZO_DA_CONFERENCIA_S), '--json']
69
+ }
70
+
71
+ /** Junta um pedaco da saida, ate o teto; do stderr fica so o fim. */
72
+ export function acumular(atual: string, pedaco: string, doErro: boolean): string {
73
+ const junto = atual + pedaco
74
+ if (doErro) return junto.length > TETO_DO_ERRO_LIDO ? junto.slice(-TETO_DO_ERRO_LIDO) : junto
75
+ return junto.length > TETO_DA_SAIDA_LIDA ? junto.slice(0, TETO_DA_SAIDA_LIDA) : junto
76
+ }
77
+
78
+ const ultimaLinha = (texto: string) => texto.trim().split('\n').filter(l => l.trim() !== '').slice(-1)[0]?.trim().slice(0, 300) ?? ''
79
+
80
+ function itemDoNucleo(bruto: unknown): ItemConferido | null {
81
+ const i = bruto as { id?: unknown; alegacao?: unknown; arquivo?: unknown; verificado?: unknown; motivo?: unknown; execucoes?: unknown } | null
82
+ if (!i || typeof i.id !== 'string' || !ID_DE_CLAIM.test(i.id)) return null
83
+ const execucoes = Array.isArray(i.execucoes) ? i.execucoes as Array<{ comando?: unknown; ok?: unknown; code?: unknown; fim?: unknown }> : []
84
+ const chave = execucoes.find(x => x && x.ok === false) ?? execucoes[0]
85
+ return {
86
+ id: i.id,
87
+ alegacao: typeof i.alegacao === 'string' ? i.alegacao : '',
88
+ arquivo: typeof i.arquivo === 'string' ? i.arquivo : null,
89
+ verificado: i.verificado === true,
90
+ motivo: typeof i.motivo === 'string' ? i.motivo : null,
91
+ comando: typeof chave?.comando === 'string' ? chave.comando : '',
92
+ codigo: typeof chave?.code === 'number' ? chave.code : null,
93
+ fim: typeof chave?.fim === 'string' ? chave.fim : '',
94
+ }
95
+ }
96
+
97
+ /**
98
+ * A saida do `ork claims conferir --json` (D6, D7): o resultado so com o formato certo, itens em lista e saida
99
+ * 0 ou 1; a recusa de uma claim so (saida 2 com `claims.conferir.retirada` ou `.desconhecida`) e por claim; a
100
+ * saida 3 e outra conducao segurando a thread; o resto (saida 2 com `erro`, um `ork` antigo) e indisponivel.
101
+ */
102
+ export function lerConferencia(stdout: string, codigo: number | null, stderr: string): Leitura {
103
+ let j: { formato?: unknown; itens?: unknown; erro?: unknown } | null = null
104
+ try { j = JSON.parse(stdout) } catch { j = null }
105
+ if (j && j.formato === FORMATO_DA_CONFERENCIA && Array.isArray(j.itens) && (codigo === 0 || codigo === 1)) {
106
+ return { tipo: 'resultado', itens: j.itens.map(itemDoNucleo).filter((i): i is ItemConferido => i !== null) }
107
+ }
108
+ const erro = j && typeof j.erro === 'string' ? j.erro.trim().slice(0, 300) : ''
109
+ const recusa = RECUSA_POR_CLAIM.exec(erro)
110
+ if (recusa) return { tipo: 'recusada', id: recusa[1] as string, motivo: erro }
111
+ // JSON que nao serve nao vira motivo: o fim do stderr diz mais; texto solto (um `ork` antigo) entra pelo fim.
112
+ const motivo = erro || ultimaLinha(stderr) || (j === null ? ultimaLinha(stdout) : '') || `saiu com codigo ${codigo ?? 'desconhecido'}`
113
+ if (codigo === SAIDA_DE_CONDUCAO) return { tipo: 'ocupada', motivo }
114
+ if (erro) return { tipo: 'indisponivel', motivo: erro }
115
+ return { tipo: 'indisponivel', motivo: `ork claims conferir sem resultado: ${motivo}` }
116
+ }
117
+
118
+ export function entregaVazia(): FreioEntrega {
119
+ return { falhas: [], semVeredito: [], recuperadas: [], indisponivel: null }
120
+ }
121
+
122
+ /** A vigia com os campos da conferencia preenchidos (a vigia gravada antes deles nasce sem eles). */
123
+ export function comConferencia(v: FreioVigia): Required<FreioVigia> {
124
+ return {
125
+ rodada: null, entrega: entregaVazia(), emRisco: [], pausaAte: 0, indisponivel: null, avisouIndisponivel: false, foraPendentes: [],
126
+ avisadas: [],
127
+ ...v,
128
+ } as Required<FreioVigia>
129
+ }
130
+
131
+ /** Ha trabalho para o laco de fundo: fila com claim ou rodada aberta (a de um laco que morreu com o worker, D10). */
132
+ export function precisaDeLaco(vigia: FreioVigia): boolean {
133
+ const v = comConferencia(vigia)
134
+ return v.fila.length > 0 || v.rodada !== null
135
+ }
136
+
137
+ const numero = (id: string) => Number(id.slice(1)) || 0
138
+
139
+ /** A mesma ordem da fila do lote: a forca maior antes; na mesma forca, a claim mais nova antes. */
140
+ function ordenar(pedidos: FreioPedido[]): FreioPedido[] {
141
+ return pedidos.sort((a, b) => b.forca - a.forca || numero(b.id) - numero(a.id))
142
+ }
143
+
144
+ /** Devolve pedidos a fila sem repeticao, juntando os caminhos e ficando com a forca maior. */
145
+ function devolverAFila(fila: readonly FreioPedido[], pedidos: readonly FreioPedido[]): FreioPedido[] {
146
+ const porId = new Map(fila.map(p => [p.id, { ...p, caminhos: [...p.caminhos] }]))
147
+ for (const p of pedidos) {
148
+ const ja = porId.get(p.id)
149
+ if (!ja) { porId.set(p.id, { ...p, caminhos: [...p.caminhos] }); continue }
150
+ ja.forca = Math.max(ja.forca, p.forca)
151
+ for (const c of p.caminhos) if (!ja.caminhos.includes(c)) ja.caminhos.push(c)
152
+ }
153
+ return ordenar([...porId.values()])
154
+ }
155
+
156
+ /** Os pedidos de uma rodada, para voltarem a fila. */
157
+ function pedidosDaRodada(rodada: FreioRodada, ids: readonly string[]): FreioPedido[] {
158
+ return ids.map(id => ({ id, forca: rodada.forcas?.[id] ?? 1, caminhos: [...(rodada.caminhos[id] ?? [])] }))
159
+ }
160
+
161
+ /**
162
+ * Abre a rodada: tira da fila ate o teto, se nao ha rodada aberta nem pausa; a rodada aberta segue como esta.
163
+ * Com a lista das claims ativas, a claim que saiu dela (retirada) sai da fila antes; a claim com numero acima do
164
+ * maior que a leitura viu (cadastrada depois dela) fica, e a recusa por claim do nucleo cobre o resto.
165
+ */
166
+ export function iniciarRodada(vigia: FreioVigia, agora: number, teto = TETO_POR_RODADA,
167
+ ativas: { ids: readonly string[]; maior: number } | null = null): Required<FreioVigia> {
168
+ const v = comConferencia(vigia)
169
+ const fila = ativas ? v.fila.filter(p => ativas.ids.includes(p.id) || numero(p.id) > ativas.maior) : v.fila
170
+ if (v.rodada || fila.length === 0 || agora < v.pausaAte) return fila === v.fila ? v : { ...v, fila }
171
+ const escolhidos = fila.slice(0, teto)
172
+ const rodada: FreioRodada = {
173
+ ids: escolhidos.map(p => p.id),
174
+ caminhos: Object.fromEntries(escolhidos.map(p => [p.id, [...p.caminhos]])),
175
+ forcas: Object.fromEntries(escolhidos.map(p => [p.id, p.forca])),
176
+ inicio: v.seq,
177
+ }
178
+ return { ...v, fila: fila.slice(teto), rodada }
179
+ }
180
+
181
+ /**
182
+ * O resultado da claim e velho (D6): a claim voltou para a fila durante a rodada (um lote tocou qualquer caminho
183
+ * que a afeta: o arquivo dela, um caminho do comando ou o nome-base) ou um caminho que a abriu foi tocado depois.
184
+ */
185
+ function velha(v: Required<FreioVigia>, rodada: FreioRodada, id: string): boolean {
186
+ return v.fila.some(p => p.id === id) || (rodada.caminhos[id] ?? []).some(c => (v.toques[c] ?? 0) > rodada.inicio)
187
+ }
188
+
189
+ /** Fecha a rodada com a leitura: quebra, sem veredito, recuperada, recusa, conducao alheia e indisponivel (D6, D7). */
190
+ export function fecharRodada(vigia: FreioVigia, leitura: Leitura, agora: number): Required<FreioVigia> {
191
+ const v = comConferencia(vigia)
192
+ const rodada = v.rodada
193
+ if (!rodada) return v
194
+ if (leitura.tipo === 'indisponivel') {
195
+ return {
196
+ ...v, rodada: null, fila: [], pausaAte: agora + PAUSA_SEM_CONFERENCIA_MS, indisponivel: leitura.motivo,
197
+ entrega: v.avisouIndisponivel ? v.entrega : { ...v.entrega, indisponivel: leitura.motivo }, avisouIndisponivel: true,
198
+ }
199
+ }
200
+ if (leitura.tipo === 'recusada') {
201
+ // So a claim recusada sai; as outras voltam para a fila e rodam na proxima rodada, sem pausa e sem aviso.
202
+ const fila = devolverAFila(v.fila.filter(p => p.id !== leitura.id), pedidosDaRodada(rodada, rodada.ids.filter(id => id !== leitura.id)))
203
+ return { ...v, rodada: null, fila, emRisco: v.emRisco.filter(id => id !== leitura.id) }
204
+ }
205
+ if (leitura.tipo === 'ocupada') {
206
+ return { ...v, rodada: null, fila: devolverAFila(v.fila, pedidosDaRodada(rodada, rodada.ids)), pausaAte: agora + PAUSA_DE_CONDUCAO_MS }
207
+ }
208
+ const avisadas = new Set(v.avisadas)
209
+ const falhas = new Map(v.entrega.falhas.map(f => [f.id, f]))
210
+ const semVeredito = new Map(v.entrega.semVeredito.map(s => [s.id, s]))
211
+ const recuperadas = new Set(v.entrega.recuperadas)
212
+ const emRisco = new Set(v.emRisco)
213
+ const semVez = (id: string, motivo: string) => { semVeredito.set(id, { id, motivo }); falhas.delete(id) }
214
+ if (leitura.tipo === 'prazo') {
215
+ for (const id of rodada.ids) if (!velha(v, rodada, id)) semVez(id, 'prazo do Freio')
216
+ } else {
217
+ for (const item of leitura.itens) {
218
+ if (!rodada.ids.includes(item.id) || velha(v, rodada, item.id)) continue
219
+ if (item.verificado) {
220
+ // So volta como recuperada a claim cuja falha o modelo ja recebeu; a que ele nunca viu sai sem barulho.
221
+ falhas.delete(item.id)
222
+ semVeredito.delete(item.id)
223
+ emRisco.delete(item.id)
224
+ if (avisadas.delete(item.id)) recuperadas.add(item.id)
225
+ } else if (item.motivo === 'claims.failed') {
226
+ const falha: FreioFalha = { id: item.id, alegacao: item.alegacao, arquivo: item.arquivo, comando: item.comando, codigo: item.codigo, fim: item.fim }
227
+ falhas.set(item.id, falha)
228
+ semVeredito.delete(item.id)
229
+ recuperadas.delete(item.id)
230
+ emRisco.add(item.id)
231
+ } else semVez(item.id, item.motivo ?? 'sem motivo')
232
+ }
233
+ }
234
+ return {
235
+ ...v, rodada: null, indisponivel: null, emRisco: [...emRisco], avisadas: [...avisadas],
236
+ entrega: { ...v.entrega, falhas: [...falhas.values()], semVeredito: [...semVeredito.values()], recuperadas: [...recuperadas] },
237
+ }
238
+ }
239
+
240
+ /**
241
+ * A conferencia fica indisponivel sem rodada aberta (o processo da rodada anterior nao fechou): o mesmo efeito da
242
+ * leitura indisponivel (D7), avisando o modelo so na primeira vez.
243
+ */
244
+ export function indisponivelSemRodada(vigia: FreioVigia, motivo: string, agora: number): Required<FreioVigia> {
245
+ const v = comConferencia(vigia)
246
+ return {
247
+ ...v, fila: [], pausaAte: agora + PAUSA_SEM_CONFERENCIA_MS, indisponivel: motivo,
248
+ entrega: v.avisouIndisponivel ? v.entrega : { ...v.entrega, indisponivel: motivo }, avisouIndisponivel: true,
249
+ }
250
+ }
251
+
252
+ /** Tira a entrega da vigia para o lote devolver: a vigia fica com a entrega vazia e lembra as falhas avisadas. */
253
+ export function tomarEntrega(vigia: FreioVigia): { vigia: Required<FreioVigia>; entrega: FreioEntrega } {
254
+ const v = comConferencia(vigia)
255
+ const avisadas = [...new Set([...v.avisadas, ...v.entrega.falhas.map(f => f.id)])]
256
+ return { vigia: { ...v, entrega: entregaVazia(), avisadas }, entrega: v.entrega }
257
+ }
258
+
259
+ /** O fim da saida de uma claim: as ultimas linhas e o ultimo trecho, dentro dos tetos (D8). */
260
+ export function fimDaSaida(fim: string): string[] {
261
+ let texto = fim.replace(/\s+$/, '')
262
+ if (texto.length > TETO_DE_CARACTERES_DA_SAIDA) texto = texto.slice(-TETO_DE_CARACTERES_DA_SAIDA)
263
+ return texto.split('\n').slice(-TETO_DE_LINHAS_DA_SAIDA)
264
+ }
265
+
266
+ /** O texto inteiro de um lote dentro do teto (D8), com a marca de corte. */
267
+ export function limitarTexto(texto: string): string {
268
+ return texto.length > TETO_DO_TEXTO ? `${texto.slice(0, TETO_DO_TEXTO - 40)}\n(texto cortado pelo Freio de Lote)` : texto
269
+ }
270
+
271
+ const lista = (ids: readonly string[]) => ids.join(', ')
272
+
273
+ /** O texto que o lote devolve ao modelo (D8); sem novidade, nenhum texto. */
274
+ export function textoDaEntrega(e: FreioEntrega): string {
275
+ const partes: string[] = []
276
+ const n = e.falhas.length
277
+ if (n > 0) {
278
+ partes.push(`Freio de Lote: a conferencia do nucleo reprovou ${n === 1 ? '1 claim ligada' : `${n} claims ligadas`} ao que voce mexeu (ork claims conferir, no sandbox do verify, sem recibo).`)
279
+ for (const f of e.falhas.slice(0, TETO_DE_CLAIMS_NO_TEXTO)) {
280
+ partes.push(`- ${f.id}${f.arquivo ? ` (${f.arquivo})` : ''}: ${f.alegacao}`)
281
+ partes.push(` comando: ${f.comando}`)
282
+ partes.push(` saiu com codigo ${f.codigo ?? 'desconhecido'}; fim da saida:`)
283
+ for (const linha of fimDaSaida(f.fim)) partes.push(` | ${linha}`)
284
+ }
285
+ if (n > TETO_DE_CLAIMS_NO_TEXTO) partes.push(`- e mais ${n - TETO_DE_CLAIMS_NO_TEXTO}: ${lista(e.falhas.slice(TETO_DE_CLAIMS_NO_TEXTO).map(f => f.id))}`)
286
+ partes.push('Rode o comando para confirmar: teste que cria processo pode reprovar no sandbox e passar no ork verify, que continua sendo quem verifica.')
287
+ }
288
+ if (e.recuperadas.length > 0) {
289
+ partes.push(`Freio de Lote: ${lista(e.recuperadas)} ${e.recuperadas.length === 1 ? 'voltou' : 'voltaram'} a passar na conferencia do nucleo.`)
290
+ }
291
+ if (e.semVeredito.length > 0) {
292
+ const motivos = [...new Set(e.semVeredito.map(s => s.motivo))]
293
+ partes.push(`Freio de Lote: a conferencia de ${lista(e.semVeredito.map(s => s.id))} terminou sem veredito (${motivos.join(', ')}); o ork verify decide.`)
294
+ }
295
+ if (e.indisponivel) {
296
+ partes.push(`Freio de Lote: a conferencia das claims esta indisponivel nesta sessao (${e.indisponivel}). O Freio segue sem conferir; o ork verify continua valendo.`)
297
+ }
298
+ return limitarTexto(partes.join('\n'))
299
+ }
300
+
301
+ /** A linha de status do Freio (D8); `undefined` limpa a linha. */
302
+ export function textoDoStatus(vigia: FreioVigia, agora: number): string | undefined {
303
+ const v = comConferencia(vigia)
304
+ if (v.indisponivel && agora < v.pausaAte) return 'freio: conferencia indisponivel'
305
+ if (v.rodada) return `freio: conferindo ${lista(v.rodada.ids)}`
306
+ const n = v.emRisco.length
307
+ if (n > 0) return `freio: ${n === 1 ? '1 claim' : `${n} claims`} em risco (${lista(v.emRisco.slice(0, 5))}${n > 5 ? ', ...' : ''})`
308
+ return undefined
309
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "modules": ["./register.ts"]
3
+ }