@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
@@ -1,10 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ID_DO_TRABALHO = exports.ID_DO_ARCO = exports.RecusaDoArco = exports.MOTIVOS_DE_RECUSA = exports.CANAIS_DO_RELATORIO = exports.UNIDADES = exports.EXCLUSOES_FIXAS = exports.PERMISSOES = exports.JANELA_MAXIMA_H = exports.JANELA_MINIMA_H = exports.MAXIMO_DE_SESSOES_DA_MAQUINA = exports.TETO_DOS_PEDIDOS_DO_ARCO = exports.TETO_DO_PEDIDO_DO_TRABALHO = exports.MAXIMO_DE_TRABALHOS = exports.CONTRATO_ENVELOPE_V2 = exports.CONTRATO_ENVELOPE = void 0;
3
+ exports.ID_DO_TRABALHO = exports.ID_DO_ARCO = exports.RecusaDoArco = exports.MOTIVOS_DE_RECUSA = exports.CANAIS_DO_RELATORIO = exports.UNIDADES = exports.EXCLUSOES_FIXAS = exports.PERMISSOES = exports.JANELA_MAXIMA_H = exports.JANELA_MINIMA_H = exports.MAXIMO_DE_SESSOES_DA_MAQUINA = exports.TETO_DOS_PEDIDOS_DO_ARCO = exports.TETO_DO_PEDIDO_DO_TRABALHO = exports.MAXIMO_DE_TRABALHOS = exports.MAXIMO_DE_MAQUINAS = exports.CONTRATO_ENVELOPE_V3 = exports.CONTRATO_ENVELOPE_V2 = exports.CONTRATO_ENVELOPE = void 0;
4
4
  exports.nomeDaBranch = nomeDaBranch;
5
5
  exports.exclusaoDaAcao = exclusaoDaAcao;
6
6
  exports.shaDoPedido = shaDoPedido;
7
+ exports.chavePublicaValida = chavePublicaValida;
8
+ exports.impressaoDaChave = impressaoDaChave;
7
9
  exports.envelopeComTrabalhos = envelopeComTrabalhos;
10
+ exports.envelopeMultimaquina = envelopeMultimaquina;
8
11
  exports.validarPedidoDeAbertura = validarPedidoDeAbertura;
9
12
  exports.canonico = canonico;
10
13
  exports.hashDoEnvelope = hashDoEnvelope;
@@ -28,12 +31,19 @@ exports.resumoDoEnvelope = resumoDoEnvelope;
28
31
  * RM-058, fatia N2 (D1 da ork-rm058n2): a `v2` acrescenta o que a `v1` deixou para uma versão nova, a lista
29
32
  * fechada de trabalhos do arco e os limites de concorrência. A `v1` segue valendo como estava, byte a byte, e
30
33
  * um arco autorizado nela não despacha nada.
34
+ *
35
+ * RM-058, fatia N3 (D2 da ork-rm058n3): a `v3` acrescenta as máquinas do arco, cada uma com a chave pública que assina
36
+ * no ramo de coordenação, o limite de sessões por conta somando as máquinas e a máquina presa de um trabalho. É o que
37
+ * o dono autoriza que faz uma máquina confiar na outra: o retrato da rede é descoberta, nunca autorização.
31
38
  */
32
39
  const node_crypto_1 = require("node:crypto");
33
40
  const horario_1 = require("./horario");
34
41
  const runtime_profiles_1 = require("./runtime-profiles");
35
42
  exports.CONTRATO_ENVELOPE = 'ork.noctum-envelope/v1';
36
43
  exports.CONTRATO_ENVELOPE_V2 = 'ork.noctum-envelope/v2';
