@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,253 @@
1
+ // A fila da Mesa do Dono (RM-059 F5), em funcoes puras: o que o pulse, o retrato e o `ork_hitl_pending` do nucleo
2
+ // trazem vira pergunta, parada ou decisao, e a faixa sai daqui. So importa tipos: o register.tsx le o nucleo pela
3
+ // ponte e entrega os objetos a estas funcoes. Nada aqui responde pedido nem escolhe opcao pelo dono.
4
+ import type { MesaAlternativa, MesaCanal, MesaDecisao, MesaFila, MesaObvia, MesaOutro, MesaPergunta } from '../types'
5
+
6
+ /** Perguntas por pagina: o lote de ate cinco da RM-041 e da RM-057. */
7
+ export const TAMANHO_DO_LOTE = 5
8
+ /** Decisoes informadas que o painel mostra por inteiro; o resto vira uma linha com a contagem. */
9
+ export const MAXIMO_DE_DECISOES = 5
10
+ /** Paradas e impedimentos que o painel mostra por inteiro. */
11
+ export const MAXIMO_DE_OUTROS = 5
12
+ /** So a decisao das ultimas 24 h que a Mesa ainda nao mostrou conta como nova. */
13
+ export const JANELA_DE_NOVAS_MS = 24 * 60 * 60 * 1000
14
+ export const TETO_DO_TEXTO = 200
15
+ export const TETO_DA_FAIXA = 200
16
+ const ID = /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,79}$/
17
+
18
+ export const FILA_VAZIA: MesaFila = { perguntas: [], outros: [], decisoes: [], totalDeDecisoes: 0, lidoEm: null, erro: null, avisos: [] }
19
+
20
+ type Bruto = Record<string, unknown>
21
+ const objeto = (v: unknown): v is Bruto => !!v && typeof v === 'object' && !Array.isArray(v)
22
+ const texto = (v: unknown): string => (typeof v === 'string' ? v : '')
23
+ const textoOuNulo = (v: unknown): string | null => (typeof v === 'string' && v.trim() !== '' ? v : null)
24
+
25
+ /** Uma linha so, sem quebra e sem espaco repetido, cortada no teto. */
26
+ export function umaLinha(v: unknown, teto = TETO_DO_TEXTO): string {
27
+ const t = texto(v).replace(/\s+/g, ' ').trim()
28
+ return t.length <= teto ? t : `${t.slice(0, teto - 1).trimEnd()}…`
29
+ }
30
+
31
+ /** Quanto tempo, como o dono le: segundos, minutos, horas e dias. */
32
+ export function duracao(ms: number): string {
33
+ const s = Math.max(0, Math.floor(ms / 1000))
34
+ if (s < 60) return `${s} s`
35
+ const min = Math.floor(s / 60)
36
+ if (min < 60) return `${min} min`
37
+ const h = Math.floor(min / 60)
38
+ if (h < 48) return `${h} h`
39
+ return `${Math.floor(h / 24)} dias`
40
+ }
41
+
42
+ /** O texto que um JSON traz, ou `undefined` quando nao e JSON. */
43
+ function lerJson(t: string): unknown {
44
+ try {
45
+ return JSON.parse(t)
46
+ } catch {
47
+ return undefined
48
+ }
49
+ }
50
+
51
+ /** O JSON que uma ferramenta MCP do nucleo devolve no primeiro bloco de texto; erro vira excecao com o motivo. */
52
+ export function jsonDoMcp(r: unknown): unknown {
53
+ const bruto = (objeto(r) ? r : {}) as { content?: unknown; isError?: unknown }
54
+ const blocos = Array.isArray(bruto.content) ? bruto.content.filter(objeto) : []
55
+ const corpo = blocos.find(c => c.type === 'text' && typeof c.text === 'string')?.text as string | undefined
56
+ if (bruto.isError === true) {
57
+ const j = corpo === undefined ? undefined : lerJson(corpo)
58
+ throw new Error(umaLinha(objeto(j) && typeof j.erro === 'string' ? j.erro : corpo ?? 'o nucleo respondeu com erro'))
59
+ }
60
+ if (corpo === undefined) throw new Error('o nucleo nao devolveu texto')
61
+ const j = lerJson(corpo)
62
+ if (j === undefined) throw new Error('o nucleo devolveu texto que nao e JSON')
63
+ return j
64
+ }
65
+
66
+ /** As alternativas de um pedido: letras no `ork.hitl/v2`, numeros no `ork.hitl/v1`, como o nucleo as declara. */
67
+ export function alternativasDoPedido(p: Bruto): MesaAlternativa[] {
68
+ if (p.contrato === 'ork.hitl/v2' && Array.isArray(p.alternativas)) {
69
+ return p.alternativas.filter(objeto).map(a => ({
70
+ chave: texto(a.letra), texto: umaLinha(a.texto), acao: texto(a.acao), consequencia: textoOuNulo(a.consequencia),
71
+ recomendada: a.recomendada === true, porque: textoOuNulo(a.porque),
72
+ }))
73
+ }
74
+ if (Array.isArray(p.opcoes)) {
75
+ return p.opcoes.filter(objeto).map(o => ({
76
+ chave: typeof o.numero === 'number' ? String(o.numero) : texto(o.numero), texto: umaLinha(o.texto), acao: texto(o.acao),
77
+ consequencia: null, recomendada: false, porque: null,
78
+ }))
79
+ }
80
+ return []
81
+ }
82
+
83
+ /**
84
+ * A pergunta de um pedido aberto (`ork.hitl/v1`, ou `ork.hitl/v2` de classe `pergunta`); `null` para o que nao e
85
+ * pergunta (a decisao informada nao pede nada) ou nao tem identificacao valida.
86
+ */
87
+ export function perguntaDoPedido(pedido: unknown, extra: { desdeEm?: string | null; reserva?: string | null } = {}): MesaPergunta | null {
88
+ if (!objeto(pedido)) return null
89
+ const v2 = pedido.contrato === 'ork.hitl/v2'
90
+ if (!v2 && pedido.contrato !== 'ork.hitl/v1') return null
91
+ if (v2 && pedido.classe !== 'pergunta') return null
92
+ const alvo = objeto(pedido.alvo) ? pedido.alvo.tipo : undefined
93
+ if (alvo !== 'gate' && alvo !== 'session') return null
94
+ const thread = texto(pedido.thread)
95
+ const id = texto(pedido.id)
96
+ if (!ID.test(thread) || !ID.test(id)) return null
97
+ const alternativas = alternativasDoPedido(pedido)
98
+ const recomendada = alternativas.find(a => a.recomendada)
99
+ const codigo = v2 ? textoOuNulo(pedido.codigo) : null
100
+ return {
101
+ thread, fase: textoOuNulo(pedido.fase), modo: textoOuNulo(pedido.modo), pedidoId: id, codigo,
102
+ contrato: v2 ? 'ork.hitl/v2' : 'ork.hitl/v1', alvo, motivo: texto(pedido.motivo), pergunta: umaLinha(pedido.pergunta),
103
+ recomendacao: v2 ? (recomendada ? `${recomendada.chave}) ${recomendada.texto}` : '') : umaLinha(pedido.recomendacao),
104
+ alternativas, irreversivel: pedido.irreversivel === true, ato: textoOuNulo(pedido.ato),
105
+ tipoDeResposta: v2 ? textoOuNulo(pedido.tipoDeResposta) : null,
106
+ respostaAceita: objeto(pedido.respostaAceita) ? textoOuNulo(pedido.respostaAceita.tipo) : null,
107
+ dependenciaTecnica: pedido.dependenciaTecnica !== undefined, acaoAoExpirar: textoOuNulo(pedido.acaoPadraoAoExpirar),
108
+ prazo: textoOuNulo(pedido.prazo), prazoLocal: null, desdeEm: extra.desdeEm ?? textoOuNulo(pedido.criadoEm),
109
+ estado: 'desconhecido', conferida: false, canalClaudeCode: 'desconhecido',
110
+ reserva: extra.reserva ?? (codigo && recomendada ? `${codigo} ${recomendada.chave}` : null),
111
+ }
112
+ }
113
+
114
+ /** As chaves que a Mesa ja registrou na ponte por um canal (`decisao:` no painel, `hitl:` na notificacao), sem o prefixo. */
115
+ function chavesDaMesa(registro: unknown, prefixo: string, canal?: string): Set<string> {
116
+ const itens = objeto(registro) && Array.isArray(registro.itens) ? registro.itens.filter(objeto) : []
117
+ const chaves = new Set<string>()
118
+ for (const i of itens) {
119
+ const chave = texto(i.chave)
120
+ if (i.mod === 'ork-mesa' && chave.startsWith(prefixo) && (canal === undefined || i.canal === canal)) chaves.add(chave.slice(prefixo.length))
121
+ }
122
+ return chaves
123
+ }
124
+
125
+ /** Os ids das decisoes que a Mesa ja mostrou ao dono, pelo registro do mostrado da ponte. */
126
+ export function decisoesVistas(registro: unknown): Set<string> {
127
+ return chavesDaMesa(registro, 'decisao:')
128
+ }
129
+
130
+ /** Os pedidos que a Mesa ja levou ao dono por notificacao, pelo registro da ponte: valem depois de uma recarga do modulo. */
131
+ export function pedidosNotificados(registro: unknown): Set<string> {
132
+ return chavesDaMesa(registro, 'hitl:', 'notificacao')
133
+ }
134
+
135
+ /** A ordem das perguntas: o prazo mais perto primeiro, depois a mais antiga; sem prazo vai para o fim. */
136
+ export function ordenarPerguntas(perguntas: readonly MesaPergunta[]): MesaPergunta[] {
137
+ const prazo = (p: MesaPergunta) => {
138
+ const t = Date.parse(p.prazo ?? '')
139
+ return Number.isFinite(t) ? t : Number.POSITIVE_INFINITY
140
+ }
141
+ const desde = (p: MesaPergunta) => {
142
+ const t = Date.parse(p.desdeEm ?? '')
143
+ return Number.isFinite(t) ? t : Number.POSITIVE_INFINITY
144
+ }
145
+ return [...perguntas].sort((a, b) => prazo(a) - prazo(b) || desde(a) - desde(b) || a.pedidoId.localeCompare(b.pedidoId))
146
+ }
147
+
148
+ /**
149
+ * A fila a partir do `ork pulse --json`: as perguntas com pedido, as paradas e os impedimentos de
150
+ * `precisaDeHumanoAgora`, e as decisoes que a fabrica tomou pelo dono na janela do pulse.
151
+ */
152
+ export function filaDoPulse(pulse: unknown, vistas: ReadonlySet<string>, agora: number): Omit<MesaFila, 'lidoEm' | 'erro' | 'avisos'> {
153
+ const p = objeto(pulse) ? pulse : {}
154
+ const humanos = Array.isArray(p.precisaDeHumanoAgora) ? p.precisaDeHumanoAgora.filter(objeto) : []
155
+ const perguntas: MesaPergunta[] = []
156
+ const outros: MesaOutro[] = []
157
+ for (const item of humanos) {
158
+ const pergunta = item.pedido === undefined ? null
159
+ : perguntaDoPedido(item.pedido, { desdeEm: textoOuNulo(item.desdeEm), reserva: textoOuNulo(item.comandoResposta) })
160
+ if (pergunta) {
161
+ if (!perguntas.some(x => x.pedidoId === pergunta.pedidoId)) perguntas.push(pergunta)
162
+ continue
163
+ }
164
+ if (item.classe === 'score_pendente') continue
165
+ const impedimento = objeto(item.impedimento) ? item.impedimento : null
166
+ outros.push({
167
+ chave: umaLinha(item.id, 160), thread: textoOuNulo(item.thread), fase: textoOuNulo(item.fase), motivo: texto(item.motivo),
168
+ texto: umaLinha(item.pergunta), recomendacao: umaLinha(item.recomendacao),
169
+ comando: umaLinha(impedimento ? impedimento.comando : item.comandoResposta), desdeEm: textoOuNulo(item.desdeEm),
170
+ impedimento: impedimento !== null,
171
+ })
172
+ }
173
+ const brutas = Array.isArray(p.decisoes) ? p.decisoes.filter(objeto) : []
174
+ // A decisao que outra desfez continua na janela, marcada como revertida, e nunca conta como nova.
175
+ const revertidaPor = new Map<string, string>()
176
+ for (const d of brutas) if (typeof d.reverte === 'string' && ID.test(d.reverte)) revertidaPor.set(d.reverte, texto(d.id))
177
+ const decisoes: MesaDecisao[] = brutas.flatMap(d => {
178
+ const id = texto(d.id)
179
+ if (!ID.test(id)) return []
180
+ const custo = objeto(d.custoDeReverter) ? d.custoDeReverter : {}
181
+ const em = texto(d.em)
182
+ const recente = Number.isFinite(Date.parse(em)) && agora - Date.parse(em) <= JANELA_DE_NOVAS_MS
183
+ const desfeita = revertidaPor.get(id) ?? null
184
+ return [{
185
+ id, thread: texto(d.thread), fase: texto(d.fase), em, decidido: umaLinha(d.decidido), porque: umaLinha(d.porque),
186
+ comoMudar: umaLinha(d.comoMudar), custoAgora: umaLinha(custo.agora, 140), custoDepois: umaLinha(custo.depois, 140),
187
+ quemDecidiu: umaLinha(d.quemDecidiu, 120), reverte: typeof d.reverte === 'string' && ID.test(d.reverte) ? d.reverte : null,
188
+ revertidaPor: desfeita, nova: recente && desfeita === null && !vistas.has(id),
189
+ }]
190
+ })
191
+ decisoes.sort((a, b) => Number(b.nova) - Number(a.nova) || b.em.localeCompare(a.em))
192
+ return { perguntas: ordenarPerguntas(perguntas), outros, decisoes, totalDeDecisoes: decisoes.length }
193
+ }
194
+
195
+ /**
196
+ * A assinatura do retrato da secao `hitl`: cada pedido com o estado dele. Muda quando um pedido abre, e respondido,
197
+ * vence ou fica velho, e e o sinal barato para reler o pulse.
198
+ */
199
+ export function assinaturaDoRetrato(retrato: unknown): string {
200
+ const secoes = objeto(retrato) && objeto(retrato.sections) ? retrato.sections : {}
201
+ const hitl = objeto(secoes.hitl) ? secoes.hitl : {}
202
+ const itens = Array.isArray(hitl.items) ? hitl.items.filter(objeto) : []
203
+ return itens.map(i => `${texto(i.id)}:${texto(i.status)}`).sort().join(',')
204
+ }
205
+
206
+ /**
207
+ * Confere a pergunta no `ork_hitl_pending` da thread: o pedido de agora, o estado, o prazo local e a oferta do canal
208
+ * `claude-code`. Pedido que o nucleo nao lista mais saiu da fila (respondido, substituido ou de outra fase): `null`.
209
+ */
210
+ export function conferirNoNucleo(pergunta: MesaPergunta, resposta: unknown): MesaPergunta | null {
211
+ const pendencias = objeto(resposta) && Array.isArray(resposta.pendencias) ? resposta.pendencias.filter(objeto) : []
212
+ const achada = pendencias.find(x => objeto(x.pedido) && x.pedido.id === pergunta.pedidoId)
213
+ if (!achada) return null
214
+ const fresca = perguntaDoPedido(achada.pedido, { desdeEm: pergunta.desdeEm, reserva: pergunta.reserva })
215
+ if (!fresca) return null
216
+ const canais = Array.isArray(achada.canais) ? achada.canais.filter(objeto) : []
217
+ const claudeCode = canais.find(c => c.canal === 'claude-code')
218
+ const canal: MesaCanal = claudeCode?.estado === 'disponivel' ? 'disponivel' : 'indisponivel'
219
+ return { ...fresca, estado: textoOuNulo(achada.estado) ?? 'desconhecido', conferida: true, prazoLocal: textoOuNulo(achada.prazoLocal),
220
+ canalClaudeCode: canal }
221
+ }
222
+
223
+ /** As threads que tem pergunta na fila, sem repetir, na ordem da fila. */
224
+ export function threadsComPergunta(perguntas: readonly MesaPergunta[]): string[] {
225
+ return [...new Set(perguntas.map(p => p.thread))]
226
+ }
227
+
228
+ /**
229
+ * O que espera o dono de fato: a pergunta que a Mesa decidiu pelo nucleo nao conta, nem a que ainda confere no nucleo
230
+ * se a decisao chegou (ela volta a esperar o dono se a leitura seguinte nao a mostrar).
231
+ */
232
+ export function esperamODono(fila: MesaFila, obvias: Readonly<Record<string, MesaObvia>>): MesaPergunta[] {
233
+ return fila.perguntas.filter(p => {
234
+ const estado = obvias[p.pedidoId]?.estado
235
+ return estado !== 'registrada' && estado !== 'incerta'
236
+ })
237
+ }
238
+
239
+ /**
240
+ * A linha da Mesa na faixa: quantos pedidos esperam o dono (com a thread do primeiro), as paradas e as decisoes novas
241
+ * tomadas por ele. Sem idade no texto, para a linha so mudar quando a fila muda. Sem nada, a linha sai.
242
+ */
243
+ export function textoDaFaixa(fila: MesaFila, obvias: Readonly<Record<string, MesaObvia>>): string | undefined {
244
+ if (fila.lidoEm === null) return undefined
245
+ const partes: string[] = []
246
+ const esperam = esperamODono(fila, obvias)
247
+ if (esperam.length) partes.push(`${esperam.length} ${esperam.length === 1 ? 'espera' : 'esperam'} você (${esperam[0]?.thread})`)
248
+ if (fila.outros.length) partes.push(`${fila.outros.length} ${fila.outros.length === 1 ? 'parada' : 'paradas'}`)
249
+ const novas = fila.decisoes.filter(d => d.nova).length
250
+ if (novas) partes.push(`${novas} ${novas === 1 ? 'decidida' : 'decididas'} sem te chamar`)
251
+ if (!partes.length) return undefined
252
+ return umaLinha(`mesa: ${partes.join(' · ')}`, TETO_DA_FAIXA)
253
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "modules": ["./register.tsx"]
3
+ }
@@ -0,0 +1,98 @@
1
+ // O teste das quatro condicoes da decisao obvia (M4 do dono, RM-059; D5 da thread ork-rm059mesa), em funcao pura.
2
+ // E o mesmo teste do nucleo (`classificarDecisao` em core/src/hitl-contract.ts, I-41): as portas fechadas primeiro
3
+ // (irreversivel, dinheiro, escopo ou produto), depois as quatro condicoes na ordem do GOAL. A Mesa so calcula os sinais
4
+ // a partir do pedido que o nucleo serve; o teste do nucleo confere que a classe sai igual a do `classificarDecisao`.
5
+ // Decidir, para a Mesa, e registrar a decisao autonoma do nucleo (`ork_decision_record`): nunca responder o pedido. Um
6
+ // pedido aberto so se resolve pela resposta humana, pela substituicao, pelo prazo com `seguir-recomendada` ou pelo
7
+ // fechamento da thread; por isso o criterio escrito que a Mesa le pelo nucleo e o proprio pedido declarar que segue a
8
+ // recomendada no prazo. Falhou uma condicao, e pergunta. So importa tipos.
9
+ import type { MesaAlternativa, MesaPergunta } from '../types'
10
+
11
+ /** As portas fechadas e as quatro condicoes, na ordem do `classificarDecisao` do nucleo. */
12
+ export type Condicao = 'irreversivel' | 'dinheiro' | 'escopo' | 'criterio' | 'alternativas' | 'reversivel' | 'barato'
13
+ export const CONDICOES: readonly Condicao[] = ['irreversivel', 'dinheiro', 'escopo', 'criterio', 'alternativas', 'reversivel', 'barato']
14
+
15
+ /** Como o cartao diz o que impediu a decisao obvia. */
16
+ export const FALHA_DA_CONDICAO: Readonly<Record<Condicao, string>> = {
17
+ irreversivel: 'o ato é irreversível',
18
+ dinheiro: 'o ato gasta dinheiro',
19
+ escopo: 'é decisão de escopo ou produto (GOAL, PLAN ou política)',
20
+ criterio: 'não há critério escrito que a Mesa leia pelo núcleo',
21
+ alternativas: 'as alternativas não são estritamente piores pelo critério',
22
+ reversivel: 'não dá para desfazer antes da entrega',
23
+ barato: 'errar não sai barato, ou só o dono resolve',
24
+ }
25
+
26
+ /** As fases antes da entrega: depois do CHECK, o que se decide vai para a `main`. */
27
+ export const FASES_ANTES_DA_ENTREGA: readonly string[] = ['GOAL', 'PLAN', 'GO', 'CHECK']
28
+ /**
29
+ * GOAL e PLAN decidem objetivo, premissa e plano: o que se pergunta ali muda escopo ou produto, e e sempre do dono
30
+ * (M4). Por isso o nucleo abre esses pedidos em profundidade `profunda` (`profundidadeDoPedido`).
31
+ */
32
+ export const FASES_DE_ESCOPO: readonly string[] = ['GOAL', 'PLAN']
33
+ /** Credencial so o dono entrega; o erro de decidir por ele nao e barato. */
34
+ export const MOTIVOS_SO_DO_DONO: readonly string[] = ['hitl.credencial']
35
+
36
+ /** Os sinais do teste, na forma do `SinaisDaDecisao` do nucleo. */
37
+ export type SinaisDaObvia = {
38
+ criterio?: { tipo: 'medicao'; referencia: string }
39
+ alternativasEstritamentePiores: boolean
40
+ reversivelAntesDaEntrega: boolean
41
+ erroBaratoEDetectavel: boolean
42
+ irreversivel: boolean
43
+ gastaDinheiro: boolean
44
+ mudaEscopoOuProduto: boolean
45
+ }
46
+
47
+ export type ResultadoDaObvia =
48
+ | { obvia: true; alternativa: MesaAlternativa; referencia: string }
49
+ | { obvia: false; falhou: Condicao[] }
50
+
51
+ /**
52
+ * A referencia do criterio que a Mesa cita: a medicao como comando que o CHECK roda contra o ledger, onde o pedido fica
53
+ * escrito para sempre (sai 0 so se o pedido declara seguir a recomendada no prazo).
54
+ */
55
+ export function referenciaDoCriterio(p: MesaPergunta): string {
56
+ return `grep -F '"id":"${p.pedidoId}"' .orkastery/threads/${p.thread}/ledger.jsonl | grep -qF '"acaoPadraoAoExpirar":"seguir-recomendada"'`
57
+ }
58
+
59
+ /** Os sinais a partir do pedido servido pelo nucleo e do instante da leitura, em ms. */
60
+ export function sinaisDaPergunta(p: MesaPergunta, agora: number): SinaisDaObvia {
61
+ const recomendadas = p.alternativas.filter(a => a.recomendada).length
62
+ const prazo = Date.parse(p.prazo ?? '')
63
+ return {
64
+ // Criterio escrito: o pedido v2 declara, no ledger, que a fabrica segue a recomendada no prazo.
65
+ ...(p.contrato === 'ork.hitl/v2' && p.acaoAoExpirar === 'seguir-recomendada' ? { criterio: { tipo: 'medicao', referencia: referenciaDoCriterio(p) } } : {}),
66
+ // Estritamente piores pelo criterio: ha escolha, e o criterio aponta uma so.
67
+ alternativasEstritamentePiores: p.alternativas.length >= 2 && recomendadas === 1,
68
+ // Desfaz antes da entrega: a fase ainda nao entrega (o ato sem volta ja fecha a porta antes).
69
+ reversivelAntesDaEntrega: FASES_ANTES_DA_ENTREGA.includes(p.fase ?? ''),
70
+ // Barato e detectavel: o pedido conferido agora no nucleo (sem isso a evidencia seria inventada), gate (responder
71
+ // sessao e falar pelo dono), escolha objetiva, nada que so o dono resolve, o pedido ainda aberto e o prazo por vir,
72
+ // para o dono poder mudar antes.
73
+ erroBaratoEDetectavel: p.conferida && p.alvo === 'gate' && p.tipoDeResposta === 'objetiva' && p.respostaAceita === 'opcao' &&
74
+ !p.dependenciaTecnica && !MOTIVOS_SO_DO_DONO.includes(p.motivo) && p.estado === 'aberto' && Number.isFinite(prazo) && prazo > agora,
75
+ irreversivel: p.irreversivel || p.ato !== null,
76
+ gastaDinheiro: p.ato === 'dinheiro' || p.motivo === 'cost.violation',
77
+ mudaEscopoOuProduto: p.motivo === 'policy.violation' || FASES_DE_ESCOPO.includes(p.fase ?? ''),
78
+ }
79
+ }
80
+
81
+ /**
82
+ * O teste: as portas fechadas e as quatro condicoes, na ordem do nucleo. Devolve a alternativa recomendada e a
83
+ * referencia do criterio quando a decisao e obvia, ou tudo que falhou, na ordem.
84
+ */
85
+ export function testeDaObvia(p: MesaPergunta, agora: number): ResultadoDaObvia {
86
+ const s = sinaisDaPergunta(p, agora)
87
+ const falhou: Condicao[] = []
88
+ if (s.irreversivel) falhou.push('irreversivel')
89
+ if (s.gastaDinheiro) falhou.push('dinheiro')
90
+ if (s.mudaEscopoOuProduto) falhou.push('escopo')
91
+ if (!s.criterio) falhou.push('criterio')
92
+ if (!s.alternativasEstritamentePiores) falhou.push('alternativas')
93
+ if (!s.reversivelAntesDaEntrega) falhou.push('reversivel')
94
+ if (!s.erroBaratoEDetectavel) falhou.push('barato')
95
+ const recomendada = p.alternativas.find(a => a.recomendada)
96
+ if (falhou.length || !s.criterio || !recomendada) return { obvia: false, falhou: falhou.length ? falhou : ['alternativas'] }
97
+ return { obvia: true, alternativa: recomendada, referencia: s.criterio.referencia }
98
+ }