@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,339 @@
1
+ // O Portão de Tarefas (RM-059, fatia F10). Na sessão de fase do GO, o quadro de tarefas do Claude Code é o do
2
+ // plano: a tarefa nativa só entra apontando uma tarefa do plano com `touch_paths` e verify que o núcleo confere
3
+ // (T10.1), só fecha com essa conferência aprovada (T10.2) e, com agent teams no piloto do núcleo, vai ao teammate
4
+ // ocioso quando a região dela está livre (T10.3). O Portão lê o núcleo pelo `$.ork` da ponte e pela ferramenta de
5
+ // leitura do plano, nunca o estado interno do disco; age só por comando do `ork`; nunca roda o verify por conta
6
+ // própria, nunca toma lease e nunca fala pelo dono. A lógica fica nos módulos puros ao lado; aqui só as chamadas ao
7
+ // `$`, sempre escritas como `x.noun.metodo(...)`, como o validador exige.
8
+ import type { EngineInterface, Register } from 'claude-code'
9
+ import type { PortaoQuadro, PortaoTarefa } from '../types'
10
+ import {
11
+ argsDoPlano, decidirCriacao, escopoDoPlano, escopoDoRetrato, FERRAMENTA_DO_PLANO, IDADE_DO_PLANO_MS, IDADE_SEM_PLANO_MS,
12
+ idsNoAssunto, jsonDoMcp, SERVIDOR, serveAoQuadro, tarefasDoPlano, textoDaRecusa, textoDoPlano, threadNoRetrato,
13
+ } from './plano'
14
+ import {
15
+ argvDaConferencia, assinatura, contarRepeticao, lerConferencia, textoDeOutraConferencia, textoDoBloqueio, textoDoStatus,
16
+ VIGIA_DA_CONFERENCIA_MS,
17
+ } from './verify'
18
+ import type { Leitura } from './verify'
19
+ import {
20
+ escolherTarefa, nomeDoTeammate, paginaDosLeases, recusaDoTeammate, TETO_DE_PAGINAS_DE_LEASES, tetosDoAmbiente,
21
+ textoDaEntrega, VIVOS,
22
+ } from './lease'
23
+ import type { LeaseAtivo } from './lease'
24
+
25
+ const QUADRO = { plugin: 'ork-portao', key: 'quadro' } as const
26
+
27
+ /**
28
+ * O quadro desta carga do módulo (D9 do plano da thread): lido do `$.state` uma vez por thread e mudado só por
29
+ * `mudarQuadro`, que aplica a mudança na versão mais nova sem esperar nada no meio e então grava sem versão. Um gancho
30
+ * que espera uma chamada de `$` deixa outro gancho rodar: mudança feita sobre uma cópia lida antes apagaria a dele.
31
+ */
32
+ let quadro: PortaoQuadro | null = null
33
+ /** O plano lido e quando, por thread; relido depois da idade (D3). */
34
+ let plano: { thread: string; em: number; tarefas: PortaoTarefa[] | null } | null = null
35
+ /**
36
+ * A tarefa do plano que o Portão está conferindo no núcleo agora (D5): uma conferência por vez. Fica no módulo, não no
37
+ * `$.state`: uma troca do worker mata o processo junto com a marca.
38
+ */
39
+ let conferindo: string | null = null
40
+
41
+ /** A sessão de fase com thread, pelo contexto da ponte; qualquer outra sessão fica sem Portão (D1). */
42
+ type ContextoDeFase = { thread: string; raiz: string }
43
+ /**
44
+ * A sessão em que o Portão age (D1): o contexto e as tarefas do plano, ou `null` nelas quando o plano não serve ao quadro
45
+ * (ausente, sem tarefa que sirva ou com a leitura falhando). Sem elas, a criação e o teammate passam, mas a tarefa
46
+ * registrada na criação continua conferida na conclusão: o núcleo lê o plano por conta própria.
47
+ */
48
+ type Sessao = { ctx: ContextoDeFase; tarefas: PortaoTarefa[] | null }
49
+
50
+ const mensagem = (erro: unknown) => (erro instanceof Error ? erro.message : String(erro)).slice(0, 300)
51
+
52
+ function quadroVazio(thread: string): PortaoQuadro {
53
+ return { thread, tarefas: {}, concluidas: [], repeticoes: {}, atribuicoes: {}, devolvidas: [], ultimaFalha: null }
54
+ }
55
+
56
+ async function contextoDeFase($: EngineInterface): Promise<ContextoDeFase | null> {
57
+ const ctx = await $.ork.contexto()
58
+ return ctx.papel === 'fase' && ctx.thread ? { thread: ctx.thread, raiz: ctx.raiz } : null
59
+ }
60
+
61
+ /** O quadro da thread: o do módulo, ou o do `$.state` na primeira leitura (a que chegar primeiro vale). */
62
+ async function lerQuadro($: EngineInterface, thread: string): Promise<PortaoQuadro> {
63
+ if (quadro && quadro.thread === thread) return quadro
64
+ const lido = (await $.state.get(QUADRO)).value
65
+ if (quadro && quadro.thread === thread) return quadro
66
+ quadro = lido && lido.thread === thread ? { ...quadroVazio(thread), ...lido } : quadroVazio(thread)
67
+ return quadro
68
+ }
69
+
70
+ /**
71
+ * Aplica `mudar` ao quadro mais novo, sem esperar nada entre ler e trocar, e grava (D9). `mudar` que devolve o mesmo
72
+ * quadro não grava nada.
73
+ */
74
+ async function mudarQuadro($: EngineInterface, thread: string, mudar: (q: PortaoQuadro) => PortaoQuadro): Promise<PortaoQuadro> {
75
+ await lerQuadro($, thread)
76
+ const atual = quadro as PortaoQuadro
77
+ const novo = mudar(atual)
78
+ if (novo === atual) return atual
79
+ quadro = novo
80
+ await $.state.set(QUADRO, novo)
81
+ return novo
82
+ }
83
+
84
+ /** As tarefas do plano pela ferramenta de leitura do núcleo, com a idade da D3; erro sobe para quem chamou. */
85
+ async function lerTarefas($: EngineInterface, ctx: ContextoDeFase): Promise<PortaoTarefa[] | null> {
86
+ const agora = await $.clock.now()
87
+ if (plano && plano.thread === ctx.thread) {
88
+ const idade = agora - plano.em
89
+ const teto = plano.tarefas && plano.tarefas.length > 0 ? IDADE_DO_PLANO_MS : IDADE_SEM_PLANO_MS
90
+ if (idade >= 0 && idade < teto) return plano.tarefas
91
+ }
92
+ const r = await $.mcp.call(SERVIDOR, FERRAMENTA_DO_PLANO, argsDoPlano(ctx.thread))
93
+ const tarefas = tarefasDoPlano(textoDoPlano(jsonDoMcp(r, FERRAMENTA_DO_PLANO)))
94
+ plano = { thread: ctx.thread, em: agora, tarefas }
95
+ return tarefas
96
+ }
97
+
98
+ /**
99
+ * A sessão em que o Portão age (D1): papel `fase` com thread, thread não fechada e GO no bloco pelo retrato da ponte. O
100
+ * retrato que falha conta como GO e aberta, e fica na linha de status. As tarefas vêm do plano legível com ao menos uma
101
+ * tarefa que serve ao quadro; sem ele, `null`.
102
+ */
103
+ async function sessaoDoPortao($: EngineInterface): Promise<Sessao | null> {
104
+ const ctx = await contextoDeFase($)
105
+ if (!ctx) return null
106
+ let retrato: { status: string | null; bloco: string | null } | null
107
+ try {
108
+ retrato = threadNoRetrato((await $.ork.retrato({ thread: ctx.thread })).retrato, ctx.thread)
109
+ } catch {
110
+ retrato = null
111
+ }
112
+ const doRetrato = escopoDoRetrato(retrato)
113
+ if (!doRetrato.ativo) return null
114
+ if (doRetrato.avisos.length > 0) $.ui.status(`portão: ${doRetrato.avisos.join('; ')}`)
115
+ let tarefas: PortaoTarefa[] | null
116
+ try {
117
+ tarefas = await lerTarefas($, ctx)
118
+ } catch (erro) {
119
+ await anotarFalha($, 'leitura do plano', erro, ctx.thread)
120
+ return { ctx, tarefas: null }
121
+ }
122
+ return { ctx, tarefas: escopoDoPlano(tarefas).ativo ? tarefas : null }
123
+ }
124
+
125
+ /**
126
+ * Guarda a falha do próprio Portão no quadro e a diz uma vez no transcript (não vai ao modelo) e na linha de
127
+ * status: nenhuma captura fica muda.
128
+ */
129
+ async function anotarFalha($: EngineInterface, onde: string, erro: unknown, thread?: string): Promise<void> {
130
+ const motivo = mensagem(erro)
131
+ $.ui.status(`portão: falhou em ${onde}`)
132
+ const alvo = thread ?? quadro?.thread
133
+ if (!alvo) {
134
+ $.ui.log(`Portão de Tarefas: falhou em ${onde}: ${motivo}`)
135
+ return
136
+ }
137
+ const em = await $.clock.now()
138
+ let nova = false
139
+ await mudarQuadro($, alvo, q => {
140
+ if (q.ultimaFalha?.onde === onde && q.ultimaFalha.motivo === motivo) return q
141
+ nova = true
142
+ return { ...q, ultimaFalha: { onde, motivo, em } }
143
+ })
144
+ if (nova) $.ui.log(`Portão de Tarefas: falhou em ${onde}: ${motivo}`)
145
+ }
146
+
147
+ /**
148
+ * A tarefa do plano de uma tarefa nativa (D2): a registrada na criação vence (renomear não tira a tarefa do portão);
149
+ * sem registro, o assunto que cita uma tarefa do plano que serve ao quadro, e uma só. Fora disso, a tarefa não é do
150
+ * plano: a que o núcleo não confere nunca prende a conclusão.
151
+ */
152
+ function tarefaDoEvento(assunto: unknown, taskId: string, q: PortaoQuadro, tarefas: readonly PortaoTarefa[]): string | null {
153
+ const registrada = q.tarefas[taskId]
154
+ if (registrada) return registrada
155
+ const ids = idsNoAssunto(assunto).filter(id => tarefas.some(t => t.id === id && serveAoQuadro(t)))
156
+ return ids.length === 1 ? (ids[0] as string) : null
157
+ }
158
+
159
+ /**
160
+ * A conferência do verify da tarefa no núcleo (D4, D5): `ork claims conferir --tarefa` por `$.process.run`, na raiz
161
+ * da sessão, com o vigia do mod. A espera é chamada de `$` e não conta no orçamento do gancho.
162
+ */
163
+ async function rodarConferencia($: EngineInterface, ctx: ContextoDeFase, tarefa: string): Promise<Leitura> {
164
+ try {
165
+ const r = await $.process.run(argvDaConferencia(ctx.thread, tarefa), { cwd: ctx.raiz, timeoutMs: VIGIA_DA_CONFERENCIA_MS })
166
+ return lerConferencia(tarefa, r.stdout, r.exitCode, r.stderr)
167
+ } catch (erro) {
168
+ return { tipo: 'indisponivel', motivo: `ork claims conferir não rodou: ${mensagem(erro)}` }
169
+ }
170
+ }
171
+
172
+ /** Os tetos do piloto de agent teams que o núcleo passou no ambiente da sessão (D10); `null` sem eles. */
173
+ async function lerTetos($: EngineInterface): Promise<{ teammates: number; custoUsd: number } | null> {
174
+ // O validador pede o nome literal; o teste do núcleo confere que ele é o de ENV_TEAMMATES e ENV_CUSTO.
175
+ return tetosDoAmbiente(await $.env.get('ORK_AGENT_TEAMS_TEAMMATES'), await $.env.get('ORK_AGENT_TEAMS_CUSTO_USD'))
176
+ }
177
+
178
+ /** O custo da sessão como o `/cost` soma; `null` quando ele não vem. */
179
+ async function custoDaSessao($: EngineInterface): Promise<number | null> {
180
+ const uso = await $.session.usage()
181
+ return typeof uso.cost?.usd === 'number' ? uso.cost.usd : null
182
+ }
183
+
184
+ /**
185
+ * Os leases ativos de todas as threads (D8 revista), página a página pelo `nextOffset`. O servidor MCP da sessão de fase
186
+ * é fixado na thread e o retrato dele só traz os leases dela; por isso a leitura vai pela consulta `maestro` da lista
187
+ * fechada da ponte (`ork maestro --json --section leases --offset N`), que vê o projeto inteiro. Lança se uma página não
188
+ * vem ou se a leitura fica incompleta (lacuna de verdade, retrato em conflito, mais páginas que o teto): lista parcial
189
+ * nunca vira região livre.
190
+ */
191
+ async function lerLeases($: EngineInterface): Promise<LeaseAtivo[]> {
192
+ const leases: LeaseAtivo[] = []
193
+ let offset = 0
194
+ for (let pagina = 0; pagina < TETO_DE_PAGINAS_DE_LEASES; pagina++) {
195
+ const pedido = { consulta: 'maestro' as const, secao: 'leases' as const, offset }
196
+ const p = paginaDosLeases(await $.ork.ler(pedido))
197
+ if (p.lacuna) throw new Error(`ork-portao: ${p.lacuna}`)
198
+ leases.push(...p.leases)
199
+ if (p.proximo === null) return leases
200
+ offset = p.proximo
201
+ }
202
+ throw new Error(`ork-portao: a seção leases tem mais de ${TETO_DE_PAGINAS_DE_LEASES} páginas; o resto não foi lido`)
203
+ }
204
+
205
+ export const register: Register = on => {
206
+ // A criação (T10.1): a tarefa nativa só entra apontando uma tarefa do plano que serve ao quadro (D2, D3). A recusa
207
+ // volta ao modelo pelo `block`, com o motivo e as tarefas que servem; a que passa fica registrada pelo id nativo.
208
+ on('classic.TaskCreated', async ($, e, next) => {
209
+ const sessao = await sessaoDoPortao($)
210
+ if (!sessao || !sessao.tarefas) return next(e)
211
+ const veredito = decidirCriacao(e.task_subject, sessao.tarefas)
212
+ if (veredito.tipo === 'recusa') return { block: textoDaRecusa(veredito, e.task_subject, sessao.tarefas) }
213
+ await mudarQuadro($, sessao.ctx.thread, q => ({ ...q, tarefas: { ...q.tarefas, [String(e.task_id)]: veredito.tarefa } }))
214
+ return next(e)
215
+ }).catch(async ($, e, next) => {
216
+ // Na reentrada o gancho nem rodou e as chamadas de `$` recusam: só a falha de verdade é anotada.
217
+ if (next.error && next.error.kind !== 're-entry') await anotarFalha($, 'criação de tarefa', next.error.message ?? next.error.kind)
218
+ return next(e)
219
+ })
220
+
221
+ // A conclusão (T10.2): a tarefa do plano só fecha com a conferência do núcleo aprovada (D4), uma por vez (D5).
222
+ // Todo bloqueio volta ao modelo com o motivo e a saída (corrigir, ou devolver a tarefa para pending), e a terceira
223
+ // vez seguida com o mesmo resultado manda deixar a tarefa aberta (D6). Tarefa que não é do plano passa.
224
+ on('classic.TaskCompleted', async ($, e, next) => {
225
+ const sessao = await sessaoDoPortao($)
226
+ if (!sessao) return next(e)
227
+ const { ctx } = sessao
228
+ const tarefa = tarefaDoEvento(e.task_subject, String(e.task_id), await lerQuadro($, ctx.thread), sessao.tarefas ?? [])
229
+ if (!tarefa) return next(e)
230
+ if (conferindo) return { block: textoDeOutraConferencia(tarefa, conferindo) }
231
+ conferindo = tarefa
232
+ let leitura: Leitura
233
+ try {
234
+ $.ui.status(`portão: conferindo ${tarefa} no núcleo`)
235
+ leitura = await rodarConferencia($, ctx, tarefa)
236
+ } finally {
237
+ conferindo = null
238
+ }
239
+ if (leitura.tipo === 'aprovada') {
240
+ const resultado = await next(e)
241
+ if (!resultado.block) {
242
+ await mudarQuadro($, ctx.thread, q => {
243
+ const repeticoes = { ...q.repeticoes }
244
+ delete repeticoes[tarefa]
245
+ return { ...q, concluidas: q.concluidas.includes(tarefa) ? q.concluidas : [...q.concluidas, tarefa], repeticoes }
246
+ })
247
+ }
248
+ $.ui.status(textoDoStatus(tarefa, leitura))
249
+ return resultado
250
+ }
251
+ const marca = assinatura(leitura)
252
+ let vezes = 1
253
+ await mudarQuadro($, ctx.thread, q => {
254
+ const repeticao = contarRepeticao(q.repeticoes[tarefa], marca)
255
+ vezes = repeticao.vezes
256
+ return { ...q, repeticoes: { ...q.repeticoes, [tarefa]: repeticao } }
257
+ })
258
+ $.ui.status(textoDoStatus(tarefa, leitura))
259
+ return { block: textoDoBloqueio(tarefa, leitura, vezes) }
260
+ }).catch(async ($, e, next) => {
261
+ // A conclusão fecha só aprovada (D4): se o próprio Portão falhou no meio, a tarefa registrada, ou a que cita um id
262
+ // de tarefa do plano no assunto, não fecha. A anotação é melhor esforço: o bloqueio não depende dela.
263
+ if (!next.error || next.error.kind === 're-entry') return next(e)
264
+ const motivo = next.error.message ?? next.error.kind
265
+ try { await anotarFalha($, 'conclusão de tarefa', motivo) } catch { $.ui.status('portão: falhou na conclusão de tarefa') }
266
+ const ids = idsNoAssunto(e.task_subject)
267
+ const tarefa = quadro?.tarefas[String(e.task_id)] ?? (ids.length === 1 ? ids[0] : undefined)
268
+ if (!tarefa) return next(e)
269
+ return { block: textoDoBloqueio(tarefa, { tipo: 'indisponivel', motivo: `o Portão falhou ao conferir: ${motivo}` }) }
270
+ })
271
+
272
+ // O teammate ocioso (T10.3): com os tetos do piloto na sessão, quem ficou ocioso sem concluir devolve a tarefa, e a
273
+ // próxima tarefa livre do plano (D7) vai a ele pelo `block`. Livre é sem lease `path:` ativo de nenhuma thread na
274
+ // região e sem cruzar as tarefas dos outros teammates (D8 revista); a escolha e a atribuição acontecem juntas sobre o
275
+ // quadro mais novo. No teto de custo, sem os leases legíveis ou sem tarefa livre, o teammate fica ocioso.
276
+ on('classic.TeammateIdle', async ($, e, next) => {
277
+ const sessao = await sessaoDoPortao($)
278
+ if (!sessao || !sessao.tarefas) return next(e)
279
+ const tetos = await lerTetos($)
280
+ const teammate = nomeDoTeammate(e.teammate_name)
281
+ if (!tetos || !teammate) return next(e)
282
+ const { ctx, tarefas } = sessao
283
+ await mudarQuadro($, ctx.thread, q => {
284
+ const tinha = q.atribuicoes[teammate]
285
+ if (!tinha) return q
286
+ const atribuicoes = { ...q.atribuicoes }
287
+ delete atribuicoes[teammate]
288
+ const devolvida = !q.concluidas.includes(tinha) && !q.devolvidas.includes(tinha)
289
+ return { ...q, atribuicoes, devolvidas: devolvida ? [...q.devolvidas, tinha] : q.devolvidas }
290
+ })
291
+ const custo = await custoDaSessao($)
292
+ if (custo === null || custo >= tetos.custoUsd) {
293
+ $.ui.status(custo === null ? 'portão: custo da sessão ilegível; nenhuma tarefa nova ao time' : `portão: teto de custo do piloto (US$ ${tetos.custoUsd}) atingido`)
294
+ return next(e)
295
+ }
296
+ let leases: LeaseAtivo[]
297
+ try {
298
+ leases = await lerLeases($)
299
+ } catch (erro) {
300
+ await anotarFalha($, 'leitura dos leases', erro, ctx.thread)
301
+ return next(e)
302
+ }
303
+ let entregue: PortaoTarefa | null = null
304
+ await mudarQuadro($, ctx.thread, q => {
305
+ const escolha = escolherTarefa({ tarefas, quadro: q, teammate, leases })
306
+ if (!escolha) return q
307
+ entregue = escolha
308
+ return { ...q, atribuicoes: { ...q.atribuicoes, [teammate]: escolha.id } }
309
+ })
310
+ const tarefa = entregue as PortaoTarefa | null
311
+ if (!tarefa) return next(e)
312
+ $.ui.status(`portão: ${tarefa.id} com ${teammate}`)
313
+ return { block: textoDaEntrega(tarefa, teammate) }
314
+ }).catch(async ($, e, next) => {
315
+ if (next.error && next.error.kind !== 're-entry') await anotarFalha($, 'teammate ocioso', next.error.message ?? next.error.kind)
316
+ return next(e)
317
+ })
318
+
319
+ // Os tetos do piloto (D10): em toda sessão de fase, o teammate só nasce com os tetos do núcleo, abaixo do teto de
320
+ // teammates vivos e do teto de custo. Subagente que não é teammate passa.
321
+ on('agent.spawn', async ($, e, next) => {
322
+ if (!e.isTeammate) return next(e)
323
+ const ctx = await contextoDeFase($)
324
+ if (!ctx) return next(e)
325
+ const tetos = await lerTetos($)
326
+ const vivos = (await $.agent.list()).filter(a => a.teammateId && VIVOS.includes(a.status)).length
327
+ const recusa = recusaDoTeammate(tetos, vivos, tetos ? await custoDaSessao($) : null)
328
+ if (recusa) {
329
+ $.ui.status('portão: teammate recusado pelo teto do piloto')
330
+ return { deny: recusa }
331
+ }
332
+ return next(e)
333
+ }).catch(async ($, e, next) => {
334
+ if (!next.error || next.error.kind === 're-entry' || !e.isTeammate || next.called) return next(e)
335
+ const motivo = next.error.message ?? next.error.kind
336
+ try { await anotarFalha($, 'teto de teammates', motivo) } catch { $.ui.status('portão: falhou no teto de teammates') }
337
+ return { deny: `Portão de Tarefas: teammate recusado. O Portão não conseguiu conferir os tetos do piloto (${motivo}).` }
338
+ })
339
+ }
@@ -0,0 +1,152 @@
1
+ // A conclusão pela conferência do núcleo (RM-059, fatia F10, T10.2). Módulo puro: monta o argv do
2
+ // `ork claims conferir --tarefa`, lê o JSON `ork.claims-conferencia/v1`, conta a repetição e escreve o texto do
3
+ // bloqueio. O Portão nunca roda o verify da tarefa: quem roda é o núcleo, no sandbox do verify (D12 do plano dos
4
+ // mods), e quem verifica de verdade continua sendo o `ork verify`.
5
+ import type { PortaoRepeticao } from '../types'
6
+
7
+ export const FORMATO_DA_CONFERENCIA = 'ork.claims-conferencia/v1'
8
+ /** O prazo total pedido ao núcleo, em segundos (D5 corrigida: cabe no teto de dez minutos do `$.process.run`). */
9
+ export const PRAZO_DA_CONFERENCIA_S = 540
10
+ /** O vigia do mod: o `$.process.run` mata o processo depois disto, e a conferência fica indisponível. */
11
+ export const VIGIA_DA_CONFERENCIA_MS = 600000
12
+ /** Da terceira conclusão seguida bloqueada pelo mesmo resultado em diante, o texto manda deixar a tarefa aberta (D6). */
13
+ export const REPETICOES_PARA_DEIXAR = 3
14
+ /** Tetos do fim da saída que vai ao modelo. */
15
+ export const TETO_DE_LINHAS_DA_SAIDA = 15
16
+ export const TETO_DE_CARACTERES_DA_SAIDA = 1500
17
+
18
+ const ID_DE_THREAD = /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,79}$/
19
+ const ID_DE_TAREFA = /^T\d+(?:\.\d+)*$/
20
+ /** A saída do CLI quando outra condução segura a thread. */
21
+ const SAIDA_DE_CONDUCAO = 3
22
+
23
+ /** O argv do `ork claims conferir` do verify de uma tarefa; o comando da tarefa fica com o núcleo. */
24
+ export function argvDaConferencia(thread: string, tarefa: string): string[] {
25
+ if (!ID_DE_THREAD.test(thread)) throw new Error('ork-portao: id de thread inválido')
26
+ if (!ID_DE_TAREFA.test(tarefa)) throw new Error('ork-portao: id de tarefa inválido')
27
+ return ['ork', 'claims', 'conferir', thread, '--tarefa', tarefa, '--prazo', String(PRAZO_DA_CONFERENCIA_S), '--json']
28
+ }
29
+
30
+ /**
31
+ * O que a conferência disse da tarefa: aprovada; reprovada (`claims.failed`); sem veredito (prazo, comando que não
32
+ * rodou); recusada pelo núcleo (`claims.conferir.*`); com outra condução na thread; ou indisponível.
33
+ */
34
+ export type Leitura =
35
+ | { tipo: 'aprovada'; comando: string }
36
+ | { tipo: 'reprovada'; comando: string; codigo: number | null; fim: string }
37
+ | { tipo: 'sem-veredito'; motivo: string; comando: string; detalhe: string }
38
+ | { tipo: 'recusada'; motivo: string }
39
+ | { tipo: 'ocupada'; motivo: string }
40
+ | { tipo: 'indisponivel'; motivo: string }
41
+
42
+ const ultimaLinha = (texto: string) => texto.trim().split('\n').filter(l => l.trim() !== '').slice(-1)[0]?.trim().slice(0, 300) ?? ''
43
+
44
+ type Execucao = { comando?: unknown; ok?: unknown; code?: unknown; fim?: unknown }
45
+
46
+ /**
47
+ * A saída do `ork claims conferir --tarefa --json` (D4): o resultado só com o formato certo, o item da tarefa pedida
48
+ * e a saída 0 ou 1; a recusa tipada é a saída 2 com `claims.conferir.*`; a saída 3 é outra condução; o resto (texto
49
+ * solto de um `ork` antigo, JSON sem o item) é indisponível.
50
+ */
51
+ export function lerConferencia(tarefa: string, stdout: string, codigo: number | null, stderr: string): Leitura {
52
+ let j: { formato?: unknown; itens?: unknown; erro?: unknown; texto?: unknown } | null = null
53
+ try { j = JSON.parse(stdout) } catch { j = null }
54
+ if (j && j.formato === FORMATO_DA_CONFERENCIA && Array.isArray(j.itens) && (codigo === 0 || codigo === 1)) {
55
+ const item = (j.itens as Array<{ id?: unknown; verificado?: unknown; motivo?: unknown; detalhe?: unknown; execucoes?: unknown } | null>)
56
+ .find(i => i?.id === tarefa)
57
+ if (!item) return { tipo: 'indisponivel', motivo: `a conferência não devolveu a tarefa ${tarefa}` }
58
+ const execucoes = Array.isArray(item.execucoes) ? item.execucoes as Execucao[] : []
59
+ const chave = execucoes.find(x => x && x.ok === false) ?? execucoes[0]
60
+ const comando = typeof chave?.comando === 'string' ? chave.comando : ''
61
+ if (item.verificado === true && codigo === 0) return { tipo: 'aprovada', comando }
62
+ if (item.motivo === 'claims.failed') {
63
+ return { tipo: 'reprovada', comando, codigo: typeof chave?.code === 'number' ? chave.code : null, fim: typeof chave?.fim === 'string' ? chave.fim : '' }
64
+ }
65
+ return {
66
+ tipo: 'sem-veredito', comando,
67
+ motivo: typeof item.motivo === 'string' ? item.motivo : 'sem motivo',
68
+ detalhe: typeof item.detalhe === 'string' ? item.detalhe.slice(0, 300) : '',
69
+ }
70
+ }
71
+ const erro = j && typeof j.erro === 'string' ? j.erro.trim().slice(0, 300) : ''
72
+ if (codigo === SAIDA_DE_CONDUCAO) {
73
+ // Com `--json`, a recusa de condução sai no stdout (`ork.conducao-recusa/v1`), com o texto para quem lê.
74
+ const texto = j && typeof j.texto === 'string' ? (j.texto.trim().split('\n')[0] ?? '').slice(0, 300) : ''
75
+ return { tipo: 'ocupada', motivo: texto || erro || ultimaLinha(stderr) || 'outra condução segura a thread' }
76
+ }
77
+ if (erro.startsWith('claims.conferir.')) return { tipo: 'recusada', motivo: erro }
78
+ const motivo = erro || ultimaLinha(stderr) || (j === null ? ultimaLinha(stdout) : '') || `saiu com código ${codigo ?? 'desconhecido'}`
79
+ return { tipo: 'indisponivel', motivo: `ork claims conferir sem resultado: ${motivo}` }
80
+ }
81
+
82
+ /** O fim da saída de um comando: as últimas linhas e o último trecho, dentro dos tetos. */
83
+ export function fimDaSaida(fim: string): string[] {
84
+ let texto = fim.replace(/\s+$/, '')
85
+ if (texto.length > TETO_DE_CARACTERES_DA_SAIDA) texto = texto.slice(-TETO_DE_CARACTERES_DA_SAIDA)
86
+ return texto === '' ? [] : texto.split('\n').slice(-TETO_DE_LINHAS_DA_SAIDA)
87
+ }
88
+
89
+ /** O que torna dois bloqueios "o mesmo resultado" para a contagem da D6. */
90
+ export function assinatura(l: Leitura): string {
91
+ switch (l.tipo) {
92
+ case 'aprovada': return 'aprovada'
93
+ case 'reprovada': return JSON.stringify(['reprovada', l.comando, l.codigo, fimDaSaida(l.fim).slice(-3)])
94
+ case 'sem-veredito': return JSON.stringify(['sem-veredito', l.motivo, l.comando])
95
+ default: return JSON.stringify([l.tipo, l.motivo])
96
+ }
97
+ }
98
+
99
+ /** A repetição da tarefa depois deste bloqueio: a mesma assinatura soma, outra recomeça em 1. */
100
+ export function contarRepeticao(anterior: PortaoRepeticao | undefined, nova: string): PortaoRepeticao {
101
+ return anterior && anterior.assinatura === nova ? { assinatura: nova, vezes: anterior.vezes + 1 } : { assinatura: nova, vezes: 1 }
102
+ }
103
+
104
+ const SAIDA = 'Se não der para corrigir agora, devolva a tarefa para pending (TaskUpdate) e siga: o ork verify continua sendo quem verifica.'
105
+
106
+ /** O texto do `block` da conclusão recusada (D4 e D6), com a saída sempre dita. */
107
+ export function textoDoBloqueio(tarefa: string, l: Exclude<Leitura, { tipo: 'aprovada' }>, vezes = 1): string {
108
+ const partes: string[] = []
109
+ switch (l.tipo) {
110
+ case 'reprovada':
111
+ partes.push(`Portão de Tarefas: a tarefa ${tarefa} não fecha: o verify do plano reprovou na conferência do núcleo (ork claims conferir, no sandbox do verify, sem recibo).`)
112
+ if (l.comando) partes.push(` comando: ${l.comando}`)
113
+ partes.push(` saiu com código ${l.codigo ?? 'desconhecido'}${l.fim.trim() ? '; fim da saída:' : ', sem saída'}`)
114
+ for (const linha of fimDaSaida(l.fim)) partes.push(` | ${linha}`)
115
+ partes.push('Corrija e conclua de novo. Teste que cria processo pode reprovar no sandbox e passar no ork verify.')
116
+ break
117
+ case 'sem-veredito':
118
+ partes.push(`Portão de Tarefas: a tarefa ${tarefa} não fecha: a conferência do núcleo terminou sem veredito (${l.motivo}${l.detalhe ? `: ${l.detalhe}` : ''}).`)
119
+ if (l.comando) partes.push(` comando: ${l.comando}`)
120
+ partes.push('Conclua de novo para conferir outra vez.')
121
+ break
122
+ case 'recusada':
123
+ partes.push(`Portão de Tarefas: a tarefa ${tarefa} não fecha: o núcleo recusou a conferência (${l.motivo}).`)
124
+ break
125
+ case 'ocupada':
126
+ partes.push(`Portão de Tarefas: a tarefa ${tarefa} não fecha agora: outra condução segura a thread (${l.motivo}). Conclua de novo em instantes.`)
127
+ break
128
+ case 'indisponivel':
129
+ partes.push(`Portão de Tarefas: a tarefa ${tarefa} não fecha: a conferência do núcleo não deu resultado (${l.motivo}).`)
130
+ break
131
+ }
132
+ partes.push(SAIDA)
133
+ if (vezes >= REPETICOES_PARA_DEIXAR) {
134
+ partes.push(`É a ${vezes}ª vez seguida com o mesmo resultado: deixe a tarefa aberta (pending) e siga para a próxima; o ork verify do CHECK decide.`)
135
+ }
136
+ return partes.join('\n')
137
+ }
138
+
139
+ /** O texto do `block` quando outra conferência do Portão está em curso (D5). */
140
+ export function textoDeOutraConferencia(tarefa: string, emCurso: string): string {
141
+ return `Portão de Tarefas: a tarefa ${tarefa} não fecha agora: o Portão está conferindo a tarefa ${emCurso} no núcleo, uma conferência por vez. ` +
142
+ `Conclua de novo quando ela terminar. ${SAIDA}`
143
+ }
144
+
145
+ /** A linha de status depois de uma conferência; `undefined` limpa a linha. */
146
+ export function textoDoStatus(tarefa: string, l: Leitura): string | undefined {
147
+ if (l.tipo === 'aprovada') return undefined
148
+ const nome: Record<Exclude<Leitura['tipo'], 'aprovada'>, string> = {
149
+ reprovada: 'reprovou', 'sem-veredito': 'sem veredito', recusada: 'recusada pelo núcleo', ocupada: 'outra condução', indisponivel: 'conferência indisponível',
150
+ }
151
+ return `portão: ${tarefa} não fechou (${nome[l.tipo]})`
152
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "formato": "ork.mod/v1",
3
+ "papeis": ["fase"],
4
+ "claudeCodeMinimo": "2.1.287"
5
+ }
@@ -0,0 +1,60 @@
1
+ // Contrato de tipos do Portão de Tarefas (RM-059, fatia F10). O Portão não acrescenta noun ao `$`: este arquivo
2
+ // declara o estado dele (`PluginState['ork-portao']`) e os tipos que os módulos puros e os testes usam.
3
+ // Sem import nem referência, como o motor exige de um contrato.
4
+
5
+ /** Uma tarefa do plano como o Portão a usa: o id, o título, os `touch_paths` e o verify pela regra do núcleo. */
6
+ export type PortaoTarefa = {
7
+ /** `T2.1`, como o cabeçalho do plano escreve. */
8
+ id: string
9
+ /** O resto da linha do cabeçalho. */
10
+ titulo: string
11
+ /** Os trechos entre crases do item `touch_paths`, normalizados. */
12
+ touchPaths: string[]
13
+ /** O comando do verify, quando o núcleo o aceita (`ork claims conferir --tarefa`). */
14
+ verify: string | null
15
+ /** Por que o núcleo recusaria a conferência dessa tarefa, ou `null`. */
16
+ problema: null | 'tarefa-sem-verify' | 'tarefa-ambigua' | 'tarefa-sem-comando' | 'tarefa-repetida'
17
+ }
18
+
19
+ /** A repetição de bloqueios da conclusão de uma tarefa (D6): a assinatura do último resultado e quantas vezes seguidas. */
20
+ export type PortaoRepeticao = {
21
+ assinatura: string
22
+ vezes: number
23
+ }
24
+
25
+ /** Uma falha do próprio Portão: onde, o motivo e quando (ms desde a época). */
26
+ export type PortaoFalha = {
27
+ onde: string
28
+ motivo: string
29
+ em: number
30
+ }
31
+
32
+ /**
33
+ * O quadro que o Portão guarda da sessão de fase (D9 do plano da thread). Ele vale para esta sessão: numa sessão de GO
34
+ * redespachada, o que fechou na anterior não está aqui.
35
+ */
36
+ export type PortaoQuadro = {
37
+ /** A thread do despacho, do contexto da ponte. */
38
+ thread: string
39
+ /** A tarefa nativa (`task_id`) para a tarefa do plano, gravada quando a criação passou. */
40
+ tarefas: Record<string, string>
41
+ /** As tarefas do plano que fecharam com a conferência do núcleo aprovada nesta sessão. */
42
+ concluidas: string[]
43
+ /** A repetição de bloqueios da conclusão, por tarefa do plano. */
44
+ repeticoes: Record<string, PortaoRepeticao>
45
+ /** A tarefa do plano que o Portão entregou a cada teammate ocioso (T10.3), pelo nome do teammate. */
46
+ atribuicoes: Record<string, string>
47
+ /** As tarefas que um teammate devolveu ao ficar ocioso sem concluí-las: saem da distribuição automática. */
48
+ devolvidas: string[]
49
+ /** A última falha do Portão, para a linha de status e o log. */
50
+ ultimaFalha: PortaoFalha | null
51
+ }
52
+
53
+ declare module 'claude-code' {
54
+ interface PluginState {
55
+ 'ork-portao': {
56
+ /** O quadro do Portão nesta sessão de fase. */
57
+ quadro: PortaoQuadro
58
+ }
59
+ }
60
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "ork-radar",
3
+ "version": "0.1.0",
4
+ "description": "Radar de Colisão do Orkastery no Claude Code: o merge de cada thread aberta contra a origin/main e entre as threads, calculado sem worktree e sem gravar objeto no repositório, com os arquivos em conflito, os leases que os cobrem e o comando que resolve. Só lê.",
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
+ }