44
+ exports.CONTRATO_ENVELOPE_V3 = 'ork.noctum-envelope/v3';
45
+ /** D2 da ork-rm058n3: as máquinas de um arco são poucas e todas do dono. */
46
+ exports.MAXIMO_DE_MAQUINAS = 8;
37
47
  /**
38
48
  * D1 da ork-rm058n2: a lista do arco é fechada e curta. O dono lê cada pedido inteiro no resumo que autoriza (A7 do
39
49
  * CHECK), então o pedido é texto curto: cada um e a soma deles cabem numa leitura.
@@ -88,6 +98,24 @@ exports.MOTIVOS_DE_RECUSA = [
88
98
  'noctum.estado.adulterado',
89
99
  // RM-058 N2 (D11 e D12 da ork-rm058n2): o despacho fora da lista, conta ou runtime do envelope, e o orçamento.
90
100
  'noctum.despacho.fora-do-envelope', 'noctum.orcamento.esgotado', 'noctum.orcamento.sem-medicao',
101
+ // RM-058 N3 (ork-rm058n3): a coordenação entre máquinas, a reserva do item e a vez de integrar.
102
+ 'noctum.coordenacao.sem-remoto', 'noctum.coordenacao.adulterado', 'noctum.coordenacao.fora-do-envelope',
103
+ 'noctum.coordenacao.autoridade-ativa', 'noctum.coordenacao.geracao-vencida', 'noctum.coordenacao.ordem-viva',
104
+ 'noctum.coordenacao.ordem-vencida', 'noctum.coordenacao.maquina-sem-batida', 'noctum.coordenacao.vaga-global',
105
+ 'noctum.coordenacao.concorrencia', 'noctum.coordenacao.exige-passo-da-maquina', 'noctum.reserva.de-outra-maquina',
106
+ 'noctum.integracao.ocupada', 'noctum.integracao.base-mudou', 'noctum.integracao.fonte-mudou',
107
+ 'noctum.integracao.ja-entregue', 'noctum.integracao.nao-provada',
108
+ // GO-FIX da revisão da ork-rm058n3: a emenda ainda não autorizada aqui, a máquina revogada, a dependência e a recusa
109
+ // da ordem com motivo próprio, e o item que o roadmap não reconhece.
110
+ 'noctum.coordenacao.versao-nao-autorizada', 'noctum.coordenacao.revogada', 'noctum.coordenacao.dependencia-pendente',
111
+ 'noctum.coordenacao.maquina-recusou', 'noctum.reserva.item-invalido',
112
+ // Rodada 2 da revisão da ork-rm058n3: a fonte que chegou à base por um merge fora da base provada.
113
+ 'noctum.integracao.base-nao-provada',
114
+ // RM-058 N6 (D1 e D2 da ork-rm058n6): o arco encerrado e o #Noctum pedido sem envelope vigente ou como modo de thread.
115
+ 'noctum.efeito.encerrado', 'noctum.modo.sem-envelope', 'noctum.modo.lista-fechada',
116
+ // RM-057 (resposta curta, ork-rm057respost): a resposta curta a uma pergunta que o arco não tem, que outra substituiu ou
117
+ // que venceu. Nenhuma delas autoriza nem revoga nada.
118
+ 'noctum.pergunta.desconhecida', 'noctum.pergunta.substituida', 'noctum.pergunta.vencida',
91
119
  ];
92
120
  class RecusaDoArco extends Error {
93
121
  motivo;
@@ -349,7 +377,7 @@ function lerOrcamento(v, p) {
349
377
  chavesConhecidas(v, ['arco', 'porConta', 'porRuntime'], 'orcamento', p);
350
378
  const arco = lerTeto(v.arco, 'orcamento.arco', p);
351
379
  const porConta = lerSubtetos(v.porConta, 'conta', 'orcamento.porConta', x => ID_DE_CONTA.test(x), 'id de perfil de conta já autenticado', p);
352
- const porRuntime = lerSubtetos(v.porRuntime, 'runtime', 'orcamento.porRuntime', x => runtime_profiles_1.RUNTIMES_COM_PERFIL.includes(x), `runtime de assinatura: ${runtime_profiles_1.RUNTIMES_COM_PERFIL.join(', ')}`, p);
380
+ const porRuntime = lerSubtetos(v.porRuntime, 'runtime', 'orcamento.porRuntime', x => runtime_profiles_1.RUNTIMES_COM_INVENTARIO.includes(x), `runtime de assinatura: ${runtime_profiles_1.RUNTIMES_COM_INVENTARIO.join(', ')}`, p);
353
381
  // Subteto acima do teto do arco, na mesma unidade, não limita nada: é teto do arco disfarçado.
354
382
  for (const [nome, teto] of [...(porConta ?? []).map(s => [`conta ${s.conta}`, s.teto]),
355
383
  ...(porRuntime ?? []).map(s => [`runtime ${s.runtime}`, s.teto])]) {
@@ -418,11 +446,170 @@ function caractereQueEsconde(texto) {
418
446
  return null;
419
447
  }
420
448
  const codigoDoCaractere = (cp) => `U+${cp.toString(16).toUpperCase().padStart(4, '0')}`;
449
+ /** O nome de uma máquina, a mesma regra do `maquina.json` (`gravarConfigDaMaquina`). */
450
+ const NOME_DE_MAQUINA = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
451
+ const BASE64 = /^[A-Za-z0-9+/]{1,200}={0,2}$/;
452
+ /**
453
+ * D2 da ork-rm058n3: a chave pública Ed25519 em SPKI DER base64, só na forma canônica que o Node exporta, ou nada. Duas
454
+ * escritas da mesma chave dariam dois hashes para o mesmo envelope e driblariam a regra da chave repetida.
455
+ */
456
+ function chavePublicaValida(v) {
457
+ if (typeof v !== 'string' || !BASE64.test(v))
458
+ return undefined;
459
+ try {
460
+ const k = (0, node_crypto_1.createPublicKey)({ key: Buffer.from(v, 'base64'), format: 'der', type: 'spki' });
461
+ if (k.asymmetricKeyType !== 'ed25519')
462
+ return undefined;
463
+ return k.export({ format: 'der', type: 'spki' }).toString('base64') === v ? v : undefined;
464
+ }
465
+ catch {
466
+ return undefined;
467
+ }
468
+ }
469
+ /** O que o resumo mostra da chave de uma máquina: o começo do sha256 dos bytes dela, para o dono conferir. */
470
+ function impressaoDaChave(chave) {
471
+ return (0, node_crypto_1.createHash)('sha256').update(Buffer.from(chave, 'base64')).digest('hex').slice(0, 16);
472
+ }
473
+ /** D2 da ork-rm058n3: as máquinas do arco, na ordem do dono (desempata a distribuição), sem nome nem chave repetidos. */
474
+ function lerMaquinas(v, p) {
475
+ if (ausente(v) || (Array.isArray(v) && v.length === 0)) {
476
+ p.faltas.push('maquinas (cada máquina do arco: nome e chave pública)');
477
+ return undefined;
478
+ }
479
+ if (!Array.isArray(v)) {
480
+ p.invalidos.push('maquinas: precisa ser lista');
481
+ return undefined;
482
+ }
483
+ if (v.length > exports.MAXIMO_DE_MAQUINAS) {
484
+ p.invalidos.push(`maquinas: até ${exports.MAXIMO_DE_MAQUINAS} por arco`);
485
+ return undefined;
486
+ }
487
+ const lidas = [];
488
+ const nomes = new Set(), chaves = new Set();
489
+ v.forEach((m, i) => {
490
+ const onde = `maquinas[${i}]`;
491
+ if (!objeto(m)) {
492
+ p.invalidos.push(`${onde}: precisa ser { nome, chave }`);
493
+ return;
494
+ }
495
+ chavesConhecidas(m, ['nome', 'chave'], onde, p);
496
+ let ok = true;
497
+ if (ausente(m.nome)) {
498
+ p.faltas.push(`${onde}.nome`);
499
+ ok = false;
500
+ }
501
+ else if (typeof m.nome !== 'string' || !NOME_DE_MAQUINA.test(m.nome)) {
502
+ p.invalidos.push(`${onde}.nome: letras, dígitos, ponto, _ ou -, até 64`);
503
+ ok = false;
504
+ }
505
+ else if (nomes.has(m.nome.toLowerCase())) {
506
+ p.invalidos.push(`${onde}.nome: repetido`);
507
+ ok = false;
508
+ }
509
+ else
510
+ nomes.add(m.nome.toLowerCase());
511
+ if (ausente(m.chave)) {
512
+ p.faltas.push(`${onde}.chave (a chave pública da máquina)`);
513
+ ok = false;
514
+ }
515
+ else if (chavePublicaValida(m.chave) === undefined) {
516
+ p.invalidos.push(`${onde}.chave: chave pública Ed25519 em SPKI DER base64, na forma canônica`);
517
+ ok = false;
518
+ }
519
+ else if (chaves.has(m.chave)) {
520
+ p.invalidos.push(`${onde}.chave: a mesma chave em duas máquinas`);
521
+ ok = false;
522
+ }
523
+ else
524
+ chaves.add(m.chave);
525
+ if (ok)
526
+ lidas.push({ nome: m.nome, chave: m.chave });
527
+ });
528
+ return lidas.length === v.length ? lidas : undefined;
529
+ }
530
+ /**
531
+ * D2 da ork-rm058n3: o limite de cada conta somando as máquinas. Toda conta do orçamento tem o seu e nenhuma outra entra;
532
+ * acima da soma dos limites locais ele não limita nada, como o subteto acima do teto.
533
+ */
534
+ function lerCapacidadeGlobal(v, orcamento, capacidade, quantasMaquinas, p) {
535
+ const falta = 'capacidadeGlobal.porConta (sessões ao mesmo tempo por conta, somando as máquinas)';
536
+ if (ausente(v)) {
537
+ p.faltas.push(falta);
538
+ return undefined;
539
+ }
540
+ if (!objeto(v)) {
541
+ p.invalidos.push('capacidadeGlobal: precisa ser { porConta }');
542
+ return undefined;
543
+ }
544
+ chavesConhecidas(v, ['porConta'], 'capacidadeGlobal', p);
545
+ const bruto = v.porConta;
546
+ if (ausente(bruto) || (Array.isArray(bruto) && bruto.length === 0)) {
547
+ p.faltas.push(falta);
548
+ return undefined;
549
+ }
550
+ if (!Array.isArray(bruto)) {
551
+ p.invalidos.push('capacidadeGlobal.porConta: precisa ser lista');
552
+ return undefined;
553
+ }
554
+ const doOrcamento = orcamento?.porConta.map(s => s.conta);
555
+ const itens = [];
556
+ const vistos = new Set();
557
+ bruto.forEach((s, i) => {
558
+ const aqui = `capacidadeGlobal.porConta[${i}]`;
559
+ if (!objeto(s)) {
560
+ p.invalidos.push(`${aqui}: precisa ser { conta, sessoes }`);
561
+ return;
562
+ }
563
+ chavesConhecidas(s, ['conta', 'sessoes'], aqui, p);
564
+ let ok = true;
565
+ if (ausente(s.conta)) {
566
+ p.faltas.push(`${aqui}.conta`);
567
+ ok = false;
568
+ }
569
+ else if (typeof s.conta !== 'string') {
570
+ p.invalidos.push(`${aqui}.conta: texto`);
571
+ ok = false;
572
+ }
573
+ else if (vistos.has(s.conta.toLowerCase())) {
574
+ p.invalidos.push(`${aqui}.conta: repetida`);
575
+ ok = false;
576
+ }
577
+ else if (doOrcamento && !doOrcamento.includes(s.conta)) {
578
+ p.invalidos.push(`${aqui}.conta: ${s.conta} não está no orçamento, e só entra aqui quem tem teto`);
579
+ ok = false;
580
+ }
581
+ else
582
+ vistos.add(s.conta.toLowerCase());
583
+ const local = typeof s.conta === 'string' ? capacidade?.porConta.find(x => x.conta === s.conta)?.sessoes : undefined;
584
+ const soma = local !== undefined && quantasMaquinas !== undefined ? local * quantasMaquinas : exports.MAXIMO_DE_SESSOES_DA_MAQUINA * exports.MAXIMO_DE_MAQUINAS;
585
+ if (ausente(s.sessoes)) {
586
+ p.faltas.push(`${aqui}.sessoes`);
587
+ ok = false;
588
+ }
589
+ else if (typeof s.sessoes !== 'number' || !Number.isSafeInteger(s.sessoes) || s.sessoes < 1) {
590
+ p.invalidos.push(`${aqui}.sessoes: inteiro a partir de 1`);
591
+ ok = false;
592
+ }
593
+ else if (s.sessoes > soma) {
594
+ p.invalidos.push(`${aqui}.sessoes: passa da soma dos limites locais da conta (${soma}) e não limita nada`);
595
+ ok = false;
596
+ }
597
+ if (ok)
598
+ itens.push({ conta: s.conta, sessoes: s.sessoes });
599
+ });
600
+ for (const conta of (doOrcamento ?? []).filter(n => !vistos.has(n.toLowerCase()))) {
601
+ p.faltas.push(`capacidadeGlobal.porConta (${conta}, que está no orçamento)`);
602
+ }
603
+ return itens.length === bruto.length ? { porConta: itens.sort((a, b) => a.conta.localeCompare(b.conta, 'en')) } : undefined;
604
+ }
421
605
  /**
422
606
  * D1 e D2 da ork-rm058n2: a lista fechada de trabalhos. A ordem da lista é do dono e entra no hash, porque a
423
607
  * posição desempata a fila (D3); a ordem dentro de `dependeDe` não muda nada e é normalizada.
608
+ *
609
+ * D2 da ork-rm058n3: na `v3`, `v3.maquinas` traz os nomes do envelope (ou `null`, quando eles não se leram) e o trabalho
610
+ * pode prender uma máquina; o de `thread` precisa dela, e o mesmo item preso a duas máquinas é recusado.
424
611
  */
