@orkastery/cli 0.5.6 → 0.6.1

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 (396) hide show
  1. package/adapters/README.md +18 -0
  2. package/adapters/claude-code/.claude-plugin/plugin.json +1 -0
  3. package/adapters/claude-code/README.md +37 -5
  4. package/adapters/claude-code/agents/ork-check.md +1 -0
  5. package/adapters/claude-code/agents/ork-go.md +1 -0
  6. package/adapters/claude-code/agents/ork-goal.md +1 -0
  7. package/adapters/claude-code/agents/ork-master.md +1 -0
  8. package/adapters/claude-code/agents/ork-plan.md +1 -0
  9. package/adapters/claude-code/agents/ork-ship.md +1 -0
  10. package/adapters/claude-code/hooks/hooks.json +2 -1
  11. package/adapters/claude-code/hooks/ork-guard.js +15 -1
  12. package/adapters/claude-code/hooks/ork-resposta.js +91 -0
  13. package/adapters/claude-code/hooks/ork-sensor.js +18 -2
  14. package/adapters/claude-code/hooks/quota-signal.js +14 -0
  15. package/adapters/claude-code/mods/ork-mensageiro/.claude-plugin/plugin.json +14 -0
  16. package/adapters/claude-code/mods/ork-mensageiro/hooks/caixa.ts +215 -0
  17. package/adapters/claude-code/mods/ork-mensageiro/hooks/hooks.json +3 -0
  18. package/adapters/claude-code/mods/ork-mensageiro/hooks/painel.tsx +165 -0
  19. package/adapters/claude-code/mods/ork-mensageiro/hooks/recepcao.ts +256 -0
  20. package/adapters/claude-code/mods/ork-mensageiro/hooks/register.tsx +481 -0
  21. package/adapters/claude-code/mods/ork-mensageiro/orkastery-mod.json +5 -0
  22. package/adapters/claude-code/mods/ork-mensageiro/types/index.d.ts +85 -0
  23. package/adapters/claude-code/mods/ork-pista/hooks/ci.ts +9 -5
  24. package/adapters/claude-code/mods/ork-ritual/.claude-plugin/plugin.json +14 -0
  25. package/adapters/claude-code/mods/ork-ritual/hooks/hooks.json +3 -0
  26. package/adapters/claude-code/mods/ork-ritual/hooks/nota.ts +88 -0
  27. package/adapters/claude-code/mods/ork-ritual/hooks/register.tsx +494 -0
  28. package/adapters/claude-code/mods/ork-ritual/hooks/replay.tsx +271 -0
  29. package/adapters/claude-code/mods/ork-ritual/hooks/ritual.ts +218 -0
  30. package/adapters/claude-code/mods/ork-ritual/orkastery-mod.json +5 -0
  31. package/adapters/claude-code/mods/ork-ritual/types/index.d.ts +106 -0
  32. package/adapters/claude-code/mods/ork-sentinela/hooks/vigia.ts +1 -1
  33. package/adapters/codex/skills/ork/SKILL.md +2 -0
  34. package/adapters/hermes/README.md +64 -1
  35. package/adapters/hermes/bin/ork-conhecimento-colecoes.sh +14 -0
  36. package/adapters/hermes/bin/ork-conhecimento-listar.sh +15 -0
  37. package/adapters/hermes/bin/ork-conhecimento-mostrar.sh +16 -0
  38. package/adapters/hermes/bin/ork-conhecimento-verificar.sh +15 -0
  39. package/adapters/hermes/bin/ork-vigia-batida.py +127 -0
  40. package/adapters/hermes/hermes.plugin.json +4 -0
  41. package/adapters/hermes/hitl-ingress/__init__.py +101 -8
  42. package/adapters/hermes/skills/orkastery-devmaster/SKILL.md +2 -2
  43. package/adapters/openclaw/README.md +81 -9
  44. package/adapters/openclaw/bin/ork-vigia-batida.js +86 -0
  45. package/adapters/openclaw/dist/hitl-ingress.js +106 -9
  46. package/adapters/openclaw/dist/index.js +86 -0
  47. package/adapters/openclaw/openclaw.plugin.json +34 -4
  48. package/adapters/openclaw/package.json +2 -2
  49. package/adapters/openclaw/src/hitl-ingress.ts +103 -9
  50. package/adapters/openclaw/src/index.ts +94 -0
  51. package/assets/adapters-en/claude-code/README.md +403 -0
  52. package/assets/adapters-en/claude-code/agents/ork-check.md +33 -0
  53. package/assets/adapters-en/claude-code/agents/ork-go.md +33 -0
  54. package/assets/adapters-en/claude-code/agents/ork-goal.md +34 -0
  55. package/assets/adapters-en/claude-code/agents/ork-master.md +35 -0
  56. package/assets/adapters-en/claude-code/agents/ork-plan.md +33 -0
  57. package/assets/adapters-en/claude-code/agents/ork-ship.md +33 -0
  58. package/assets/adapters-en/claude-code/commands/check.md +46 -0
  59. package/assets/adapters-en/claude-code/commands/go.md +46 -0
  60. package/assets/adapters-en/claude-code/commands/goal.md +55 -0
  61. package/assets/adapters-en/claude-code/commands/master.md +54 -0
  62. package/assets/adapters-en/claude-code/commands/onboarding.md +22 -0
  63. package/assets/adapters-en/claude-code/commands/ork.md +126 -0
  64. package/assets/adapters-en/claude-code/commands/plan.md +47 -0
  65. package/assets/adapters-en/claude-code/commands/ship.md +45 -0
  66. package/assets/adapters-en/codex/skills/ork/SKILL.md +85 -0
  67. package/assets/adapters-en/hermes/README.md +273 -0
  68. package/assets/adapters-en/hermes/skills/orkastery-devmaster/SKILL.md +169 -0
  69. package/assets/references-en/README.md +25 -0
  70. package/assets/references-en/code-review-axes.md +82 -0
  71. package/assets/references-en/definition-of-done.md +74 -0
  72. package/assets/references-en/performance-checklist.md +53 -0
  73. package/assets/references-en/security-checklist.md +101 -0
  74. package/assets/references-en/testing-patterns.md +51 -0
  75. package/assets/security-scan/defaults/.golangci.yml +2 -0
  76. package/assets/security-scan/defaults/eslint.config.mjs +11 -0
  77. package/assets/security-scan/defaults/gitleaks.toml +3 -0
  78. package/assets/security-scan/defaults/ruff.toml +1 -0
  79. package/assets/security-scan/tools-agil.json +87 -0
  80. package/assets/security-scan/tools.json +625 -0
  81. package/assets/skills-en/core/onboarding/SKILL.md +58 -0
  82. package/assets/skills-en/core/orkastery-bootstrap/SKILL.md +119 -0
  83. package/assets/skills-en/core/thread-state/SKILL.md +88 -0
  84. package/assets/skills-en/governance/decision-triage/SKILL.md +65 -0
  85. package/assets/skills-en/governance/narrative-guardian/SKILL.md +66 -0
  86. package/assets/skills-en/governance/roadmap-keeper/SKILL.md +60 -0
  87. package/assets/skills-en/governance/scope-check-capability-map/SKILL.md +71 -0
  88. package/assets/skills-en/observability/thread-tracing/SKILL.md +63 -0
  89. package/assets/skills-en/phases/check-quality/SKILL.md +103 -0
  90. package/assets/skills-en/phases/go-implementation/SKILL.md +78 -0
  91. package/assets/skills-en/phases/goal-definition/SKILL.md +69 -0
  92. package/assets/skills-en/phases/master-metrics/SKILL.md +72 -0
  93. package/assets/skills-en/phases/plan-specification/SKILL.md +67 -0
  94. package/assets/skills-en/phases/security-remediation/SKILL.md +70 -0
  95. package/assets/skills-en/phases/ship-release/SKILL.md +71 -0
  96. package/assets/skills-en/reviewers/code-reviewer/SKILL.md +61 -0
  97. package/assets/skills-en/reviewers/security-auditor/SKILL.md +93 -0
  98. package/assets/skills-en/reviewers/test-engineer/SKILL.md +66 -0
  99. package/assets/skills-en/reviewers/web-performance-auditor/SKILL.md +63 -0
  100. package/dist/adapters/claude-bg.js +123 -9
  101. package/dist/adapters/codex-controller-worker.js +12 -5
  102. package/dist/adapters/codex-events.js +16 -4
  103. package/dist/adapters/codex.js +3 -1
  104. package/dist/adapters/grok-events.js +227 -0
  105. package/dist/adapters/grok-mcp-servidor.js +612 -0
  106. package/dist/adapters/grok-mcp.js +178 -0
  107. package/dist/adapters/grok.js +686 -0
  108. package/dist/agendador-cpu.js +92 -0
  109. package/dist/atualizar-branch.js +245 -0
  110. package/dist/auditoria.js +13 -11
  111. package/dist/auditrun.js +51 -46
  112. package/dist/autorizacao-duravel.js +327 -0
  113. package/dist/board.js +94 -40
  114. package/dist/branch-de-estado.js +23 -10
  115. package/dist/busca-semantica.js +7 -5
  116. package/dist/catalogo.js +2 -1
  117. package/dist/changelog-reconciliacao.js +290 -0
  118. package/dist/check-agil.js +528 -0
  119. package/dist/check-modo.js +41 -0
  120. package/dist/check-paralelo.js +238 -0
  121. package/dist/ci.js +248 -45
  122. package/dist/ciclos.js +8 -6
  123. package/dist/claim-lint.js +12 -8
  124. package/dist/claims-conferir.js +47 -39
  125. package/dist/claims.js +10 -9
  126. package/dist/company-brain-client.js +10 -3
  127. package/dist/company-brain-migration.js +11 -5
  128. package/dist/conducao-batida.js +759 -0
  129. package/dist/conducao-sentinela.js +25 -8
  130. package/dist/conducao.js +89 -29
  131. package/dist/conhecimento-consulta.js +270 -0
  132. package/dist/conhecimento.js +111 -87
  133. package/dist/conta-esgotada.js +127 -0
  134. package/dist/dado-privado.js +816 -0
  135. package/dist/decisao-autonoma.js +5 -4
  136. package/dist/demo.js +14 -12
  137. package/dist/dependencias-da-worktree.js +25 -0
  138. package/dist/divida.js +38 -36
  139. package/dist/docs.js +89 -89
  140. package/dist/doctor-forja.js +343 -0
  141. package/dist/doctor-pulse-cron.js +113 -1
  142. package/dist/doctor.js +164 -6
  143. package/dist/dsn-da-memoria.js +417 -0
  144. package/dist/entrega-por-mod.js +503 -0
  145. package/dist/entrega-pr.js +154 -54
  146. package/dist/esgotamentos.js +13 -11
  147. package/dist/estado-thread.js +21 -18
  148. package/dist/evalrunner.js +35 -32
  149. package/dist/fabrica-batida.js +34 -21
  150. package/dist/fabrica-estado.js +33 -21
  151. package/dist/fix.js +21 -19
  152. package/dist/forja/adaptador.js +6 -0
  153. package/dist/forja/ambiente-de-ci.js +27 -0
  154. package/dist/forja/api.js +256 -0
  155. package/dist/forja/cache.js +89 -0
  156. package/dist/forja/cli.js +60 -0
  157. package/dist/forja/contrato.js +9 -0
  158. package/dist/forja/endereco.js +43 -0
  159. package/dist/forja/estado.js +22 -0
  160. package/dist/forja/executor.js +73 -0
  161. package/dist/forja/github/ci.js +95 -0
  162. package/dist/forja/github/dono-da-rede.js +18 -0
  163. package/dist/forja/github/metadados.js +15 -0
  164. package/dist/forja/github/pedidos.js +200 -0
  165. package/dist/forja/gitlab/ci.js +141 -0
  166. package/dist/forja/gitlab/consultas.js +59 -0
  167. package/dist/forja/gitlab/dono-da-rede.js +43 -0
  168. package/dist/forja/gitlab/metadados.js +15 -0
  169. package/dist/forja/gitlab/pedidos-abertos.js +158 -0
  170. package/dist/forja/gitlab/pedidos.js +141 -0
  171. package/dist/forja/produtor.js +32 -0
  172. package/dist/forja/registro.js +31 -0
  173. package/dist/forja/resolver.js +121 -0
  174. package/dist/forja/texto.js +13 -0
  175. package/dist/forja/versao.js +71 -0
  176. package/dist/forja.js +123 -80
  177. package/dist/handoff.js +23 -13
  178. package/dist/hitl-contract.js +46 -43
  179. package/dist/hitl-curto.js +37 -25
  180. package/dist/hitl-gates.js +13 -11
  181. package/dist/hitl-local-receipt.js +51 -0
  182. package/dist/hitl-local.js +11 -2
  183. package/dist/hitl-lock.js +5 -11
  184. package/dist/hitl-public-receipt.js +11 -0
  185. package/dist/hitl-resumo.js +44 -1
  186. package/dist/hitl-sessions.js +62 -9
  187. package/dist/hitl-texto-livre.js +7 -4
  188. package/dist/hitl.js +34 -31
  189. package/dist/horario.js +20 -1
  190. package/dist/hosts.js +104 -134
  191. package/dist/index.js +864 -554
  192. package/dist/indice-vetorial.js +14 -12
  193. package/dist/init.js +11 -2
  194. package/dist/integracoes-locais.js +4 -0
  195. package/dist/intelligence-graph-cli.js +123 -69
  196. package/dist/intelligence-graph-query.js +37 -22
  197. package/dist/leases.js +19 -18
  198. package/dist/ledger-stats.js +17 -16
  199. package/dist/ledger.js +31 -1
  200. package/dist/licoes.js +12 -11
  201. package/dist/locale-accounts.js +228 -0
  202. package/dist/locale-adapter.js +291 -0
  203. package/dist/locale-ajuda.js +86 -10
  204. package/dist/locale-audit.js +199 -0
  205. package/dist/locale-claims.js +213 -0
  206. package/dist/locale-conducao.js +138 -0
  207. package/dist/locale-conhecimento.js +181 -0
  208. package/dist/locale-decisao.js +205 -0
  209. package/dist/locale-docs.js +230 -0
  210. package/dist/locale-estado.js +101 -0
  211. package/dist/locale-forja.js +288 -0
  212. package/dist/locale-gerados.js +5 -0
  213. package/dist/locale-grafo.js +129 -0
  214. package/dist/locale-master.js +83 -0
  215. package/dist/locale-network.js +524 -0
  216. package/dist/locale-operacao-restante.js +931 -0
  217. package/dist/locale-projetos.js +100 -0
  218. package/dist/locale-prompts.js +194 -0
  219. package/dist/locale-pulse.js +140 -2
  220. package/dist/locale-reservas.js +87 -0
  221. package/dist/locale-retry.js +183 -6
  222. package/dist/locale-security-scan.js +69 -0
  223. package/dist/locale-setup.js +192 -0
  224. package/dist/locale-ship.js +177 -66
  225. package/dist/locale-thread.js +149 -5
  226. package/dist/locale-verify.js +80 -4
  227. package/dist/locale-worktree.js +246 -0
  228. package/dist/locale.js +553 -9
  229. package/dist/maestro-runtime.js +22 -3
  230. package/dist/manifest.js +293 -12
  231. package/dist/maquina-ociosa.js +211 -0
  232. package/dist/maquina.js +10 -5
  233. package/dist/master-nota-local.js +200 -0
  234. package/dist/master-nota.js +10 -2
  235. package/dist/master.js +118 -41
  236. package/dist/mcp-conhecimento.js +52 -0
  237. package/dist/mcp-experiencia.js +26 -5
  238. package/dist/mcp-install.js +90 -15
  239. package/dist/mcp-noctum.js +101 -0
  240. package/dist/mcp-server.js +116 -22
  241. package/dist/mcp-ship-transporte.js +977 -0
  242. package/dist/mcp-ship.js +68 -24
  243. package/dist/mcp-transportes.js +17 -0
  244. package/dist/memoria.js +63 -58
  245. package/dist/merge-da-entrega.js +85 -0
  246. package/dist/modos-migracao.js +8 -6
  247. package/dist/modos.js +17 -18
  248. package/dist/network-roadmap.js +94 -54
  249. package/dist/noctum-aceite.js +929 -0
  250. package/dist/noctum-arco.js +540 -33
  251. package/dist/noctum-capacidade.js +3 -3
  252. package/dist/noctum-changelog.js +399 -0
  253. package/dist/noctum-cli.js +539 -0
  254. package/dist/noctum-coordenacao.js +1225 -0
  255. package/dist/noctum-entrega.js +1217 -0
  256. package/dist/noctum-envelope.js +253 -17
  257. package/dist/noctum-execucao.js +100 -24
  258. package/dist/noctum-fechamento.js +416 -0
  259. package/dist/noctum-fila.js +49 -10
  260. package/dist/noctum-guarda.js +333 -0
  261. package/dist/noctum-integracao.js +507 -0
  262. package/dist/noctum-maquinas.js +387 -0
  263. package/dist/noctum-medicao.js +450 -0
  264. package/dist/noctum-modo.js +390 -0
  265. package/dist/noctum-pergunta-canal.js +96 -0
  266. package/dist/noctum-pergunta.js +115 -0
  267. package/dist/noctum-provas.js +838 -0
  268. package/dist/noctum-reconciliacao.js +594 -0
  269. package/dist/noctum-rede.js +102 -0
  270. package/dist/noctum-registro.js +215 -0
  271. package/dist/noctum-relatorio.js +861 -0
  272. package/dist/noctum-resposta.js +207 -0
  273. package/dist/noctum-retomada-rede.js +71 -0
  274. package/dist/noctum-sessoes-em-voo.js +93 -0
  275. package/dist/noctum-supervisao.js +649 -0
  276. package/dist/noctum-terminal.js +459 -0
  277. package/dist/noctum-transporte.js +655 -0
  278. package/dist/noctum-vigia.js +679 -0
  279. package/dist/ocupacao.js +3 -1
  280. package/dist/onboarding.js +16 -5
  281. package/dist/orkmind.js +39 -23
  282. package/dist/orquestracao.js +31 -35
  283. package/dist/parado-no-condutor.js +160 -139
  284. package/dist/phase.js +96 -42
  285. package/dist/policies.js +33 -0
  286. package/dist/postura-local.js +11 -6
  287. package/dist/preflight.js +2 -1
  288. package/dist/procedencia.js +9 -14
  289. package/dist/projeto-alvo.js +59 -20
  290. package/dist/prompts.js +136 -42
  291. package/dist/prova-minima.js +5 -6
  292. package/dist/pulse-cadencia.js +5 -4
  293. package/dist/pulse-consentimento.js +53 -1
  294. package/dist/pulse-delivery.js +143 -27
  295. package/dist/pulse-resposta.js +368 -4
  296. package/dist/pulse.js +136 -3
  297. package/dist/ratelimit.js +13 -4
  298. package/dist/recall.js +14 -13
  299. package/dist/rede-adesao.js +13 -8
  300. package/dist/rede-forja.js +108 -24
  301. package/dist/rede-status.js +63 -45
  302. package/dist/rede.js +86 -46
  303. package/dist/retomada-checkpoint.js +101 -0
  304. package/dist/retry.js +326 -78
  305. package/dist/roadmap-reservas.js +32 -32
  306. package/dist/runtime-ambiente.js +5 -0
  307. package/dist/runtime-context.js +279 -85
  308. package/dist/runtime-escada.js +429 -0
  309. package/dist/runtime-profiles.js +130 -63
  310. package/dist/runtimes.js +42 -5
  311. package/dist/saida-segura.js +3 -2
  312. package/dist/security-scan/agil.js +366 -0
  313. package/dist/security-scan/cli.js +76 -0
  314. package/dist/security-scan/compare.js +149 -0
  315. package/dist/security-scan/config.js +93 -0
  316. package/dist/security-scan/detect.js +97 -0
  317. package/dist/security-scan/executor.js +196 -0
  318. package/dist/security-scan/guard.js +180 -0
  319. package/dist/security-scan/imagem-de-lint.js +96 -0
  320. package/dist/security-scan/normalize/actionlint.js +12 -0
  321. package/dist/security-scan/normalize/eslint.js +12 -0
  322. package/dist/security-scan/normalize/gitleaks.js +13 -0
  323. package/dist/security-scan/normalize/golangci-lint.js +13 -0
  324. package/dist/security-scan/normalize/govulncheck.js +36 -0
  325. package/dist/security-scan/normalize/index.js +25 -0
  326. package/dist/security-scan/normalize/npm-audit.js +30 -0
  327. package/dist/security-scan/normalize/osv-scanner.js +32 -0
  328. package/dist/security-scan/normalize/oxlint.js +51 -0
  329. package/dist/security-scan/normalize/pip-audit.js +16 -0
  330. package/dist/security-scan/normalize/ruff.js +12 -0
  331. package/dist/security-scan/normalize/semgrep.js +20 -0
  332. package/dist/security-scan/normalize/trivy.js +33 -0
  333. package/dist/security-scan/normalize/util.js +204 -0
  334. package/dist/security-scan/normalize/zizmor.js +19 -0
  335. package/dist/security-scan/report.js +156 -0
  336. package/dist/security-scan/run.js +448 -0
  337. package/dist/security-scan/tools.js +62 -0
  338. package/dist/security-scan/trabalhador.js +74 -0
  339. package/dist/security-scan/types.js +15 -0
  340. package/dist/session-events.js +19 -2
  341. package/dist/session-watcher-claude.js +74 -19
  342. package/dist/session-watcher-grok.js +425 -0
  343. package/dist/session-watcher.js +174 -13
  344. package/dist/sessoes-contas.js +42 -5
  345. package/dist/sessoes-inventario.js +1 -1
  346. package/dist/sessoes.js +25 -23
  347. package/dist/setup.js +67 -66
  348. package/dist/ship-publicar.js +263 -92
  349. package/dist/ship.js +160 -17
  350. package/dist/superficie.js +14 -11
  351. package/dist/thread-close.js +9 -9
  352. package/dist/thread.js +121 -18
  353. package/dist/tokens.js +15 -11
  354. package/dist/trabalho-thread.js +297 -0
  355. package/dist/transporte-do-canal.js +189 -0
  356. package/dist/types.js +12 -1
  357. package/dist/util.js +5 -2
  358. package/dist/varredura-acoes.js +500 -0
  359. package/dist/verify.js +491 -32
  360. package/dist/worktree-local.js +12 -10
  361. package/dist/worktree.js +81 -85
  362. package/eval/casos/check-quality.json +77 -0
  363. package/eval/casos/security-auditor.json +148 -0
  364. package/eval/casos/security-remediation.json +161 -0
  365. package/eval/fixtures/security-scan/README.md +8 -0
  366. package/eval/fixtures/security-scan/go/.github/workflows/ci.yml +12 -0
  367. package/eval/fixtures/security-scan/go/Dockerfile +4 -0
  368. package/eval/fixtures/security-scan/go/README.md +4 -0
  369. package/eval/fixtures/security-scan/go/config.go +4 -0
  370. package/eval/fixtures/security-scan/go/go.mod +5 -0
  371. package/eval/fixtures/security-scan/go/go.sum +2 -0
  372. package/eval/fixtures/security-scan/go/main.go +31 -0
  373. package/eval/fixtures/security-scan/js/.github/workflows/ci.yml +12 -0
  374. package/eval/fixtures/security-scan/js/Dockerfile +3 -0
  375. package/eval/fixtures/security-scan/js/README.md +4 -0
  376. package/eval/fixtures/security-scan/js/package-lock.json +844 -0
  377. package/eval/fixtures/security-scan/js/package.json +10 -0
  378. package/eval/fixtures/security-scan/js/src/config.js +4 -0
  379. package/eval/fixtures/security-scan/js/src/server.js +17 -0
  380. package/eval/fixtures/security-scan/js/src/util.ts +4 -0
  381. package/eval/fixtures/security-scan/js/tsconfig.json +4 -0
  382. package/eval/fixtures/security-scan/python/.github/workflows/ci.yml +12 -0
  383. package/eval/fixtures/security-scan/python/Dockerfile +3 -0
  384. package/eval/fixtures/security-scan/python/README.md +4 -0
  385. package/eval/fixtures/security-scan/python/app/__init__.py +0 -0
  386. package/eval/fixtures/security-scan/python/app/config.py +2 -0
  387. package/eval/fixtures/security-scan/python/app/main.py +19 -0
  388. package/eval/fixtures/security-scan/python/requirements.txt +1 -0
  389. package/package.json +10 -5
  390. package/references/security-checklist.md +3 -3
  391. package/skills/README.md +1 -0
  392. package/skills/core/orkastery-bootstrap/SKILL.md +2 -2
  393. package/skills/phases/check-quality/SKILL.md +16 -0
  394. package/skills/phases/go-implementation/SKILL.md +6 -0
  395. package/skills/phases/security-remediation/SKILL.md +70 -0
  396. package/skills/reviewers/security-auditor/SKILL.md +25 -0