425
- function lerTrabalhos(v, p) {
612
+ function lerTrabalhos(v, p, v3 = null) {
426
613
  if (ausente(v) || (Array.isArray(v) && v.length === 0)) {
427
614
  p.faltas.push('trabalhos (a lista fechada do arco: id, item, prioridade, dependeDe e thread ou pedido)');
428
615
  return undefined;
@@ -443,7 +630,7 @@ function lerTrabalhos(v, p) {
443
630
  p.invalidos.push(`${onde}: precisa ser { id, item, prioridade, dependeDe, thread ou pedido }`);
444
631
  return;
445
632
  }
446
- chavesConhecidas(t, ['id', 'item', 'prioridade', 'dependeDe', 'thread', 'pedido'], onde, p);
633
+ chavesConhecidas(t, ['id', 'item', 'prioridade', 'dependeDe', 'thread', 'pedido', ...(v3 ? ['maquina'] : [])], onde, p);
447
634
  let ok = true;
448
635
  if (ausente(t.id)) {
449
636
  p.faltas.push(`${onde}.id`);
@@ -529,14 +716,45 @@ function lerTrabalhos(v, p) {
529
716
  }
530
717
  else if (typeof t.item === 'string')
531
718
  pedidos.add(`${t.item}|${shaDoPedido(t.pedido)}`);
719
+ let maquina;
720
+ if (v3) {
721
+ if (ausente(t.maquina)) {
722
+ if (comThread) {
723
+ p.faltas.push(`${onde}.maquina (a máquina em que a thread mora)`);
724
+ ok = false;
725
+ }
726
+ }
727
+ else if (typeof t.maquina !== 'string' || !NOME_DE_MAQUINA.test(t.maquina)) {
728
+ p.invalidos.push(`${onde}.maquina: nome de uma das máquinas do arco`);
729
+ ok = false;
730
+ }
731
+ else if (v3.maquinas && !v3.maquinas.has(t.maquina)) {
732
+ p.invalidos.push(`${onde}.maquina: ${t.maquina} não está em maquinas`);
733
+ ok = false;
734
+ }
735
+ else
736
+ maquina = t.maquina;
737
+ }
532
738
  if (ok) {
533
739
  lidos.push({ id: t.id, item: t.item, prioridade: t.prioridade,
534
740
  dependeDe: [...t.dependeDe].sort(),
535
- ...(comThread ? { thread: t.thread } : { pedido: t.pedido.trim() }) });
741
+ ...(comThread ? { thread: t.thread } : { pedido: t.pedido.trim() }),
742
+ ...(maquina ? { maquina } : {}) });
536
743
  }
537
744
  });
538
745
  if (lidos.length !== v.length)
539
746
  return undefined;
747
+ if (v3) {
748
+ // A reserva da RM-047 é do item numa máquina só: o mesmo item preso a duas máquinas nunca andaria nas duas.
749
+ const presas = new Map();
750
+ for (const t of lidos)
751
+ if (t.maquina)
752
+ presas.set(t.item, (presas.get(t.item) ?? new Set()).add(t.maquina));
753
+ for (const [item, maquinas] of presas) {
754
+ if (maquinas.size > 1)
755
+ p.invalidos.push(`trabalhos: o item ${item} está preso a ${[...maquinas].join(' e ')}; a reserva do item é de uma máquina só`);
756
+ }
757
+ }
540
758
  const somaDosPedidos = lidos.reduce((n, t) => n + (t.pedido ? Buffer.byteLength(t.pedido, 'utf8') : 0), 0);
541
759
  if (somaDosPedidos > exports.TETO_DOS_PEDIDOS_DO_ARCO) {
542
760
  p.invalidos.push(`trabalhos: os pedidos somam ${somaDosPedidos} bytes; até ${exports.TETO_DOS_PEDIDOS_DO_ARCO} por arco`);
@@ -666,9 +884,13 @@ function lerCapacidade(v, orcamento, p) {
666
884
  const porRuntime = lerLimites(v.porRuntime, 'runtime', 'capacidade.porRuntime', orcamento?.porRuntime.map(s => s.runtime));
667
885
  return maquina !== undefined && revisao !== undefined && porConta && porRuntime ? { maquina, porConta, porRuntime, revisao } : undefined;
668
886
  }
669
- /** O conteúdo traz a lista e os limites da `v2`? Só ele despacha (D1 da ork-rm058n2). */
887
+ /** O conteúdo traz a lista e os limites da `v2` (ou da `v3`, que os mantém)? Só ele despacha (D1 da ork-rm058n2). */
670
888
  function envelopeComTrabalhos(c) {
671
- return c.contrato === exports.CONTRATO_ENVELOPE_V2 && Array.isArray(c.trabalhos) && !!c.capacidade;
889
+ return (c.contrato === exports.CONTRATO_ENVELOPE_V2 || c.contrato === exports.CONTRATO_ENVELOPE_V3) && Array.isArray(c.trabalhos) && !!c.capacidade;
890
+ }
891
+ /** RM-058 N3: o conteúdo é de um arco entre máquinas (`v3`, com as máquinas e o limite global)? */
892
+ function envelopeMultimaquina(c) {
893
+ return c.contrato === exports.CONTRATO_ENVELOPE_V3 && envelopeComTrabalhos(c) && Array.isArray(c.maquinas) && !!c.capacidadeGlobal;
672
894
  }
673
895
  /**
674
896
  * Valida o pedido de abertura e devolve o conteúdo normalizado que o dono autoriza.
@@ -681,11 +903,14 @@ function validarPedidoDeAbertura(v) {
681
903
  const p = { faltas: [], ilimitados: [], excluidas: [], invalidos: [] };
682
904
  if (!objeto(v))
683
905
  throw new RecusaDoArco('noctum.envelope.invalido', 'o pedido de abertura precisa ser um objeto');
684
- // Na `v1`, `trabalhos` e `capacidade` continuam campo desconhecido, como antes da N2.
685
- const v2 = v.contrato === exports.CONTRATO_ENVELOPE_V2;
686
- chavesConhecidas(v, ['contrato', 'arco', 'janela', 'destinos', 'orcamento', 'relatorio', ...(v2 ? ['trabalhos', 'capacidade'] : [])], 'pedido', p);
687
- if (v.contrato !== exports.CONTRATO_ENVELOPE && !v2)
688
- p.invalidos.push(`contrato: precisa ser ${exports.CONTRATO_ENVELOPE} ou ${exports.CONTRATO_ENVELOPE_V2}`);
906
+ // Na `v1`, `trabalhos` e `capacidade` continuam campo desconhecido, como antes da N2; fora da `v3`, as máquinas também.
907
+ const v3 = v.contrato === exports.CONTRATO_ENVELOPE_V3;
908
+ const v2 = v.contrato === exports.CONTRATO_ENVELOPE_V2 || v3;
909
+ chavesConhecidas(v, ['contrato', 'arco', 'janela', 'destinos', 'orcamento', 'relatorio', ...(v2 ? ['trabalhos', 'capacidade'] : []),
910
+ ...(v3 ? ['maquinas', 'capacidadeGlobal'] : [])], 'pedido', p);
911
+ if (v.contrato !== exports.CONTRATO_ENVELOPE && !v2) {
912
+ p.invalidos.push(`contrato: precisa ser ${exports.CONTRATO_ENVELOPE}, ${exports.CONTRATO_ENVELOPE_V2} ou ${exports.CONTRATO_ENVELOPE_V3}`);
913
+ }
689
914
  let arco;
690
915
  if (ausente(v.arco))
691
916
  p.faltas.push('arco (identificador do arco)');
@@ -697,8 +922,10 @@ function validarPedidoDeAbertura(v) {
697
922
  const destinos = lerDestinos(v.destinos, p);
698
923
  const orcamento = lerOrcamento(v.orcamento, p);
699
924
  const relatorio = lerRelatorio(v.relatorio, p);
700
- const trabalhos = v2 ? lerTrabalhos(v.trabalhos, p) : undefined;
925
+ const maquinas = v3 ? lerMaquinas(v.maquinas, p) : undefined;
926
+ const trabalhos = v2 ? lerTrabalhos(v.trabalhos, p, v3 ? { maquinas: maquinas ? new Set(maquinas.map(m => m.nome)) : null } : null) : undefined;
701
927
  const capacidade = v2 ? lerCapacidade(v.capacidade, orcamento, p) : undefined;
928
+ const capacidadeGlobal = v3 ? lerCapacidadeGlobal(v.capacidadeGlobal, orcamento, capacidade, maquinas?.length, p) : undefined;
702
929
  const partes = [
703
930
  p.excluidas.length ? `ação excluída: ${p.excluidas.join('; ')}` : '',
704
931
  p.ilimitados.length ? `teto ilimitado não é aceito: ${p.ilimitados.join('; ')}` : '',
@@ -711,9 +938,10 @@ function validarPedidoDeAbertura(v) {
711
938
  : p.faltas.length ? 'noctum.envelope.valores-ausentes' : 'noctum.envelope.invalido';
712
939
  throw new RecusaDoArco(motivo, `${partes.join(' | ')}. Nenhum valor do dono é presumido.`);
713
940
  }
714
- return { contrato: v2 ? exports.CONTRATO_ENVELOPE_V2 : exports.CONTRATO_ENVELOPE, arco: arco, janela: janela, destinos: destinos,
715
- orcamento: orcamento, relatorio: relatorio, exclusoes: [...exports.EXCLUSOES_FIXAS],
716
- ...(v2 ? { trabalhos: trabalhos, capacidade: capacidade } : {}) };
941
+ return { contrato: v3 ? exports.CONTRATO_ENVELOPE_V3 : v2 ? exports.CONTRATO_ENVELOPE_V2 : exports.CONTRATO_ENVELOPE, arco: arco, janela: janela,
942
+ destinos: destinos, orcamento: orcamento, relatorio: relatorio, exclusoes: [...exports.EXCLUSOES_FIXAS],
943
+ ...(v2 ? { trabalhos: trabalhos, capacidade: capacidade } : {}),
944
+ ...(v3 ? { maquinas: maquinas, capacidadeGlobal: capacidadeGlobal } : {}) };
717
945
  }
718
946
  /** JSON com as chaves em ordem: o mesmo conteúdo dá o mesmo hash, qualquer que seja a ordem de entrada. */
719
947
  function canonico(v) {
@@ -784,6 +1012,7 @@ function resumoDoEnvelope(conteudo, envelopeSha256) {
784
1012
  ...conteudo.orcamento.porRuntime.map(s => `Subteto do runtime ${s.runtime}: ${teto(s.teto)}`),
785
1013
  `Relatório da manhã: ${conteudo.relatorio.horario} (${conteudo.janela.fuso}) por ${conteudo.relatorio.canal}`,
786
1014
  ...(envelopeComTrabalhos(conteudo) ? linhasDaV2(conteudo) : []),
1015
+ ...(envelopeMultimaquina(conteudo) ? linhasDaV3(conteudo) : []),
787
1016
  `Nunca entram: ${conteudo.exclusoes.join(', ')}`,
788
1017
  `Hash do envelope: ${envelopeSha256}`,
789
1018
  ];
@@ -796,7 +1025,7 @@ function linhasDaV2(c) {
796
1025
  return [
797
1026
  ...c.trabalhos.flatMap(t => {
798
1027
  const cabeca = `Trabalho ${t.id} (${t.item}), prioridade ${t.prioridade}` +
799
- `${t.dependeDe.length ? `, depois de ${t.dependeDe.join(', ')}` : ''}: `;
1028
+ `${t.dependeDe.length ? `, depois de ${t.dependeDe.join(', ')}` : ''}${t.maquina ? `, na máquina ${t.maquina}` : ''}: `;
800
1029
  if (t.thread)
801
1030
  return [`${cabeca}thread ${t.thread}`];
802
1031
  const linhas = t.pedido.split('\n');
@@ -808,3 +1037,10 @@ function linhasDaV2(c) {
808
1037
  ...c.capacidade.porRuntime.map(s => `Sessões ao mesmo tempo no runtime ${s.runtime}: ${s.sessoes}`),
809
1038
  ];
810
1039
  }
1040
+ /** O que a `v3` acrescenta: cada máquina com a impressão da chave, para o dono conferir, e o limite global de cada conta. */
1041
+ function linhasDaV3(c) {
1042
+ return [
1043
+ ...c.maquinas.map(m => `Máquina ${m.nome}: chave ${impressaoDaChave(m.chave)}`),
1044
+ ...c.capacidadeGlobal.porConta.map(s => `Sessões ao mesmo tempo na conta ${s.conta}, somando as máquinas: ${s.sessoes}`),
1045
+ ];
1046
+ }
@@ -33,7 +33,8 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.ESPERA_DA_VEZ_DE_DESPACHAR_MS = void 0;
36
+ exports.MOTIVO_DA_BASELINE_FALHA = exports.ESPERA_DA_VEZ_DE_DESPACHAR_MS = void 0;
37
+ exports.baselineDoArcoPendente = baselineDoArcoPendente;
37
38
  exports.passoDoArco = passoDoArco;
38
39
  /**
39
40
  * RM-058, fatia N2 (D11 da ork-rm058n2): o passo do arco.
@@ -45,9 +46,12 @@ exports.passoDoArco = passoDoArco;
45
46
  * `rodarFase`, que toma o lease de condução e é idempotente para a mesma fase e o mesmo prompt. O desfecho volta ao log do
46
47
  * arco (`concluirDespacho`): a recusa do núcleo devolve a sessão ao orçamento.
47
48
  *
48
- * A baseline que o bloco com GO no codex pede nunca roda entre a reserva e o lançamento (A1 do CHECK da ork-rm058n2): o
49
- * despacho recusa com `baseline.pendente`, a reserva volta, a trava de despacho é solta e só então a baseline é gravada;
50
- * o passo seguinte confere o envelope de novo e despacha. A falha de um candidato antes de lançar (abrir a thread, ler o
49
+ * A baseline que o bloco com GO pede nunca roda entre a reserva e o lançamento (A1 do CHECK da ork-rm058n2): o despacho
50
+ * recusa com `baseline.pendente`, a reserva volta, a trava de despacho é solta e só então a baseline é gravada; o passo
51
+ * seguinte confere o envelope de novo e despacha. Desde a integração (D6 da ork-rm058integra), vale em qualquer runtime,
52
+ * não só no codex: a entrega da N5 cobra a baseline de toda thread do arco. RM-037 (CHECK 3 da ork-rm037defeit2): o preparo
53
+ * de `worktree.dependencias` (até 10 min) também nunca roda ali: a thread nova nasce com ele pendente (`adiarDependencias`),
54
+ * e a baseline o roda antes dos comandos dela, fora da trava e na fila pesada. A falha de um candidato antes de lançar (abrir a thread, ler o
51
55
  * pedido) devolve a reserva e o passo segue para o próximo (A4). Do núcleo, só a recusa de antes do lançamento devolve a
52
56
  * reserva: a recusa dos portões, e a da conta, do limite de uso ou do impedimento do dono quando nenhuma sessão foi dita.
53
57
  * Quando o runtime pode ter aberto a sessão (a falha seca dele, o id dito antes da falha, o núcleo que voltou sem sessão
@@ -56,6 +60,10 @@ exports.passoDoArco = passoDoArco;
56
60
  *
57
61
  * A revogação nunca espera um despacho: ela usa o lock do arco, e a trava de despacho é outra. Quem chama decide quando
58
62
  * chamar de novo (o vigia é da N4); a decisão da fila diz até quando esperar. Nada aqui liga o modo.
63
+ *
64
+ * RM-058, fatia N4 (D8, D14 e D21 da ork-rm058n4): o passo aceita as decisões da supervisão, que vão à fila (isolados e
65
+ * retomadas), e os alvos de runtime da fase, em ordem: o primeiro com conta do envelope disponível e com folga leva o
66
+ * despacho, com o modelo e o esforço dele. Sem essas entradas, o passo é o da N2.
59
67
  */
60
68
  const fs = __importStar(require("node:fs"));
61
69
  const path = __importStar(require("node:path"));
@@ -67,7 +75,9 @@ const fila_pesada_1 = require("./fila-pesada");
67
75
  const noctum_arco_1 = require("./noctum-arco");
68
76
  const noctum_capacidade_1 = require("./noctum-capacidade");
69
77
  const noctum_envelope_1 = require("./noctum-envelope");
78
+ const noctum_medicao_1 = require("./noctum-medicao");
70
79
  const noctum_fila_1 = require("./noctum-fila");
80
+ const noctum_guarda_1 = require("./noctum-guarda");
71
81
  const phase_1 = require("./phase");
72
82
  const runtime_profiles_1 = require("./runtime-profiles");
73
83
  const setup_1 = require("./setup");
@@ -123,6 +133,24 @@ function eventosDaThread(raiz, threadId) {
123
133
  return null;
124
134
  }
125
135
  }
136
+ /**
137
+ * RM-058, integração (D6 da ork-rm058integra): a baseline do arco, em qualquer runtime. Fora do arco, o despacho só a
138
+ * exige no codex (`baselineDoDespachoNecessaria`, em `phase.ts`, que não muda); no arco, a entrega da N5 a cobra sempre
139
+ * (`noctum.entrega.baseline-ausente`), então o bloco que tem GO a partir da fase do despacho não sai sem ela. O ledger que
140
+ * não se lê não prova a baseline: o despacho espera.
141
+ */
142
+ function baselineDoArcoPendente(raiz, thread, fase) {
143
+ const bloco = (0, thread_1.blocoDaThread)(thread, fase);
144
+ if (!bloco.fases.slice(bloco.fases.indexOf(fase)).includes('GO'))
145
+ return false;
146
+ const eventos = eventosDaThread(raiz, thread.id);
147
+ return eventos === null || !eventos.some(e => e.tipo === ledger_1.TIPOS_DE_EVENTO.baselineGravada);
148
+ }
149
+ /**
150
+ * RM-058, integração (N1 do CHECK 2): a baseline do arco que falhou sem ser espera (o comando que não existe na worktree nova,
151
+ * a worktree quebrada). O passo devolve a recusa tipada deste trabalho, e a supervisão o isola com a causa.
152
+ */
153
+ exports.MOTIVO_DA_BASELINE_FALHA = 'noctum.baseline.falhou';
126
154
  /**
127
155
  * Um passo do arco: no máximo um despacho, pelo núcleo, com o envelope conferido imediatamente antes. Lança só o erro
128
156
  * que não é recusa (disco, git); toda recusa tipada volta como resultado.
@@ -144,7 +172,8 @@ function passoDoArco(carregado, arco, opcoes = {}) {
144
172
  try {
145
173
  let fatos;
146
174
  try {
147
- fatos = (0, noctum_fila_1.fatosDaFila)(carregado, arco, { quando, maquina: opcoes.maquina, lerReservas: opcoes.lerReservas });
175
+ fatos = (0, noctum_fila_1.fatosDaFila)(carregado, arco, { quando, maquina: opcoes.maquina, lerReservas: opcoes.lerReservas, coordenacao: opcoes.coordenacao,
176
+ supervisao: opcoes.supervisao });
148
177
  }
149
178
  catch (e) {
150
179
  // S6 do CHECK: a recusa tipada do arco (o log adulterado, por exemplo) volta como resultado, e não como exceção.
@@ -152,6 +181,11 @@ function passoDoArco(carregado, arco, opcoes = {}) {
152
181
  throw e;
153
182
  return { acao: 'aguardar', ate: cadencia, motivo: e.motivo, detalhe: e.message, fila: null };
154
183
  }
184
+ // N3: o arco entre máquinas só despacha pelo passo da máquina, que confere ordem, reserva e vaga global antes.
185
+ if (fatos.estado?.conteudo && (0, noctum_envelope_1.envelopeMultimaquina)(fatos.estado.conteudo) && typeof opcoes.coordenacao?.reservar !== 'function') {
186
+ return { acao: 'aguardar', ate: cadencia, motivo: 'noctum.coordenacao.exige-passo-da-maquina', fila: null,
187
+ detalhe: 'o envelope vigente é de várias máquinas: o despacho passa pelo passo da máquina, com a ordem, a reserva e a vaga global' };
188
+ }
155
189
  const fila = (0, noctum_fila_1.montarFila)(fatos);
156
190
  if (fila.decisao.acao === 'encerrar')
157
191
  return { acao: 'encerrar', motivo: fila.decisao.motivo, fila };
@@ -164,6 +198,9 @@ function passoDoArco(carregado, arco, opcoes = {}) {
164
198
  }
165
199
  const capacidade = (0, noctum_capacidade_1.fatosDeCapacidade)(carregado, { quando, consultar: opcoes.consultarSessoes });
166
200
  const perfis = (opcoes.perfis ?? (() => perfisDisponiveis(raiz, Date.parse(quando))))();
201
+ // N6 (D5): fora da trava do arco e sob a de despacho; a reserva confere o medido de cada teto sob o lock do arco.
202
+ const medicao = (0, noctum_medicao_1.precisaMedir)(conteudo) && fatos.estado
203
+ ? (opcoes.medir ?? ((r, e, q) => (0, noctum_medicao_1.medirArco)(r, e, { quando: q })))(raiz, fatos.estado, quando) : null;
167
204
  let recusa = null;
168
205
  let doCandidato = null;
169
206
  for (const candidato of fila.candidatos) {
@@ -179,24 +216,43 @@ function passoDoArco(carregado, arco, opcoes = {}) {
179
216
  detalhe: mensagem(e), reserva: null, fila };
180
217
  continue;
181
218
  }
182
- const doRuntime = perfis.filter(p => p.runtime === runtime).map(p => p.id);
183
- // A conta com mais folga; sem folga em nenhuma, a primeira do envelope no runtime, para `admitir` dizer o motivo na
184
- // ordem dele (a máquina cheia antes da conta).
185
- const conta = (0, noctum_capacidade_1.escolherConta)(conteudo.capacidade, capacidade, doRuntime)
186
- ?? conteudo.capacidade.porConta.find(c => doRuntime.includes(c.conta))?.conta ?? null;
219
+ // RM-058 N4 (D14): os alvos da fase, em ordem; sem eles, só o runtime do bloco, como na N2.
220
+ const alvos = opcoes.alvos ? opcoes.alvos(fase, runtime, thread?.modo ?? 'auto') : [{ runtime }];
221
+ const doRuntime = (r) => perfis.filter(p => p.runtime === r).map(p => p.id);
222
+ // A conta com mais folga no primeiro alvo que tem uma; sem folga em nenhum, a primeira do envelope no primeiro alvo com
223
+ // perfil, para `admitir` dizer o motivo na ordem dele (a máquina cheia antes da conta).
224
+ let alvo = alvos[0] ?? { runtime }, conta = null;
225
+ for (const a of alvos) {
226
+ const c = (0, noctum_capacidade_1.escolherConta)(conteudo.capacidade, capacidade, doRuntime(a.runtime));
227
+ if (c) {
228
+ alvo = a;
229
+ conta = c;
230
+ break;
231
+ }
232
+ }
233
+ for (const a of conta === null ? alvos : []) {
234
+ const c = conteudo.capacidade.porConta.find(x => doRuntime(a.runtime).includes(x.conta))?.conta;
235
+ if (c) {
236
+ alvo = a;
237
+ conta = c;
238
+ break;
239
+ }
240
+ }
187
241
  const vaga = (0, board_1.vagaDoDespacho)(carregado, thread?.id ?? '', quando);
188
242
  const admissao = conta === null
189
243
  ? { ok: false, motivo: 'capacidade.conta-nao-autorizada',
190
- detalhe: `nenhuma conta do envelope é perfil disponível do runtime ${runtime}` }
191
- : (0, noctum_capacidade_1.admitir)({ runtime, conta, revisao: fase === 'CHECK' }, conteudo.capacidade, capacidade, vaga);
244
+ detalhe: `nenhuma conta do envelope é perfil disponível do runtime ${alvos.map(a => a.runtime).join(' ou ') || runtime}` }
245
+ : (0, noctum_capacidade_1.admitir)({ runtime: alvo.runtime, conta, revisao: fase === 'CHECK' }, conteudo.capacidade, capacidade, vaga);
192
246
  if (!admissao.ok) {
193
247
  recusa = admissao;
194
248
  if (MOTIVOS_DE_MAQUINA.includes(admissao.motivo))
195
249
  break;
196
250
  continue;
197
251
  }
198
- const r = despacharCandidato(carregado, arco, fila, { trabalho: candidato.id, thread, item: candidato.item, fase, runtime,
199
- conta: conta, quando, pedido: conteudo.trabalhos.find(t => t.id === candidato.id)?.pedido ?? null }, opcoes);
252
+ const r = despacharCandidato(carregado, arco, fila, { trabalho: candidato.id, thread, item: candidato.item, fase, runtime: alvo.runtime,
253
+ conta: conta, quando, pedido: conteudo.trabalhos.find(t => t.id === candidato.id)?.pedido ?? null,
254
+ trio: alvo.runtime === runtime ? null : alvo,
255
+ medido: medicao ? (0, noctum_medicao_1.medidoParaReserva)(medicao, conteudo, conta, alvo.runtime) : undefined }, opcoes);
200
256
  if (r.acao === 'baseline') {
201
257
  pendente = r;
202
258
  break;
@@ -213,7 +269,8 @@ function passoDoArco(carregado, arco, opcoes = {}) {
213
269
  if (doCandidato && !recusa)
214
270
  return doCandidato;
215
271
  return { acao: 'aguardar', ate: new Date(Math.min(Date.parse(quando) + 5 * 60_000, Date.parse(conteudo.janela.fim))).toISOString(),
216
- motivo: recusa?.motivo ?? 'capacidade', detalhe: recusa?.detalhe ?? 'nenhum candidato coube', fila };
272
+ motivo: recusa?.motivo ?? 'capacidade', detalhe: recusa?.detalhe ?? 'nenhum candidato coube', fila,
273
+ ocupantes: capacidade.ocupantes.map(o => ({ origem: o.origem, runtime: o.runtime, revisao: o.revisao })) };
217
274
  }
218
275
  }
219
276
  finally {
@@ -231,8 +288,12 @@ function baselineForaDaTrava(carregado, arco, p, cadencia, opcoes) {
231
288
  (opcoes.gravarBaseline ?? ((k, t, c) => { (0, verify_1.gravarBaseline)(k, t, undefined, c, 'phase.run'); }))(carregado, p.thread, conducao);
232
289
  }
233
290
  catch (e) {
234
- if (!(e instanceof fila_pesada_1.ErroDaFilaPesada) && !(e instanceof conducao_1.ErroDeConducao))
235
- throw e;
291
+ // N1 do CHECK 2: a falha que não é espera não derruba o passo (nem o condutor, nem o CLI): volta como a recusa deste
292
+ // trabalho, que não passa sozinha, e a supervisão o isola; os outros trabalhos seguem.
293
+ if (!(e instanceof fila_pesada_1.ErroDaFilaPesada) && !(e instanceof conducao_1.ErroDeConducao)) {
294
+ return { acao: 'recusado', trabalho: p.trabalho, thread: p.thread, fase: p.fase, motivo: exports.MOTIVO_DA_BASELINE_FALHA, detalhe: mensagem(e),
295
+ reserva: null, fila: p.fila };
296
+ }
236
297
  return { acao: 'aguardar', ate: cadencia, motivo: 'baseline-pendente', detalhe: mensagem(e), fila: p.fila };
237
298
  }
238
299
  return { acao: 'aguardar', ate: new Date().toISOString(), motivo: 'baseline-gravada', fila: p.fila,
@@ -243,8 +304,9 @@ function despacharCandidato(carregado, arco, fila, c, opcoes) {
243
304
  const { raiz } = carregado;
244
305
  let reserva;
245
306
  try {
246
- reserva = (opcoes.reservar ?? noctum_arco_1.reservarDespacho)(raiz, { arco, envelopeSha256: fila.envelopeSha256 ?? '', trabalho: c.trabalho,
247
- thread: c.thread?.id ?? null, fase: c.fase, runtime: c.runtime, conta: c.conta, revisao: c.fase === 'CHECK' }, c.quando).seq;
307
+ reserva = (opcoes.coordenacao?.reservar ?? opcoes.reservar ?? noctum_arco_1.reservarDespacho)(raiz, { arco, envelopeSha256: fila.envelopeSha256 ?? '', trabalho: c.trabalho,
308
+ thread: c.thread?.id ?? null, fase: c.fase, runtime: c.runtime, conta: c.conta, revisao: c.fase === 'CHECK',
309
+ ...(c.medido ? { medido: c.medido } : {}) }, c.quando).seq;
248
310
  }
249
311
  catch (e) {
250
312
  if (!(e instanceof noctum_envelope_1.RecusaDoArco))
@@ -263,8 +325,13 @@ function despacharCandidato(carregado, arco, fila, c, opcoes) {
263
325
  if (!thread) {
264
326
  if (!c.pedido)
265
327
  return { ...recusar('noctum.despacho.pedido-ausente', `o trabalho ${c.trabalho} não traz pedido para abrir a thread`, null), seguir: true };
266
- thread = (opcoes.abrirThread ?? ((k, o) => (0, thread_1.novaThread)(k, o).thread))(carregado, { nome: `${c.item.toLowerCase()} ${c.trabalho}`,
267
- modo: 'auto', roadmap: c.item, pedido: c.pedido, criarWorktree: true });
328
+ // RM-037 (CHECK 3 da ork-rm037defeit2): sob a trava de despacho, o preparo das dependências só fica pendente.
329
+ const base = `${c.item.toLowerCase()} ${c.trabalho}`;
330
+ const numero = opcoes.coordenacao?.ordens?.get(c.trabalho)?.numero;
331
+ const sufixo = numero === undefined ? '' : ` o${numero}`;
332
+ const nome = sufixo ? `${base.slice(0, Math.max(1, 12 - sufixo.length)).trimEnd()}${sufixo}` : base;
333
+ thread = (opcoes.abrirThread ?? ((k, o) => (0, thread_1.novaThread)(k, o).thread))(carregado, { nome,
334
+ modo: 'auto', roadmap: c.item, pedido: c.pedido, criarWorktree: true, adiarDependencias: true });
268
335
  }
269
336
  pedido = (0, thread_1.pedidoDaThread)(raiz, thread);
270
337
  }
@@ -274,13 +341,22 @@ function despacharCandidato(carregado, arco, fila, c, opcoes) {
274
341
  }
275
342
  if (!pedido)
276
343
  return { ...recusar('noctum.despacho.pedido-ausente', `a thread ${thread.id} não guardou o pedido`, thread.id), seguir: true };
344
+ // RM-058, integração (D6): sem a baseline do bloco com GO, em qualquer runtime, a reserva volta antes do lançamento, e a
345
+ // baseline roda fora da trava de despacho (`baselineForaDaTrava`); o vigia nunca a grava: ela fica para o condutor.
346
+ if (baselineDoArcoPendente(raiz, thread, c.fase)) {
347
+ (0, noctum_arco_1.concluirDespacho)(raiz, arco, reserva, { motivo: 'baseline.pendente' });
348
+ return { acao: 'baseline', trabalho: c.trabalho, thread: thread.id, fase: c.fase, fila };
349
+ }
277
350
  // S5 do CHECK: a falha de dentro do núcleo pode ter aberto a sessão; ela sobe, e a reserva fica pendente e conta.
278
351
  const antes = eventosDaThread(raiz, thread.id)?.length ?? 0;
279
- const r = (opcoes.despachar ?? phase_1.rodarFase)(carregado, thread.id, { fase: c.fase, prompt: pedido.texto, perfil: c.conta,
280
- canal: (0, conducao_1.canalDoProcesso)(), correlacao: `noctum:${arco}`, baselinePeloDespacho: false });
352
+ const trio = c.trio ? { runtime: c.trio.runtime, ...(c.trio.model ? { model: c.trio.model } : {}), ...(c.trio.effort ? { effort: c.trio.effort } : {}) } : {};
353
+ // RM-058, integração (D5 da ork-rm058integra): a sessão sabe que o SHIP e o MASTER da thread são do arco. A linha é fixa,
354
+ // e o mesmo pedido dá o mesmo prompt em toda chamada: o `rodarFase` segue idempotente.
355
+ const r = (opcoes.despachar ?? phase_1.rodarFase)(carregado, thread.id, { fase: c.fase, prompt: (0, noctum_guarda_1.pedidoDoArco)(pedido.texto, arco), perfil: c.conta,
356
+ canal: (0, conducao_1.canalDoProcesso)(), correlacao: `noctum:${arco}`, baselinePeloDespacho: false, ...trio });
281
357
  if (r.motivo === 'baseline.pendente') {
282
358
  (0, noctum_arco_1.concluirDespacho)(raiz, arco, reserva, { motivo: 'baseline.pendente' });
283
- return { acao: 'baseline', thread: thread.id, fila };
359
+ return { acao: 'baseline', trabalho: c.trabalho, thread: thread.id, fase: c.fase, fila };
284
360
  }
285
361
  // A recusa dos portões do núcleo vem antes de chamar o runtime: nada abriu, e a sessão volta ao orçamento.
286
362
  if (r.bloqueado)