@@ -0,0 +1,594 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.lerRemotoPadrao = exports.ultimaReservaDoArco = exports.reservaQueLancou = exports.listagemCompleta = void 0;
37
+ exports.provaDaSessao = provaDaSessao;
38
+ exports.retomadaPendente = retomadaPendente;
39
+ exports.faseDaRetomada = faseDaRetomada;
40
+ exports.decidirReservas = decidirReservas;
41
+ exports.decidirSessoes = decidirSessoes;
42
+ exports.paradaPorAmbiente = paradaPorAmbiente;
43
+ exports.recusaDeAmbiente = recusaDeAmbiente;
44
+ exports.decidirPedidos = decidirPedidos;
45
+ exports.registrarEfeitoIncerto = registrarEfeitoIncerto;
46
+ exports.reconciliarEfeito = reconciliarEfeito;
47
+ exports.efeitosAbertos = efeitosAbertos;
48
+ /**
49
+ * RM-058, fatia N4 (D7 a D12, D15 e D16 da ork-rm058n4): a reconciliação do que ficou no meio.
50
+ *
51
+ * Aqui só se DECIDE, com a prova de cada decisão; quem grava é o vigia (`noctum-vigia.ts`), sob as travas dele. Toda
52
+ * decisão relê o disco (o log do arco, a supervisão e o ledger das threads) e a leitura do runtime que o vigia passa:
53
+ *
54
+ * - a reserva pendente de um passo morto no meio (D7): a prova no ledger decide entre concluída (o despacho com a
55
+ * correlação do arco veio), devolvida (nada foi lançado: nenhum prompt gravado com a correlação depois da reserva) e
56
+ * incerta (o runtime pode ter aberto a sessão); a incerta só se resolve pelo runtime e, sem prova, fica contada e o
57
+ * trabalho é isolado depois da carência;
58
+ * - a sessão sem resultado (D10 a D12): morta pelo runtime, ou parada pelo controle dele com confirmação, vira
59
+ * `sessao_morta` no ledger da thread (o que solta condução e vaga) e uma retomada autorizada, que o passo da N2 despacha
60
+ * com a conferência do envelope e a reserva (D8); a fase é a seguinte à prova mais adiantada que a sessão deixou (D9);
61
+ * - o despacho de fora do arco numa thread dele isola o trabalho (D16), e a recusa de ambiente repetida para o arco (D15).
62
+ *
63
+ * "Pouca saída, sozinha, não prova travamento": só evento de produto conta como avanço, heartbeat não; e parar uma sessão
64
+ * exige o prazo vencido, o runtime dizendo o estado e a confirmação depois.
65
+ */
66
+ const fs = __importStar(require("node:fs"));
67
+ const path = __importStar(require("node:path"));
68
+ const node_crypto_1 = require("node:crypto");
69
+ const conducao_sentinela_1 = require("./conducao-sentinela");
70
+ const horario_1 = require("./horario");
71
+ const ledger_1 = require("./ledger");
72
+ const noctum_envelope_1 = require("./noctum-envelope");
73
+ const noctum_supervisao_1 = require("./noctum-supervisao");
74
+ const parado_no_condutor_1 = require("./parado-no-condutor");
75
+ const ratelimit_1 = require("./ratelimit");
76
+ const thread_1 = require("./thread");
77
+ const util_1 = require("./util");
78
+ const TERMINAIS = ['done', 'completed', 'exited', 'stopped', 'failed'];
79
+ /** O runtime mostra a sessão trabalhando agora (o claude-bg diz `running` ou `busy`; o watcher, `working`). */
80
+ const TRABALHANDO = ['running', 'busy', 'working'];
81
+ const estadoDe = (s) => String(s.state ?? s.status ?? '').trim().toLowerCase();
82
+ const texto = (v) => typeof v === 'string' && v ? v : null;
83
+ /**
84
+ * A listagem que prova ausência: todas as fontes lidas, nenhuma ausente (B6 do CHECK: sem o `claude` no PATH, a fonte
85
+ * diz `ausente` e não lista nada, o que não prova que não há sessão).
86
+ */
87
+ const listagemCompleta = (leitura) => leitura.fontes.every(f => f.ok && !f.ausente);
88
+ exports.listagemCompleta = listagemCompleta;
89
+ /**
90
+ * D10: a prova do runtime. Vista com estado terminal ou fantasma (sem processo), morta; vista em outro estado, viva.
91
+ * Ausente, só o claude-bg prova a morte, e só com a listagem completa (`--all`, todas as fontes lidas e nenhuma ausente),
92
+ * a conta da sessão entre as consultadas e a sessão despachada pelo menos `ausenciaMinimaMs` antes da leitura (B3: a
93
+ * sessão que nasceu durante a leitura ainda não aparece nela). O codex lista só rollouts recentes: ausência lá não prova.
94
+ */
95
+ function provaDaSessao(leitura, sessionId, conta, runtime, despachadaEm, ausenciaMinimaMs = 0) {
96
+ const vista = leitura.sessoes.find(s => s.sessionId === sessionId);
97
+ if (vista) {
98
+ if (vista.fantasma)
99
+ return { estado: 'morta', detalhe: `o runtime ${runtime} lista a sessão sem processo (fantasma)` };
100
+ const estado = estadoDe(vista);
101
+ if (TERMINAIS.includes(estado))
102
+ return { estado: 'morta', detalhe: `o runtime ${runtime} lista a sessão em ${estado}` };
103
+ return { estado: 'viva', trabalhando: TRABALHANDO.includes(estado), detalhe: `o runtime ${runtime} lista a sessão em ${estado || 'estado sem nome'}` };
104
+ }
105
+ const completa = (0, exports.listagemCompleta)(leitura);
106
+ const consultada = leitura.contas.some(c => c.runtime === runtime && (c.perfil?.id ?? null) === conta);
107
+ const lidaEm = Date.parse(leitura.lidaEm ?? '');
108
+ const recente = despachadaEm !== undefined && (!Number.isFinite(lidaEm) || lidaEm - Date.parse(despachadaEm) < ausenciaMinimaMs);
109
+ if (runtime === 'claude-bg' && completa && consultada && !recente) {
110
+ return { estado: 'morta', detalhe: `a listagem completa do claude-bg na conta ${conta ?? 'do processo'} não tem a sessão` };
111
+ }
112
+ return { estado: 'desconhecida', detalhe: !completa ? 'o runtime não respondeu em todas as contas, ou o claude não está no PATH'
113
+ : runtime !== 'claude-bg' ? `ausência no ${runtime} não prova o fim da sessão`
114
+ : !consultada ? `a conta ${conta ?? 'do processo'} não foi consultada`
115
+ : 'a sessão foi despachada há pouco e a listagem pode ainda não a ter' };
116
+ }
117
+ /**
118
+ * As sessões da thread no runtime que podem ser do lançamento: pelo nome do despacho (o slug), ou pela pasta da worktree
119
+ * com o início depois da reserva (A3 do CHECK: uma sessão antiga da mesma worktree não é deste lançamento).
120
+ */
121
+ function sessoesDaThread(leitura, t, slug, desde) {
122
+ const pasta = t.worktree ? path.resolve(t.worktree) : null;
123
+ const inicio = (s) => typeof s.startedAt === 'number' ? (s.startedAt < 1e12 ? s.startedAt * 1000 : s.startedAt) : NaN;
124
+ return leitura.sessoes.filter(s => (slug && s.name === slug) ||
125
+ (pasta && s.cwd && path.resolve(s.cwd) === pasta && inicio(s) >= desde - 60_000));
126
+ }
127
+ /**
128
+ * A reserva que lançou: concluída ou pendente, ou devolvida pela reconciliação sem sessão depois do prompt gravado (NA2 da
129
+ * rodada 2: o runtime foi chamado e não abriu nada). A recusa de antes do lançamento e a devolução sem lançamento não.
130
+ */
131
+ const reservaQueLancou = (d) => d.desfecho !== 'recusado' || (d.motivo ?? '').startsWith('noctum.reconciliacao.sem-sessao');
132
+ exports.reservaQueLancou = reservaQueLancou;
133
+ /**
134
+ * B1 do CHECK: a retomada autorizada que nenhuma reserva do arco atendeu ainda (a mais nova do trabalho). Uma reserva do
135
+ * trabalho depois da autorização que lançou a atende, tenha o lançamento dado certo ou não; só a recusa de antes do
136
+ * lançamento (nada abriu) deixa a autorização de pé. Assim cada tentativa conta no teto. O "depois" é a ordem do log do
137
+ * arco (NA1 da rodada 2: o `seq` de reserva gravado na autorização), e não o relógio de dois processos; a linha sem ele
138
+ * fica com o instante.
139
+ */
140
+ function retomadaPendente(sup, estado, trabalho) {
141
+ const r = sup.retomadas.filter(x => x.trabalho === trabalho).at(-1);
142
+ if (!r)
143
+ return null;
144
+ const depois = (d) => r.aposReserva !== null ? d.seq > r.aposReserva : Date.parse(d.reservadoEm) >= Date.parse(r.em);
145
+ const atendida = estado.despachos.some(d => d.trabalho === trabalho && (0, exports.reservaQueLancou)(d) && depois(d));
146
+ return atendida ? null : r;
147
+ }
148
+ /** O último `seq` de reserva do arco: o que a autorização grava para ser atendida só pelas reservas de depois dela. */
149
+ const ultimaReservaDoArco = (estado) => Math.max(0, ...estado.despachos.map(d => d.seq));
150
+ exports.ultimaReservaDoArco = ultimaReservaDoArco;
151
+ const depoisDe = (e, desde) => Date.parse(e.ts) >= desde;
152
+ const perfilDe = (e) => {
153
+ const p = e?.perfil;
154
+ if (typeof p === 'string')
155
+ return p;
156
+ return p && typeof p === 'object' && typeof p.id === 'string' ? p.id : null;
157
+ };
158
+ // ---------------------------------------------------------------------------
159
+ // D9: a fase da retomada.
160
+ // ---------------------------------------------------------------------------
161
+ /** O artefato de cada fase que tem um, na pasta `docs/` da thread (a mesma regra da Sentinela). */
162
+ const ARTEFATO_DA_FASE = { GOAL: 'goal.md', PLAN: 'plan.md', CHECK: 'check.md' };
163
+ const FASES_DA_ENTREGA = ['SHIP', 'MASTER'];
164
+ /** Eventos de produto: o que só uma sessão que trabalha deixa. Heartbeat não é avanço. */
165
+ const EVENTOS_DE_AVANCO = ['mcp_git_committed', 'commit', 'claim_added', 'verify_run', 'baseline_recorded', 'autonomous_decision',
166
+ 'phase_result', 'hitl_requested', 'ship_started', 'ship_done', 'ship_blocked', 'go_fix_opened', 'check_reverify', 'artifact_delivered'];
167
+ /**
168
+ * A fase da retomada (D9): a seguinte à prova mais adiantada que a sessão deixou depois do despacho (o artefato da fase
169
+ * gravado depois dele, ou o `ship_done`), ou a própria fase do despacho sem prova. Diferente da Sentinela, que lê um turno
170
+ * concluído, aqui o turno não concluiu: produto na branch prova o PLAN (o GO começou), mas não prova o GO; só o
171
+ * `check.md` depois dele prova. `null` quando a seguinte é SHIP ou MASTER: a entrega é da N5, e o arco não a retoma.
172
+ */
173
+ function faseDaRetomada(raiz, t, eventos, fase, despachoEm) {
174
+ const desde = Date.parse(despachoEm);
175
+ const i = t.fases.indexOf(fase);
176
+ if (i < 0)
177
+ return null;
178
+ const comProduto = eventos.some(e => (e.tipo === 'mcp_git_committed' || e.tipo === 'commit') && depoisDe(e, desde));
179
+ const feita = (f) => {
180
+ const artefato = ARTEFATO_DA_FASE[f];
181
+ if (artefato) {
182
+ const st = fs.statSync(path.join((0, thread_1.dirThread)(raiz, t.id), 'docs', artefato), { throwIfNoEntry: false });
183
+ if (st && st.mtimeMs >= desde)
184
+ return true;
185
+ }
186
+ if (f === 'PLAN' && comProduto)
187
+ return true;
188
+ return f === 'SHIP' && eventos.some(e => e.tipo === 'ship_done' && depoisDe(e, desde));
189
+ };
190
+ let maisAdiantada = -1;
191
+ for (let k = i; k < t.fases.length; k++)
192
+ if (feita(t.fases[k]))
193
+ maisAdiantada = k;
194
+ const proxima = maisAdiantada >= 0 ? t.fases[maisAdiantada + 1] ?? null : fase;
195
+ return proxima && !FASES_DA_ENTREGA.includes(proxima) ? proxima : null;
196
+ }
197
+ /**
198
+ * D7: o que fazer com cada reserva pendente. Só vale com a trava de despacho na mão de quem chama (nenhum passo no meio).
199
+ * Os eventos que contam são os da thread depois da reserva e antes da reserva seguinte do mesmo trabalho.
200
+ */
201
+ function decidirReservas(raiz, arco, conteudo, estado, leitura, parametros, quando) {
202
+ const correlacao = (0, noctum_supervisao_1.correlacaoDoArco)(arco);
203
+ const threads = (0, noctum_supervisao_1.threadsDoArco)(raiz, conteudo, estado);
204
+ let ilegivel = false;
205
+ for (const id of (0, thread_1.listarIds)(raiz)) {
206
+ try {
207
+ (0, thread_1.lerThread)(raiz, id);
208
+ }
209
+ catch {
210
+ ilegivel = true;
211
+ break;
212
+ }
213
+ }
214
+ const decisoes = [];
215
+ for (const d of estado.despachos.filter(x => x.desfecho === 'pendente')) {
216
+ const base = { seq: d.seq, trabalho: d.trabalho, sessionId: null, idempotente: false, motivo: null, morta: null, isolamento: null };
217
+ const desde = Date.parse(d.reservadoEm);
218
+ const vencida = Date.parse(quando) - desde >= parametros.carenciaDaReservaMs;
219
+ const thread = d.thread ?? threads.get(d.trabalho) ?? null;
220
+ if (!thread) {
221
+ // Sem thread do trabalho de pedido: o passo morreu antes de abrir. Com thread ilegível no projeto, ela pode ser a dele.
222
+ decisoes.push(ilegivel
223
+ ? { ...base, thread: null, acao: 'aguardar', prova: 'há thread ilegível no projeto, e ela pode ser a do trabalho: sem prova de que nada foi lançado' }
224
+ : { ...base, thread: null, acao: 'devolver', motivo: 'noctum.reconciliacao.sem-lancamento: nenhuma thread do trabalho existe depois da reserva',
225
+ prova: 'nenhuma thread casa com o item e o sha256 do pedido' });
226
+ continue;
227
+ }
228
+ let eventos, t;
229
+ try {
230
+ t = (0, thread_1.lerThread)(raiz, thread);
231
+ eventos = (0, ledger_1.lerLedger)((0, thread_1.dirThread)(raiz, thread));
232
+ }
233
+ catch {
234
+ decisoes.push({ ...base, thread, acao: 'aguardar', prova: `a thread ${thread} não pôde ser lida` });
235
+ continue;
236
+ }
237
+ // A janela desta reserva: até a reserva seguinte do mesmo trabalho, se houver.
238
+ const proxima = estado.despachos.filter(x => x.trabalho === d.trabalho && x.seq > d.seq).map(x => Date.parse(x.reservadoEm))[0] ?? Infinity;
239
+ const janela = eventos.filter(e => depoisDe(e, desde) && Date.parse(e.ts) < proxima);
240
+ const doArco = (e) => e.correlacao === correlacao && e.fase === d.fase;
241
+ const despacho = janela.find(e => e.tipo === 'phase_dispatch' && doArco(e));
242
+ if (despacho) {
243
+ decisoes.push({ ...base, thread, acao: 'concluir', sessionId: texto(despacho.sessionId),
244
+ prova: `${thread}: phase_dispatch ${String(despacho.eventId ?? '')} com a correlação do arco depois da reserva` });
245
+ continue;
246
+ }
247
+ const idempotente = janela.find(e => e.tipo === 'despacho_idempotente' && e.fase === d.fase);
248
+ if (idempotente) {
249
+ decisoes.push({ ...base, thread, acao: 'concluir', sessionId: texto(idempotente.sessionId), idempotente: true,
250
+ prova: `${thread}: despacho_idempotente ${String(idempotente.eventId ?? '')} depois da reserva` });
251
+ continue;
252
+ }
253
+ const prompt = janela.find(e => e.tipo === 'phase_prompt_recorded' && doArco(e));
254
+ const falha = janela.filter(e => e.tipo === 'phase_dispatch_failed' && doArco(e)).at(-1);
255
+ if (!prompt && !falha) {
256
+ // O prompt vai ao disco e ao ledger antes do adaptador (RM-063, F3): sem ele, o runtime não foi chamado.
257
+ decisoes.push({ ...base, thread, acao: 'devolver',
258
+ motivo: 'noctum.reconciliacao.sem-lancamento: nenhum phase_prompt_recorded do arco depois da reserva',
259
+ prova: `${thread}: nenhum prompt gravado com a correlação do arco depois da reserva` });
260
+ continue;
261
+ }
262
+ const conta = perfilDe(falha) ?? perfilDe(prompt) ?? d.conta;
263
+ const runtime = texto(falha?.runtime) ?? texto(prompt?.runtime) ?? d.runtime;
264
+ const idDaFalha = texto(falha?.sessionId);
265
+ if (idDaFalha) {
266
+ const p = provaDaSessao(leitura, idDaFalha, conta, runtime, falha.ts, parametros.ausenciaMinimaMs);
267
+ if (p.estado === 'viva') {
268
+ decisoes.push({ ...base, thread, acao: 'concluir', sessionId: idDaFalha, prova: `a sessão ${idDaFalha} dita na falha está viva: ${p.detalhe}` });
269
+ }
270
+ else if (p.estado === 'morta') {
271
+ decisoes.push({ ...base, thread, acao: 'concluir', sessionId: idDaFalha, morta: { sessionId: idDaFalha, fase: d.fase, runtime, perfil: conta },
272
+ prova: `a sessão ${idDaFalha} dita na falha acabou: ${p.detalhe}` });
273
+ }
274
+ else {
275
+ decisoes.push({ ...base, thread, acao: vencida ? 'isolar' : 'aguardar',
276
+ isolamento: vencida ? { motivo: 'reserva-incerta', detalhe: `a sessão ${idDaFalha} pode estar aberta e o runtime não prova: ${p.detalhe}` } : null,
277
+ prova: `sessão ${idDaFalha} sem prova do runtime: ${p.detalhe}` });
278
+ }
279
+ continue;
280
+ }
281
+ // Prompt gravado sem despacho nem id: o runtime pode ter aberto a sessão sem que o núcleo soubesse dela.
282
+ const achadas = sessoesDaThread(leitura, t, texto(prompt?.slug) ?? texto(falha?.slug), desde);
283
+ const viva = achadas.find(s => !s.fantasma && !TERMINAIS.includes(estadoDe(s)));
284
+ if (viva) {
285
+ decisoes.push({ ...base, thread, acao: 'concluir', sessionId: viva.sessionId,
286
+ isolamento: { motivo: 'sessao-fora-do-registro', detalhe: `a sessão ${viva.sessionId} trabalha na thread sem o registro do núcleo; o arco não despacha por cima` },
287
+ prova: `o runtime lista a sessão ${viva.sessionId} na thread ${thread} (${estadoDe(viva) || 'estado sem nome'})` });
288
+ continue;
289
+ }
290
+ if (achadas.length) {
291
+ const morta = achadas[0];
292
+ decisoes.push({ ...base, thread, acao: 'concluir', sessionId: morta.sessionId,
293
+ morta: { sessionId: morta.sessionId, fase: d.fase, runtime, perfil: conta }, prova: `a sessão ${morta.sessionId} da thread acabou no runtime` });
294
+ continue;
295
+ }
296
+ const completa = (0, exports.listagemCompleta)(leitura) && leitura.contas.some(c => c.runtime === runtime && (c.perfil?.id ?? null) === conta);
297
+ if (runtime === 'claude-bg' && completa && vencida) {
298
+ decisoes.push({ ...base, thread, acao: 'devolver',
299
+ motivo: 'noctum.reconciliacao.sem-sessao: a listagem completa do claude-bg não tem sessão da thread depois da carência',
300
+ prova: `nenhuma sessão da thread ${thread} na conta ${conta} depois de ${Math.round(parametros.carenciaDaReservaMs / 60000)} min` });
301
+ continue;
302
+ }
303
+ decisoes.push({ ...base, thread, acao: vencida ? 'isolar' : 'aguardar',
304
+ isolamento: vencida ? { motivo: 'reserva-incerta', detalhe: 'o lançamento ficou incerto e o runtime não prova nada: a reserva segue contada' } : null,
305
+ prova: completa ? 'ainda na carência da reserva' : 'o runtime não prova a ausência da sessão (fonte sem leitura, conta não consultada ou codex)' });
306
+ }
307
+ return decisoes;
308
+ }
309
+ /** Atividade da própria sessão depois do despacho: o que mostra que o turno começou. */
310
+ const ATIVIDADE = ['runtime_event', 'runtime_heartbeat', 'commit', 'runtime_stop', 'runtime_subagent_stop', 'sessao_destravada', 'phase_result'];
311
+ /** O último instante de avanço da thread desde o despacho: evento de produto no ledger ou artefato de fase gravado. */
312
+ function ultimoAvanco(raiz, thread, eventos, desde) {
313
+ let ultimo = desde;
314
+ for (const e of eventos)
315
+ if (EVENTOS_DE_AVANCO.includes(e.tipo) && Date.parse(e.ts) > ultimo)
316
+ ultimo = Date.parse(e.ts);
317
+ for (const arquivo of Object.values(ARTEFATO_DA_FASE)) {
318
+ const st = fs.statSync(path.join((0, thread_1.dirThread)(raiz, thread), 'docs', arquivo), { throwIfNoEntry: false });
319
+ if (st && st.mtimeMs > ultimo)
320
+ ultimo = st.mtimeMs;
321
+ }
322
+ return ultimo;
323
+ }
324
+ /** A recusa do núcleo ou do runtime (o portão, a conta), e não a do próprio arco (`noctum.*`) nem a baseline pendente. */
325
+ const recusaDoNucleo = (motivo) => !!motivo && !motivo.startsWith('noctum.') && motivo !== 'baseline.pendente';
326
+ /**
327
+ * D10 a D12 e D16: o que fazer em cada thread do arco. Despacho de fora isola, mesmo com retomada pendente (NB1 da rodada
328
+ * 2). A recusa do núcleo depois do último lançamento numa thread já despachada (o portão: a árvore, a policy, a
329
+ * autoconferência, o contexto, a conta) vira a retomada da fase recusada (NA3 da rodada 2): a Sentinela a pede, e a fila
330
+ * da N2 não a despacha sozinha. O último lançamento do arco na thread (o `phase_dispatch`, ou a falha do despacho, B2 do
331
+ * CHECK) decide o resto: a falha que disse o id põe essa sessão sob vigia, como um despacho; a falha sem id numa thread já
332
+ * despachada vira a retomada da mesma fase (a conta que falhou já está fora). A sessão morta sem resultado (depois da
333
+ * carência desde a última atividade dela, B5), o fim de turno sem resultado depois da carência, a sessão sem avanço e a
334
+ * sem primeiro turno viram retomada (com a parada pelo runtime antes, quando a sessão está viva), até o teto; no teto,
335
+ * isolamento. Trabalho isolado, entregue, fechado, esperando o dono, com reserva pendente ou com retomada autorizada e
336
+ * ainda não atendida (B1) fica de fora.
337
+ */
338
+ function decidirSessoes(raiz, arco, conteudo, estado, sup, leitura, quando, ordensCanceladas = new Set()) {
339
+ const parametros = sup.parametros;
340
+ if (!parametros)
341
+ return [];
342
+ const agora = Date.parse(quando);
343
+ const correlacao = (0, noctum_supervisao_1.correlacaoDoArco)(arco);
344
+ const autorizadoEm = Date.parse(estado.autorizadoPor?.recebidoEm ?? conteudo.janela.inicio);
345
+ const pendentes = new Set(estado.despachos.filter((d) => d.desfecho === 'pendente').map(d => d.trabalho));
346
+ const decisoes = [];
347
+ const aposReserva = (0, exports.ultimaReservaDoArco)(estado);
348
+ for (const [trabalho, thread] of (0, noctum_supervisao_1.threadsDoArco)(raiz, conteudo, estado)) {
349
+ // A sessão da ordem cancelada na rede é encerrada pelo vigia, mas nunca autorizada a retomar nesta máquina.
350
+ if (ordensCanceladas.has(trabalho))
351
+ continue;
352
+ if (sup.isolados.has(trabalho) || pendentes.has(trabalho))
353
+ continue;
354
+ let t, eventos;
355
+ try {
356
+ t = (0, thread_1.lerThread)(raiz, thread);
357
+ eventos = (0, ledger_1.lerLedger)((0, thread_1.dirThread)(raiz, thread));
358
+ }
359
+ catch {
360
+ continue;
361
+ }
362
+ if (t.status === 'fechada' || eventos.some(e => e.tipo === 'ship_done'))
363
+ continue;
364
+ // D16: um despacho de fora do arco, depois da autorização, isola o trabalho.
365
+ const deFora = eventos.find(e => e.tipo === 'phase_dispatch' && Date.parse(e.ts) >= autorizadoEm && e.correlacao !== correlacao);
366
+ if (deFora) {
367
+ decisoes.push({ acao: 'isolar', trabalho, thread, motivo: 'condutor-externo', parar: null,
368
+ detalhe: `a thread ${thread} recebeu despacho de fora do arco (${String(deFora.fase)}, canal ${String(deFora.canal ?? 'desconhecido')}, ` +
369
+ `origem ${String(deFora.origem ?? 'desconhecida')}): o arco não conduz junto com outro condutor` });
370
+ continue;
371
+ }
372
+ if ((0, parado_no_condutor_1.pendenciaDoDono)(t, eventos, quando) || retomadaPendente(sup, estado, trabalho))
373
+ continue;
374
+ const retomar = (fase, motivo, sessao, prova, viva) => {
375
+ const parar = sessao && viva ? { ...sessao, thread } : null;
376
+ const morta = sessao && !viva ? { sessionId: sessao.sessionId, fase: (fase ?? 'GOAL'), runtime: sessao.runtime, perfil: sessao.perfil } : null;
377
+ if (!fase)
378
+ return { acao: 'aguardar', trabalho, thread, detalhe: `${prova}; a fase seguinte é da entrega (N5)` };
379
+ const feitas = (0, noctum_supervisao_1.retomadasDe)(sup, trabalho, fase);
380
+ const semTurno = motivo === 'sem-primeiro-turno'
381
+ ? sup.retomadas.filter(r => r.trabalho === trabalho && r.motivo === 'sem-primeiro-turno').length : 0;
382
+ if (motivo === 'sem-primeiro-turno' && semTurno >= parametros.retomadasSemPrimeiroTurno) {
383
+ return { acao: 'isolar', trabalho, thread, motivo: 'sem-primeiro-turno', parar,
384
+ detalhe: `${prova}; a sessão não começou o turno ${semTurno + 1} vez(es): a causa costuma ser de ambiente (diálogo de servidor MCP novo, ` +
385
+ 'credencial) e o dono resolve' };
386
+ }
387
+ if (feitas >= parametros.tetoDeRetomadas) {
388
+ return { acao: 'isolar', trabalho, thread, motivo: 'retomadas-esgotadas', parar,
389
+ detalhe: `${prova}; ${feitas} retomada(s) da fase ${fase} já autorizadas, e o teto é ${parametros.tetoDeRetomadas}` };
390
+ }
391
+ return { acao: 'retomar', trabalho, thread, fase, tentativa: feitas + 1, motivo, sessao: sessao?.sessionId ?? null, aposReserva, parar, morta, prova };
392
+ };
393
+ // NA3 da rodada 2: a última reserva do trabalho recusada pelo núcleo, com o bloqueio do despacho no ledger depois do
394
+ // último despacho, numa thread já despachada. A retomada é contada; a recusa repetida isola no teto (B4) e a de ambiente
395
+ // da máquina para o arco (D15); a que passa (a conta, a vaga, a condução) se repete na cadência do arco.
396
+ const ultimaReserva = estado.despachos.filter(x => x.trabalho === trabalho).sort((a, b) => b.seq - a.seq)[0];
397
+ const bloqueio = (0, conducao_sentinela_1.bloqueioDepoisDoDespacho)(eventos);
398
+ // NA3b da rodada 3: qualquer despacho vale (a thread que o dono despachou antes do arco também), como na B2.
399
+ if (ultimaReserva?.desfecho === 'recusado' && recusaDoNucleo(ultimaReserva.motivo) && bloqueio?.deDespacho &&
400
+ eventos.some(e => e.tipo === 'phase_dispatch')) {
401
+ decisoes.push(retomar(ultimaReserva.fase, 'despacho-falho', null, `o despacho do ${ultimaReserva.fase} foi recusado pelo núcleo (${ultimaReserva.motivo}) depois do último lançamento`, false));
402
+ continue;
403
+ }
404
+ // B2 do CHECK: o último lançamento do arco, despacho ou falha do despacho.
405
+ const lancamentos = eventos.filter(e => (e.tipo === 'phase_dispatch' || e.tipo === 'phase_dispatch_failed') && e.correlacao === correlacao);
406
+ const d = lancamentos.at(-1);
407
+ if (!d)
408
+ continue;
409
+ const fase = (texto(d.fase) ?? t.faseAtual);
410
+ const runtime = texto(d.runtime) ?? 'claude-bg', conta = perfilDe(d);
411
+ const sid = texto(d.sessionId);
412
+ if (!sid) {
413
+ // A falha sem id não abriu nada (a conta, o limite de uso) ou já foi reconciliada como sem lançamento. Na thread que
414
+ // nunca teve despacho a fila da N2 a despacha de novo; na já despachada, a Sentinela pede retomar, e quem autoriza é aqui.
415
+ if (d.tipo !== 'phase_dispatch_failed' || !eventos.some(e => e.tipo === 'phase_dispatch'))
416
+ continue;
417
+ decisoes.push(retomar(fase, 'despacho-falho', null, `o despacho do ${fase} falhou sem abrir sessão: ${String(d.erro ?? 'sem detalhe').slice(0, 160)}`, false));
418
+ continue;
419
+ }
420
+ const desde = Date.parse(d.ts);
421
+ const sessao = { sessionId: sid, runtime, perfil: conta };
422
+ const fim = eventos.some(e => e.tipo === 'sessao_morta' && e.sessionId === sid && Date.parse(e.ts) >= desde);
423
+ // A espera do condutor é a do último `phase_dispatch`: vale só quando ele é o último lançamento.
424
+ const espera = d.tipo === 'phase_dispatch' ? (0, parado_no_condutor_1.esperaDoCondutor)(t, eventos, quando) : null;
425
+ if (espera?.tipo === 'concluida' || eventos.some(e => e.tipo === 'phase_result' && e.sessionId === sid && Date.parse(e.ts) >= desde &&
426
+ (e.classificacao === 'fase_concluida' || (e.classificacao === undefined && e.ok === true))))
427
+ continue;
428
+ const p = provaDaSessao(leitura, sid, conta, runtime, d.ts, parametros.ausenciaMinimaMs);
429
+ // A última atividade da própria sessão (ou o lançamento): a carência conta dela.
430
+ const ultimaAtividade = Math.max(desde, ...eventos.filter(e => e.sessionId === sid).map(e => Date.parse(e.ts)).filter(Number.isFinite));
431
+ if (fim || p.estado === 'morta') {
432
+ // B5 do CHECK: o estado `done` vem antes do resultado que o observador grava; sem `sessao_morta` provada antes, a
433
+ // morte só vira retomada depois da carência desde a última atividade da sessão.
434
+ if (!fim && agora - ultimaAtividade < parametros.carenciaMs) {
435
+ decisoes.push({ acao: 'aguardar', trabalho, thread, detalhe: `a sessão ${sid} acabou há pouco (${p.detalhe}): o resultado da fase pode ainda chegar` });
436
+ continue;
437
+ }
438
+ decisoes.push(retomar(faseDaRetomada(raiz, t, eventos, fase, d.ts), d.tipo === 'phase_dispatch_failed' ? 'despacho-falho' : 'sessao-morta', fim ? null : sessao, fim ? `a sessão ${sid} já tem sessao_morta no ledger` : `a sessão ${sid} acabou sem resultado: ${p.detalhe}`, false));
439
+ continue;
440
+ }
441
+ if (p.estado === 'desconhecida') {
442
+ decisoes.push({ acao: 'aguardar', trabalho, thread, detalhe: `a sessão ${sid} sem prova do runtime: ${p.detalhe}` });
443
+ continue;
444
+ }
445
+ const trabalhando = p.estado === 'viva' && p.trabalhando;
446
+ // D11: o fim de turno sem resultado (o Stop sem `phase_result`, ou o fim de turno que o observador viu com a sessão
447
+ // parada), depois da carência e com a sessão sem trabalhar no runtime; a que voltou a trabalhar segue.
448
+ if (espera?.tipo === 'stop-sem-resultado' || espera?.tipo === 'espera-do-observador') {
449
+ if (agora - Date.parse(espera.fimDoTurnoEm) >= parametros.carenciaMs && !trabalhando) {
450
+ decisoes.push(retomar(faseDaRetomada(raiz, t, eventos, fase, d.ts), 'fim-de-turno-sem-resultado', sessao, `o turno da sessão ${sid} acabou sem resultado em ${(0, horario_1.formatarDataHora)(espera.fimDoTurnoEm)} e ela não trabalha: ${p.detalhe}`, true));
451
+ }
452
+ continue;
453
+ }
454
+ // O CHECK sem veredito é do passo da N2 (a Sentinela pede o CHECK de novo).
455
+ if (espera)
456
+ continue;
457
+ // D12: no turno, a sessão viva (trabalhando ou parada num diálogo antes de começar) é presa sem nenhuma atividade desde o
458
+ // despacho além do prazo do primeiro turno, ou sem evento de produto além do prazo da fase.
459
+ const ativa = eventos.some(e => Date.parse(e.ts) > desde && ((ATIVIDADE.includes(e.tipo) && e.sessionId === sid) || EVENTOS_DE_AVANCO.includes(e.tipo)));
460
+ if (!ativa && agora - desde >= parametros.primeiroTurnoMs) {
461
+ decisoes.push(retomar(fase, 'sem-primeiro-turno', sessao, `a sessão ${sid} não deu sinal nenhum em ${Math.round((agora - desde) / 60000)} min depois do despacho (${p.detalhe})`, true));
462
+ continue;
463
+ }
464
+ const parado = agora - ultimoAvanco(raiz, thread, eventos, desde);
465
+ if (ativa && parado >= parametros.semAvancoMs) {
466
+ decisoes.push(retomar(faseDaRetomada(raiz, t, eventos, fase, d.ts), 'sem-avanco', sessao, `a sessão ${sid} segue sem evento de produto há ${Math.round(parado / 60000)} min (prazo ${Math.round(parametros.semAvancoMs / 60000)} min; ${p.detalhe})`, true));
467
+ }
468
+ }
469
+ return decisoes;
470
+ }
471
+ /** D15: as recusas de ambiente seguidas desde o último despacho concluído do arco, e se elas param o arco. */
472
+ function paradaPorAmbiente(sup, estado) {
473
+ const parametros = sup.parametros;
474
+ if (!parametros)
475
+ return null;
476
+ const ultimo = Math.max(...estado.despachos.filter(d => d.desfecho === 'concluido' && d.concluidoEm).map(d => Date.parse(d.concluidoEm)), -Infinity);
477
+ const seguidas = sup.recusasDeAmbiente.filter(r => Date.parse(r.em) > ultimo);
478
+ if (seguidas.length < parametros.recusasDeAmbienteQueParam)
479
+ return null;
480
+ return { motivo: 'ambiente-do-despacho', detalhe: `${seguidas.length} despachos seguidos recusados pelo ambiente de quem despacha ` +
481
+ `(${seguidas.at(-1).detalhe.slice(0, 160)}): o dono confere o ambiente (o node no PATH, a instalação do MCP) antes de outro arco` };
482
+ }
483
+ /**
484
+ * A recusa do núcleo que vem do ambiente da máquina de quem despacha, e não do trabalho: o MCP divergente com outro `node`
485
+ * no PATH, a instalação do MCP, o projeto sem o perfil do instalador. Os erros de contexto da thread (o escopo, a worktree,
486
+ * a identidade) contam como recusa do trabalho, que isola no teto (RA2 da rodada 3).
487
+ */
488
+ function recusaDeAmbiente(detalhe) {
489
+ return /runtime\.context\.(conflict|installation|missing)/.test(detalhe);
490
+ }
491
+ /**
492
+ * D17: o pedido aguardando na fila durável que é do arco (a correlação dele) nunca é redespachado pelo `ork retry resume`.
493
+ * A retomada da fase sai pelo passo do arco, autorizada como qualquer despacho que falhou sem abrir sessão (B2 do CHECK,
494
+ * `decidirSessoes`), com a conta do limite de uso fora até o `liberaEm`. Aqui o pedido só sai da fila: depois de um
495
+ * despacho do arco na thread, como retomado; com o arco encerrado, com o motivo (retomar exige o dono).
496
+ */
497
+ function decidirPedidos(raiz, arco, estado) {
498
+ const correlacao = (0, noctum_supervisao_1.correlacaoDoArco)(arco);
499
+ const ultimos = new Map();
500
+ for (const p of (0, ratelimit_1.lerFilaDeRetomada)(raiz))
501
+ ultimos.set(p.id, p);
502
+ const encerrado = estado.situacao === 'revogado' || estado.situacao === 'expirado';
503
+ const decisoes = [];
504
+ for (const p of ultimos.values()) {
505
+ if (p.estado !== 'aguardando' || p.correlacao !== correlacao)
506
+ continue;
507
+ if (encerrado) {
508
+ decisoes.push({ acao: 'cancelar', pedido: p.id, thread: p.thread,
509
+ motivo: `o arco ${arco} terminou (${estado.situacao}): retomar a fase ${p.fase} da thread ${p.thread} exige o dono` });
510
+ continue;
511
+ }
512
+ let eventos;
513
+ try {
514
+ eventos = (0, ledger_1.lerLedger)((0, thread_1.dirThread)(raiz, p.thread));
515
+ }
516
+ catch {
517
+ continue;
518
+ }
519
+ const retomado = eventos.find(e => e.tipo === 'phase_dispatch' && e.correlacao === correlacao && Date.parse(e.ts) > Date.parse(p.criadoEm));
520
+ if (retomado) {
521
+ decisoes.push({ acao: 'cancelar', pedido: p.id, thread: p.thread,
522
+ motivo: `retomado pelo arco ${arco} no despacho ${String(retomado.eventId ?? (0, horario_1.formatarDataHora)(retomado.ts))} (${String(retomado.fase)})` });
523
+ }
524
+ }
525
+ return decisoes;
526
+ }
527
+ /**
528
+ * O remoto que o `git ls-remote` aceita do log: nome ou endereço sem espaço, que nunca começa com `-` (senão viraria opção
529
+ * do git, como `--upload-pack`).
530
+ */
531
+ const REMOTO_SEGURO = /^[A-Za-z0-9_][A-Za-z0-9._/:@~+-]{0,299}$/;
532
+ /** O `git ls-remote` da branch exata, com prazo e sem pedir credencial no terminal. */
533
+ const lerRemotoPadrao = (remoto, branch, cwd) => {
534
+ if (!REMOTO_SEGURO.test(remoto) || (0, noctum_envelope_1.nomeDaBranch)(branch) === undefined)
535
+ return { ok: false, detalhe: 'remoto ou branch fora do formato seguro' };
536
+ const r = (0, util_1.exec)('git', ['ls-remote', '--heads', remoto, `refs/heads/${branch}`], cwd, 30_000, { ...process.env, GIT_TERMINAL_PROMPT: '0' });
537
+ if (!r.ok)
538
+ return { ok: false, detalhe: `git ls-remote saiu ${r.code}: ${(r.stderr.trim().split('\n')[0] ?? '').slice(0, 160)}` };
539
+ const linha = r.stdout.split('\n').find(l => l.endsWith(`\trefs/heads/${branch}`));
540
+ const sha = linha?.split('\t')[0] ?? null;
541
+ return { ok: true, sha: sha && /^[0-9a-f]{40}$/.test(sha) ? sha : null };
542
+ };
543
+ exports.lerRemotoPadrao = lerRemotoPadrao;
544
+ const chaveDoEfeito = (e) => JSON.stringify([e.acao, e.repositorio, e.remoto, e.branch, e.sha, e.trabalho]);
545
+ /** D18: registra o efeito de resultado incerto (o push que estourou o prazo ou caiu sem resposta) e devolve o id dele. */
546
+ function registrarEfeitoIncerto(carregado, arco, efeito, quando = new Date().toISOString()) {
547
+ if (!/^[0-9a-f]{40}$/.test(efeito.sha) || (0, noctum_envelope_1.nomeDaBranch)(efeito.branch) === undefined || !REMOTO_SEGURO.test(efeito.remoto)) {
548
+ throw new noctum_supervisao_1.RecusaDaSupervisao('noctum.supervisao.invalida', 'efeito incerto exige o sha completo, a branch e o remoto, no formato seguro');
549
+ }
550
+ const id = 'E-' + (0, node_crypto_1.createHash)('sha256').update(JSON.stringify([arco, chaveDoEfeito(efeito), quando])).digest('hex').slice(0, 12);
551
+ (0, noctum_supervisao_1.gravarNaSupervisao)(carregado, arco, [{ tipo: 'efeito_incerto', id, efeito: { ...efeito, detalhe: efeito.detalhe.slice(0, 200) } }], quando);
552
+ return id;
553
+ }
554
+ /**
555
+ * D18: o remoto decide o efeito incerto, e o desfecho vai ao log. Igual ao sha: confirmado, e nunca se repete. Branch
556
+ * ausente: a repetição é permitida enquanto os efeitos do mesmo push não passam do teto, e quem repete confere o envelope
557
+ * de novo; no teto, o trabalho é isolado. Outro sha na branch: divergente, isolado (alguém escreveu ali). Sem leitura:
558
+ * lacuna, lida de novo no tique seguinte, e isolada no teto. Desfecho final não é relido.
559
+ */
560
+ function reconciliarEfeito(carregado, arco, id, opcoes = {}) {
561
+ const quando = opcoes.quando ?? new Date().toISOString();
562
+ const sup = (0, noctum_supervisao_1.lerSupervisao)(carregado.raiz, arco);
563
+ const ef = sup.efeitos.get(id);
564
+ if (!ef)
565
+ throw new noctum_supervisao_1.RecusaDaSupervisao('noctum.supervisao.invalida', `efeito ${id} sem registro neste arco`);
566
+ const teto = sup.parametros?.leiturasDoEfeito ?? 2;
567
+ const ultimo = ef.desfechos.at(-1);
568
+ if (ultimo && ultimo.desfecho !== 'sem-leitura') {
569
+ return { id, desfecho: ultimo.desfecho, podeRepetir: false, isolado: sup.isolados.has(ef.efeito.trabalho), prova: ultimo.prova };
570
+ }
571
+ const r = (opcoes.lerRemoto ?? exports.lerRemotoPadrao)(ef.efeito.remoto, ef.efeito.branch, opcoes.cwd ?? carregado.raiz);
572
+ const desfecho = !r.ok ? 'sem-leitura' : r.sha === ef.efeito.sha ? 'confirmado' : r.sha === null ? 'ausente' : 'divergente';
573
+ const prova = !r.ok ? r.detalhe : r.sha === null ? `a branch ${ef.efeito.branch} não existe no remoto ${ef.efeito.remoto}`
574
+ : `o remoto ${ef.efeito.remoto} tem ${r.sha} na branch ${ef.efeito.branch}`;
575
+ // As tentativas do mesmo push (a mesma chave) contam juntas: um efeito novo não zera o teto.
576
+ const mesmas = [...sup.efeitos.values()].filter(x => chaveDoEfeito(x.efeito) === chaveDoEfeito(ef.efeito)).length;
577
+ const leituras = ef.desfechos.filter(d => d.desfecho === 'sem-leitura').length + 1;
578
+ const podeRepetir = desfecho === 'ausente' && mesmas < teto;
579
+ const motivo = desfecho === 'divergente' ? 'efeito-divergente' : desfecho === 'sem-leitura' && leituras >= teto ? 'efeito-sem-leitura'
580
+ : desfecho === 'ausente' && !podeRepetir ? 'efeito-esgotado' : null;
581
+ const linhas = [{ tipo: 'efeito_reconciliado', id, desfecho, prova: prova.slice(0, 300) }];
582
+ if (motivo)
583
+ linhas.push({ tipo: 'isolado', trabalho: ef.efeito.trabalho, thread: ef.efeito.thread, motivo,
584
+ detalhe: `${ef.efeito.acao} de ${ef.efeito.sha.slice(0, 12)} em ${ef.efeito.branch}: ${prova}`.slice(0, 300) });
585
+ (0, noctum_supervisao_1.gravarNaSupervisao)(carregado, arco, linhas, quando);
586
+ return { id, desfecho, podeRepetir, isolado: motivo !== null || sup.isolados.has(ef.efeito.trabalho), prova };
587
+ }
588
+ /** Os efeitos incertos ainda sem desfecho final: o vigia os relê a cada tique. */
589
+ function efeitosAbertos(sup) {
590
+ return [...sup.efeitos.values()].filter(e => {
591
+ const ultimo = e.desfechos.at(-1);
592
+ return (!ultimo || ultimo.desfecho === 'sem-leitura') && !sup.isolados.has(e.efeito.trabalho);
593
+ }).map(e => e.id);
594
+ }