@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,655 @@
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.identidadeDoCommit = exports.CHAVE_DO_DESTINO = exports.ID_DA_VAGA = exports.ID_DA_ORDEM = exports.NOME_DE_MAQUINA = exports.TENTATIVAS_DE_GRAVAR = exports.TETO_DE_REGISTROS = exports.TETO_DO_REGISTRO = exports.RECIBOS = exports.TIPOS_DE_REGISTRO = exports.CONTRATO_COORDENACAO = exports.BRANCH_DE_COORDENACAO = void 0;
37
+ exports.identidadeDaMaquina = identidadeDaMaquina;
38
+ exports.assinarDados = assinarDados;
39
+ exports.assinaturaConfere = assinaturaConfere;
40
+ exports.caminhoDoRegistro = caminhoDoRegistro;
41
+ exports.lerRamo = lerRamo;
42
+ exports.euNaVersaoVigente = euNaVersaoVigente;
43
+ exports.gravarNoRamo = gravarNoRamo;
44
+ exports.cabecasNoRemoto = cabecasNoRemoto;
45
+ /**
46
+ * RM-058, fatia N3 (D1, D5, D6 e D7 da ork-rm058n3): o transporte entre as máquinas de um arco #Noctum.
47
+ *
48
+ * O meio é um ramo de estado no remoto do projeto, `ork/noctum-coordenacao`, gravado como as reservas da RM-047: commit em
49
+ * cima da ponta lida e push sem força (`gravarNaBranch`). Duas máquinas que gravam juntas nunca perdem uma à outra: quem
50
+ * perdeu a corrida relê e roda a regra de novo sobre o estado novo. Cada registro é assinado pela máquina que o escreveu,
51
+ * com a chave Ed25519 derivada da chave local do núcleo (D5), e conferido com a chave que o dono autorizou no envelope
52
+ * `v3` desta máquina. O retrato da rede (RM-053) e o da fábrica (RM-047) são descoberta: nada deles entra aqui.
53
+ *
54
+ * A leitura reprova inteira (D6): assinatura, caminho ou máquina que não conferem, arquivo apagado ou alterado (só a
55
+ * batida é regravada, e nunca para trás no tempo) e história que não desce da última ponta vista por esta máquina recusam
56
+ * a decisão com `noctum.coordenacao.adulterado`, sem usar a parte que conferiu. O registro de uma versão do envelope que
57
+ * esta máquina ainda não autorizou é espera, não adulteração (`noctum.coordenacao.versao-nao-autorizada`). A última ponta
58
+ * vista só avança depois que a leitura inteira conferiu, inclusive a coerência de quem chama. Sem leitura nova do remoto
59
+ * nada é decidido (D7, `noctum.coordenacao.sem-remoto`): a cópia local do ramo nunca vira fato.
60
+ *
61
+ * Cada registro sai assinado sob a versão vigente aqui; o fim do que é desta máquina (recibo de fim, vaga solta, fim da
62
+ * vez, entrega, revogação) sai também sob a última versão em que ela estava, depois de uma emenda que a tirou do arco. O
63
+ * commit leva a identidade da máquina, nunca a do git de quem a usa.
64
+ *
65
+ * Limites ditos na página: a chave da máquina é legível pelo mesmo usuário do sistema, como a chave local da N1; quem tem
66
+ * push no remoto pode apagar ou reescrever o ramo, e só a máquina que já viu o registro percebe.
67
+ */
68
+ const node_child_process_1 = require("node:child_process");
69
+ const fs = __importStar(require("node:fs"));
70
+ const path = __importStar(require("node:path"));
71
+ const node_crypto_1 = require("node:crypto");
72
+ const branch_de_estado_1 = require("./branch-de-estado");
73
+ const estado_thread_1 = require("./estado-thread");
74
+ const fila_pesada_1 = require("./fila-pesada");
75
+ const hitl_local_receipt_1 = require("./hitl-local-receipt");
76
+ const maquina_1 = require("./maquina");
77
+ const noctum_arco_1 = require("./noctum-arco");
78
+ const noctum_envelope_1 = require("./noctum-envelope");
79
+ const roadmap_reservas_1 = require("./roadmap-reservas");
80
+ exports.BRANCH_DE_COORDENACAO = 'ork/noctum-coordenacao';
81
+ exports.CONTRATO_COORDENACAO = 'ork.noctum-coordenacao/v1';
82
+ const CONTRATO_DA_VISTA = 'ork.noctum-coordenacao-vista/v1';
83
+ const PREFIXO = 'noctum';
84
+ const DOMINIO_DA_CHAVE = 'ork.noctum-maquina/v1';
85
+ /** O cabeçalho PKCS#8 de uma chave privada Ed25519; a semente de 32 bytes vem logo depois (o padrão do recibo público). */
86
+ const PKCS8_ED25519 = Buffer.from('302e020100300506032b657004220420', 'hex');
87
+ exports.TIPOS_DE_REGISTRO = ['autoridade', 'cessao', 'ordem', 'cancelamento', 'recibo', 'vaga', 'vaga-solta', 'vez', 'vez-fim',
88
+ 'entrega', 'batida', 'revogacao'];
89
+ exports.RECIBOS = ['aceita', 'recusada', 'concluida', 'encerrada'];
90
+ /** Tetos da leitura: um registro é um JSON curto, e um arco de uma noite não chega perto do número. */
91
+ exports.TETO_DO_REGISTRO = 8 * 1024;
92
+ exports.TETO_DE_REGISTROS = 10000;
93
+ /** Pushes recusados seguidos antes de desistir: o mesmo número das reservas da RM-047. */
94
+ exports.TENTATIVAS_DE_GRAVAR = 5;
95
+ exports.NOME_DE_MAQUINA = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
96
+ exports.ID_DA_ORDEM = /^O-([a-z0-9][a-z0-9-]{0,31})-([1-9][0-9]{0,3})$/;
97
+ exports.ID_DA_VAGA = /^V-([A-Za-z0-9][A-Za-z0-9._-]{0,63})-([1-9][0-9]{0,8})$/;
98
+ exports.CHAVE_DO_DESTINO = /^[0-9a-f]{16}$/;
99
+ const adulterado = (detalhe) => new noctum_envelope_1.RecusaDoArco('noctum.coordenacao.adulterado', `${detalhe}; o ramo de coordenação exige inspeção`);
100
+ const semRemoto = (detalhe) => new noctum_envelope_1.RecusaDoArco('noctum.coordenacao.sem-remoto', detalhe);
101
+ const foraDoEnvelope = (detalhe) => new noctum_envelope_1.RecusaDoArco('noctum.coordenacao.fora-do-envelope', detalhe);
102
+ const objeto = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
103
+ const quatro = (n) => {
104
+ if (typeof n !== 'number' || !Number.isSafeInteger(n) || n < 1 || n > 9999)
105
+ throw adulterado(`número de registro inválido (${String(n)})`);
106
+ return String(n).padStart(4, '0');
107
+ };
108
+ const primeiraLinha = (e) => String(e?.message ?? e).split('\n')[0].slice(0, 300);
109
+ // ---------------------------------------------------------------------------
110
+ // Identidade da máquina (D5).
111
+ // ---------------------------------------------------------------------------
112
+ function chavePrivada(raiz) {
113
+ const semente = (0, node_crypto_1.createHmac)('sha256', (0, hitl_local_receipt_1.chaveLocal)(raiz, true)).update(DOMINIO_DA_CHAVE).digest();
114
+ return (0, node_crypto_1.createPrivateKey)({ key: Buffer.concat([PKCS8_ED25519, semente]), format: 'der', type: 'pkcs8' });
115
+ }
116
+ /**
117
+ * O nome e a chave pública Ed25519 (SPKI DER em base64) desta raiz de estado: é o que o dono põe em `maquinas` no envelope
118
+ * `v3`. A chave privada nunca sai daqui; ela deriva da chave local do núcleo, que a raiz de estado guarda 0600.
119
+ */
120
+ function identidadeDaMaquina(raiz, nome) {
121
+ return { nome: (0, maquina_1.nomeDaMaquina)(nome), chave: (0, node_crypto_1.createPublicKey)(chavePrivada(raiz)).export({ format: 'der', type: 'spki' }).toString('base64') };
122
+ }
123
+ function assinarDados(raiz, dados) {
124
+ return (0, node_crypto_1.sign)(null, Buffer.from((0, noctum_envelope_1.canonico)(dados), 'utf8'), chavePrivada(raiz)).toString('base64');
125
+ }
126
+ function assinaturaConfere(chave, dados, assinatura) {
127
+ if (typeof assinatura !== 'string' || !/^[A-Za-z0-9+/]{86}==$/.test(assinatura))
128
+ return false;
129
+ try {
130
+ const publica = (0, node_crypto_1.createPublicKey)({ key: Buffer.from(chave, 'base64'), format: 'der', type: 'spki' });
131
+ return publica.asymmetricKeyType === 'ed25519' && (0, node_crypto_1.verify)(null, Buffer.from((0, noctum_envelope_1.canonico)(dados), 'utf8'), publica, Buffer.from(assinatura, 'base64'));
132
+ }
133
+ catch {
134
+ return false;
135
+ }
136
+ }
137
+ // ---------------------------------------------------------------------------
138
+ // O caminho de cada registro sai dos dados dele.
139
+ // ---------------------------------------------------------------------------
140
+ /** O caminho do registro no ramo, recalculado dos dados; identificador fora do formato reprova. */
141
+ function caminhoDoRegistro(d) {
142
+ if (typeof d.arco !== 'string' || !noctum_envelope_1.ID_DO_ARCO.test(d.arco))
143
+ throw adulterado('registro com arco inválido');
144
+ const base = `arcos/${d.arco}`;
145
+ const ordem = () => { if (typeof d.ordem !== 'string' || !exports.ID_DA_ORDEM.test(d.ordem))
146
+ throw adulterado('registro com ordem inválida'); return d.ordem; };
147
+ const vaga = () => { if (typeof d.vaga !== 'string' || !exports.ID_DA_VAGA.test(d.vaga))
148
+ throw adulterado('registro com vaga inválida'); return d.vaga; };
149
+ const chave = () => { if (typeof d.chave !== 'string' || !exports.CHAVE_DO_DESTINO.test(d.chave))
150
+ throw adulterado('registro com destino inválido'); return d.chave; };
151
+ switch (d.tipo) {
152
+ case 'autoridade': return `${base}/autoridade/${quatro(d.geracao)}.json`;
153
+ case 'cessao': return `${base}/cessoes/${quatro(d.geracao)}.json`;
154
+ case 'ordem': return `${base}/ordens/${ordem()}.json`;
155
+ case 'cancelamento': return `${base}/cancelamentos/${ordem()}.json`;
156
+ case 'recibo':
157
+ if (!exports.RECIBOS.includes(d.recibo))
158
+ throw adulterado('recibo de tipo desconhecido');
159
+ return `${base}/recibos/${ordem()}/${String(d.recibo)}.json`;
160
+ case 'vaga': return `${base}/vagas/${vaga()}.json`;
161
+ case 'vaga-solta': return `${base}/vagas/${vaga()}.solta.json`;
162
+ case 'vez': return `${base}/integracao/${chave()}/${quatro(d.n)}.json`;
163
+ case 'vez-fim': return `${base}/integracao/${chave()}/${quatro(d.n)}.fim.json`;
164
+ case 'entrega':
165
+ if (typeof d.trabalho !== 'string' || !noctum_envelope_1.ID_DO_TRABALHO.test(d.trabalho))
166
+ throw adulterado('entrega com trabalho inválido');
167
+ return `${base}/entregas/${d.trabalho}.json`;
168
+ case 'batida':
169
+ if (typeof d.por !== 'string' || !exports.NOME_DE_MAQUINA.test(d.por))
170
+ throw adulterado('batida com máquina inválida');
171
+ return `${base}/batidas/${d.por}.json`;
172
+ case 'revogacao':
173
+ if (typeof d.por !== 'string' || !exports.NOME_DE_MAQUINA.test(d.por))
174
+ throw adulterado('revogação com máquina inválida');
175
+ return `${base}/revogacoes/${d.por}.json`;
176
+ default: throw adulterado(`registro de tipo desconhecido (${String(d.tipo)})`);
177
+ }
178
+ }
179
+ /** A batida é o único registro que a máquina regrava; todo o resto é só de acréscimo. */
180
+ const regravavel = (arco, caminho) => new RegExp(`^arcos/${arco}/batidas/[^/]+[.]json$`).test(caminho);
181
+ // ---------------------------------------------------------------------------
182
+ // Git sem limite de saída pequeno: a árvore do arco e os blobs vêm de uma vez.
183
+ // ---------------------------------------------------------------------------
184
+ function gitBruto(raiz, args, entrada) {
185
+ const r = (0, node_child_process_1.spawnSync)('git', args, { cwd: raiz, input: entrada, maxBuffer: 256 * 1024 * 1024, timeout: 60000 });
186
+ return { ok: r.status === 0 && !r.error, status: r.status, saida: r.stdout ?? Buffer.alloc(0), erro: (r.stderr ?? Buffer.alloc(0)).toString('utf8') };
187
+ }
188
+ /**
189
+ * Os arquivos do arco na ponta: modo, objeto, tamanho e caminho. Só blob comum; link, executável ou submódulo reprovam, e o
190
+ * tamanho confere antes de ler qualquer conteúdo (S5 da rodada 2: um blob enorme nunca entra na memória).
191
+ */
192
+ function arquivosDaPonta(raiz, ponta, arco) {
193
+ const r = gitBruto(raiz, ['ls-tree', '-r', '-l', '-z', '--full-tree', ponta, '--', `arcos/${arco}/`]);
194
+ if (!r.ok)
195
+ throw semRemoto(`git ls-tree falhou: ${r.erro.trim().split('\n').pop() ?? ''}`);
196
+ const entradas = r.saida.toString('utf8').split('\0').filter(Boolean);
197
+ if (entradas.length > exports.TETO_DE_REGISTROS)
198
+ throw adulterado(`o arco tem ${entradas.length} registros, acima de ${exports.TETO_DE_REGISTROS}`);
199
+ return entradas.map((e) => {
200
+ const m = /^(\d{6}) (\w+) ([0-9a-f]{40,64}) +(\d+|-)\t(.+)$/s.exec(e);
201
+ if (!m || m[1] !== '100644' || m[2] !== 'blob')
202
+ throw adulterado(`entrada que não é arquivo comum no ramo (${e.slice(0, 80)})`);
203
+ if (Number(m[4]) > exports.TETO_DO_REGISTRO)
204
+ throw adulterado(`${m[5]} tem ${m[4]} bytes, acima de ${exports.TETO_DO_REGISTRO}`);
205
+ return { oid: m[3], caminho: m[5] };
206
+ });
207
+ }
208
+ /** Os conteúdos dos blobs, numa chamada só de `git cat-file --batch`. */
209
+ function lerBlobs(raiz, oids) {
210
+ const conteudos = new Map();
211
+ if (!oids.length)
212
+ return conteudos;
213
+ const r = gitBruto(raiz, ['cat-file', '--batch'], oids.join('\n') + '\n');
214
+ if (!r.ok)
215
+ throw semRemoto(`git cat-file falhou: ${r.erro.trim().split('\n').pop() ?? ''}`);
216
+ let i = 0;
217
+ for (const oid of oids) {
218
+ const fim = r.saida.indexOf(0x0a, i);
219
+ const cabeca = r.saida.subarray(i, fim).toString('utf8');
220
+ const m = /^([0-9a-f]{40,64}) blob (\d+)$/.exec(cabeca);
221
+ if (!m || m[1] !== oid)
222
+ throw adulterado(`blob ilegível no ramo (${cabeca.slice(0, 80)})`);
223
+ const tamanho = Number(m[2]);
224
+ if (tamanho > exports.TETO_DO_REGISTRO)
225
+ throw adulterado(`registro de ${tamanho} bytes, acima de ${exports.TETO_DO_REGISTRO}`);
226
+ conteudos.set(oid, r.saida.subarray(fim + 1, fim + 1 + tamanho));
227
+ i = fim + 1 + tamanho + 1;
228
+ }
229
+ return conteudos;
230
+ }
231
+ // ---------------------------------------------------------------------------
232
+ // A última ponta vista por esta máquina (D6): a história só anda para a frente.
233
+ // ---------------------------------------------------------------------------
234
+ function dirDoArcoLocal(raiz, arco) {
235
+ if (typeof arco !== 'string' || !noctum_envelope_1.ID_DO_ARCO.test(arco))
236
+ throw new noctum_envelope_1.RecusaDoArco('noctum.envelope.invalido', 'identificador de arco inválido');
237
+ const principal = (0, estado_thread_1.raizDoEstado)(raiz);
238
+ (0, estado_thread_1.exigirEstadoSemLink)(principal, ['.orkastery', 'noctum', 'arcos', arco]);
239
+ const dir = path.join(principal, '.orkastery', 'noctum', 'arcos', arco);
240
+ fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
241
+ return dir;
242
+ }
243
+ function lerVista(dir) {
244
+ const arquivo = path.join(dir, 'coordenacao.json');
245
+ let bruto;
246
+ try {
247
+ const fd = fs.openSync(arquivo, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW);
248
+ try {
249
+ const st = fs.fstatSync(fd);
250
+ if (!st.isFile() || st.uid !== process.getuid?.() || (st.mode & 0o077) !== 0)
251
+ throw adulterado('a última ponta vista não é arquivo privado do núcleo');
252
+ bruto = fs.readFileSync(fd, 'utf8');
253
+ }
254
+ finally {
255
+ fs.closeSync(fd);
256
+ }
257
+ }
258
+ catch (e) {
259
+ if (e.code === 'ENOENT')
260
+ return {};
261
+ if (e instanceof noctum_envelope_1.RecusaDoArco)
262
+ throw e;
263
+ throw adulterado('a última ponta vista não se lê');
264
+ }
265
+ let v;
266
+ try {
267
+ v = JSON.parse(bruto);
268
+ }
269
+ catch {
270
+ throw adulterado('a última ponta vista não é JSON');
271
+ }
272
+ if (!objeto(v) || v.contrato !== CONTRATO_DA_VISTA || !objeto(v.remotos))
273
+ throw adulterado('a última ponta vista fora do formato');
274
+ const vista = {};
275
+ for (const [remoto, x] of Object.entries(v.remotos)) {
276
+ if (!objeto(x) || typeof x.ponta !== 'string' || !/^[0-9a-f]{40,64}$/.test(x.ponta) || typeof x.vistoEm !== 'string' ||
277
+ (x.adulterado !== undefined && (!objeto(x.adulterado) || typeof x.adulterado.em !== 'string' || typeof x.adulterado.detalhe !== 'string'))) {
278
+ throw adulterado('a última ponta vista fora do formato');
279
+ }
280
+ vista[remoto] = { ponta: x.ponta, vistoEm: x.vistoEm,
281
+ ...(x.adulterado ? { adulterado: { em: x.adulterado.em, detalhe: x.adulterado.detalhe } } : {}) };
282
+ }
283
+ return vista;
284
+ }
285
+ function gravarVista(dir, vista) {
286
+ const temp = path.join(dir, `.coordenacao.${(0, node_crypto_1.randomUUID)()}.tmp`);
287
+ const fd = fs.openSync(temp, fs.constants.O_WRONLY | fs.constants.O_CREAT | fs.constants.O_EXCL | fs.constants.O_NOFOLLOW, 0o600);
288
+ try {
289
+ fs.writeFileSync(fd, JSON.stringify({ contrato: CONTRATO_DA_VISTA, remotos: vista }) + '\n');
290
+ fs.fsyncSync(fd);
291
+ }
292
+ finally {
293
+ fs.closeSync(fd);
294
+ }
295
+ fs.renameSync(temp, path.join(dir, 'coordenacao.json'));
296
+ }
297
+ /** A trava curta da última ponta vista: dois processos desta máquina nunca a fazem andar para trás. */
298
+ function comTravaDaVista(dir, executar) {
299
+ const prazo = Date.now() + 10000;
300
+ for (;;) {
301
+ const fd = (0, fila_pesada_1.tentarTravaExclusiva)(path.join(dir, 'coordenacao.lock'));
302
+ if (fd !== null) {
303
+ try {
304
+ return executar();
305
+ }
306
+ finally {
307
+ fs.closeSync(fd);
308
+ }
309
+ }
310
+ if (Date.now() >= prazo)
311
+ throw new noctum_envelope_1.RecusaDoArco('noctum.coordenacao.concorrencia', 'outro processo desta máquina segura a última ponta vista');
312
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 50);
313
+ }
314
+ }
315
+ const ancestral = (raiz, antigo, novo) => {
316
+ const r = gitBruto(raiz, ['merge-base', '--is-ancestor', antigo, novo]);
317
+ if (r.ok)
318
+ return 'sim';
319
+ return r.status === 1 ? 'nao' : 'ilegivel';
320
+ };
321
+ /** O `em` de um registro numa ponta, para comparar a batida antiga com a nova. */
322
+ function emDoRegistro(raiz, ponta, caminho) {
323
+ const r = gitBruto(raiz, ['show', `${ponta}:${caminho}`]);
324
+ if (!r.ok)
325
+ return { lido: false, em: NaN };
326
+ try {
327
+ return { lido: true, em: Date.parse(String(JSON.parse(r.saida.toString('utf8')).dados.em)) };
328
+ }
329
+ catch {
330
+ return { lido: true, em: NaN };
331
+ }
332
+ }
333
+ /**
334
+ * Entre duas pontas da história, só acréscimo, salvo a batida, que nunca volta no tempo (D6 e S3 da revisão). Devolve a
335
+ * violação que achou, `null` quando só houve acréscimo, ou `ilegivel` quando o diff não se leu.
336
+ */
337
+ function violacaoDeAcrescimo(raiz, arco, de, para) {
338
+ const diff = gitBruto(raiz, ['diff', '--name-status', '--no-renames', '-z', de, para, '--', `arcos/${arco}/`]);
339
+ if (!diff.ok)
340
+ return 'ilegivel';
341
+ const partes = diff.saida.toString('utf8').split('\0').filter(Boolean);
342
+ for (let i = 0; i + 1 < partes.length; i += 2) {
343
+ const [status, caminho] = [partes[i], partes[i + 1]];
344
+ if (status === 'A')
345
+ continue;
346
+ if (status === 'M' && regravavel(arco, caminho)) {
347
+ const nova = emDoRegistro(raiz, para, caminho), antiga = emDoRegistro(raiz, de, caminho);
348
+ if (!nova.lido || !antiga.lido)
349
+ return 'ilegivel';
350
+ if (!(nova.em >= antiga.em))
351
+ return `a batida ${caminho} voltou no tempo depois de vista`;
352
+ continue;
353
+ }
354
+ return `o registro ${caminho} foi ${status === 'D' ? 'apagado' : 'alterado'} depois de visto`;
355
+ }
356
+ return null;
357
+ }
358
+ /**
359
+ * Confere a ponta lida contra a última vista, com a trava da vista nas mãos de quem chama: `atras` quando outro processo
360
+ * desta máquina já viu uma ponta mais nova e, entre as duas, só houve acréscimo (a leitura ficou velha, não adulterada).
361
+ * Nos dois sentidos, o apagamento, a alteração e a história reescrita reprovam e marcam a vista (A-R2A-1 da rodada 2): o
362
+ * que um processo desta máquina viu, nenhum outro faz esquecer.
363
+ */
364
+ function relacaoComAVista(raiz, dir, remoto, arco, ponta, quando) {
365
+ const vista = lerVista(dir);
366
+ const atual = vista[remoto];
367
+ if (atual?.adulterado) {
368
+ throw adulterado(`esta máquina já viu o ramo adulterado em ${atual.adulterado.em} (${atual.adulterado.detalhe}); a última ponta vista ` +
369
+ 'fica marcada até a inspeção');
370
+ }
371
+ const antiga = atual?.ponta ?? null;
372
+ if (antiga === null)
373
+ return 'ok';
374
+ const reprovar = (detalhe) => {
375
+ gravarVista(dir, { ...vista, [remoto]: { ...atual, adulterado: { em: quando, detalhe: detalhe.slice(0, 300) } } });
376
+ throw adulterado(detalhe);
377
+ };
378
+ const conferir = (de, para) => {
379
+ const v = violacaoDeAcrescimo(raiz, arco, de, para);
380
+ if (v === 'ilegivel')
381
+ throw adulterado('o diff entre a última ponta vista e a nova não se lê');
382
+ if (v)
383
+ reprovar(v);
384
+ };
385
+ if (ponta === null)
386
+ return reprovar(`o ramo ${exports.BRANCH_DE_COORDENACAO} sumiu do remoto ${remoto} depois de visto`);
387
+ if (ponta === antiga)
388
+ return 'ok';
389
+ const adiante = ancestral(raiz, antiga, ponta);
390
+ if (adiante === 'ilegivel')
391
+ throw adulterado('a relação entre a última ponta vista e a nova não se lê');
392
+ if (adiante === 'sim') {
393
+ conferir(antiga, ponta);
394
+ return 'ok';
395
+ }
396
+ const atras = ancestral(raiz, ponta, antiga);
397
+ if (atras === 'ilegivel')
398
+ throw adulterado('a relação entre a nova ponta e a última vista não se lê');
399
+ if (atras === 'sim') {
400
+ conferir(ponta, antiga);
401
+ return 'atras';
402
+ }
403
+ return reprovar(`a história do ramo foi reescrita: a ponta ${ponta.slice(0, 12)} não desce da última vista ${antiga.slice(0, 12)}`);
404
+ }
405
+ /** Grava a ponta como a última vista; só com a trava da vista e depois de `relacaoComAVista` dizer `ok`. */
406
+ function gravarPonta(dir, remoto, ponta, quando) {
407
+ if (!ponta)
408
+ return;
409
+ const vista = lerVista(dir);
410
+ if (vista[remoto]?.ponta === ponta)
411
+ return;
412
+ gravarVista(dir, { ...vista, [remoto]: { ponta, vistoEm: quando } });
413
+ }
414
+ /**
415
+ * Avança a última ponta vista para o commit que esta máquina acabou de gravar, conferido nos dois sentidos contra a vista
416
+ * de agora (outro processo pode ter visto uma ponta mais nova, e o registro gravado aqui não pode ter sumido dela).
417
+ */
418
+ function avancarVista(raiz, dir, remoto, arco, ponta, quando) {
419
+ comTravaDaVista(dir, () => {
420
+ if (relacaoComAVista(raiz, dir, remoto, arco, ponta, quando) === 'ok')
421
+ gravarPonta(dir, remoto, ponta, quando);
422
+ });
423
+ }
424
+ // ---------------------------------------------------------------------------
425
+ // Leitura e gravação.
426
+ // ---------------------------------------------------------------------------
427
+ function remotoDe(carregado, opcoes) {
428
+ try {
429
+ return (0, branch_de_estado_1.exigirRemoto)(opcoes.remoto ?? carregado.manifesto.fabrica.remoto, PREFIXO);
430
+ }
431
+ catch (e) {
432
+ throw semRemoto(primeiraLinha(e));
433
+ }
434
+ }
435
+ const redeDe = (raiz, opcoes) => opcoes.rede ?? (0, roadmap_reservas_1.redeDoFechamento)(raiz);
436
+ /** As versões autorizadas aqui que são de várias máquinas, pelo hash: só elas assinam no ramo. */
437
+ function chavesPorVersao(versoes) {
438
+ const porHash = new Map();
439
+ for (const v of versoes) {
440
+ if ((0, noctum_envelope_1.envelopeMultimaquina)(v.conteudo))
441
+ porHash.set(v.envelopeSha256, new Map(v.conteudo.maquinas.map(m => [m.nome, m.chave])));
442
+ }
443
+ return porHash;
444
+ }
445
+ function lerUmaVez(carregado, arco, opcoes, conferir) {
446
+ const { raiz } = carregado;
447
+ const quando = opcoes.quando ?? new Date().toISOString();
448
+ const remoto = remotoDe(carregado, opcoes);
449
+ const versoes = (0, noctum_arco_1.versoesAutorizadas)(raiz, arco);
450
+ if (!versoes.length)
451
+ throw foraDoEnvelope(`o arco ${arco} não tem envelope autorizado nesta máquina`);
452
+ const dir = dirDoArcoLocal(raiz, arco);
453
+ const chaves = chavesPorVersao(versoes);
454
+ const rede = redeDe(raiz, opcoes);
455
+ let lida;
456
+ try {
457
+ lida = (0, branch_de_estado_1.buscarBranch)(raiz, remoto, exports.BRANCH_DE_COORDENACAO, PREFIXO, rede.timeoutMs, rede.env);
458
+ }
459
+ catch (e) {
460
+ throw semRemoto(primeiraLinha(e));
461
+ }
462
+ if (!lida.atualizado)
463
+ throw semRemoto(`não consegui ler ${exports.BRANCH_DE_COORDENACAO} em ${remoto}; sem leitura nova, nada é decidido`);
464
+ const registros = [];
465
+ if (lida.ponta) {
466
+ const arquivos = arquivosDaPonta(raiz, lida.ponta, arco);
467
+ const blobs = lerBlobs(raiz, arquivos.map(a => a.oid));
468
+ for (const { oid, caminho } of arquivos) {
469
+ let bruto;
470
+ try {
471
+ bruto = JSON.parse(blobs.get(oid).toString('utf8'));
472
+ }
473
+ catch {
474
+ throw adulterado(`${caminho} não é JSON`);
475
+ }
476
+ if (!objeto(bruto) || Object.keys(bruto).sort().join(',') !== 'assinatura,dados' || !objeto(bruto.dados)) {
477
+ throw adulterado(`${caminho} fora do formato`);
478
+ }
479
+ const d = bruto.dados;
480
+ if (d.contrato !== exports.CONTRATO_COORDENACAO || d.arco !== arco || !exports.TIPOS_DE_REGISTRO.includes(d.tipo) ||
481
+ typeof d.em !== 'string' || !Number.isFinite(Date.parse(d.em))) {
482
+ throw adulterado(`${caminho} fora do contrato`);
483
+ }
484
+ const daVersao = typeof d.envelopeSha256 === 'string' ? chaves.get(d.envelopeSha256) : undefined;
485
+ // A3 da revisão: a emenda que a outra máquina já autorizou e esta ainda não é espera, não adulteração.
486
+ if (!daVersao) {
487
+ throw new noctum_envelope_1.RecusaDoArco('noctum.coordenacao.versao-nao-autorizada', `${caminho} é de uma versão do envelope que esta máquina ` +
488
+ 'ainda não autorizou: se o dono autorizou uma emenda em outra máquina, autorize-a aqui, na mesma ordem das outras máquinas; se não houve emenda, o ramo exige ' +
489
+ 'inspeção. Até lá, esta máquina não decide nem grava no ramo');
490
+ }
491
+ const chave = typeof d.por === 'string' ? daVersao.get(d.por) : undefined;
492
+ if (!chave)
493
+ throw adulterado(`${caminho} assinado por uma máquina fora do envelope (${String(d.por).slice(0, 64)})`);
494
+ if (!assinaturaConfere(chave, d, bruto.assinatura))
495
+ throw adulterado(`${caminho} com assinatura que não confere`);
496
+ if (caminhoDoRegistro(d) !== caminho)
497
+ throw adulterado(`${caminho} não é o caminho dos dados dele`);
498
+ registros.push({ caminho, dados: d });
499
+ }
500
+ }
501
+ const leitura = { arco, remoto, ponta: lida.ponta, registros, versoes, lidoEm: quando };
502
+ // A2 da revisão e A-R2A-1 da rodada 2: a história, a coerência de quem chama e o avanço da vista numa seção crítica só.
503
+ // Nenhum outro processo desta máquina avança a vista entre conferir e gravar, e a vista só anda depois da coerência.
504
+ return comTravaDaVista(dir, () => {
505
+ if (relacaoComAVista(raiz, dir, remoto, arco, lida.ponta, quando) === 'atras')
506
+ return 'atras';
507
+ conferir?.(leitura);
508
+ gravarPonta(dir, remoto, lida.ponta, quando);
509
+ return leitura;
510
+ });
511
+ }
512
+ /**
513
+ * A leitura conferida do ramo de coordenação do arco, nova do remoto. Lança a recusa tipada; nunca devolve leitura
514
+ * parcial. Outro processo desta máquina que já viu uma ponta mais nova faz esta ler de novo; a ponta que continua atrás da
515
+ * última vista depois de três leituras é o ramo que voltou atrás (push forçado para um commit antigo), e reprova.
516
+ */
517
+ function lerRamo(carregado, arco, opcoes = {}, conferir) {
518
+ for (let tentativa = 1; tentativa <= 3; tentativa++) {
519
+ const lida = lerUmaVez(carregado, arco, opcoes, conferir);
520
+ if (lida !== 'atras')
521
+ return lida;
522
+ }
523
+ throw adulterado('a história do ramo foi reescrita: a ponta do remoto voltou para trás da última vista por esta máquina');
524
+ }
525
+ /** A versão vigente aqui (a última autorizada) e esta máquina nela, com o mesmo nome e a mesma chave; senão, a recusa. */
526
+ function euNaVersaoVigente(raiz, leitura, opcoes) {
527
+ const vigente = leitura.versoes.at(-1);
528
+ if (!(0, noctum_envelope_1.envelopeMultimaquina)(vigente.conteudo)) {
529
+ throw foraDoEnvelope(`o envelope vigente do arco ${leitura.arco} é ${vigente.conteudo.contrato}, sem máquinas: só a v3 coordena`);
530
+ }
531
+ const eu = identidadeDaMaquina(raiz, opcoes.maquina);
532
+ const listada = vigente.conteudo.maquinas.find(m => m.nome === eu.nome);
533
+ if (!listada)
534
+ throw foraDoEnvelope(`a máquina ${eu.nome} não está no envelope vigente do arco ${leitura.arco}`);
535
+ if (listada.chave !== eu.chave)
536
+ throw foraDoEnvelope(`a chave desta raiz não é a que o envelope dá à máquina ${eu.nome}`);
537
+ return { eu, vigente };
538
+ }
539
+ /** Os registros com que uma máquina fecha o que é dela; só eles saem sob uma versão anterior do envelope (A4 da revisão). */
540
+ const ehFechamento = (c) => c.tipo === 'vaga-solta' || c.tipo === 'vez-fim' || c.tipo === 'entrega' ||
541
+ c.tipo === 'revogacao' || (c.tipo === 'recibo' && (c.recibo === 'concluida' || c.recibo === 'encerrada'));
542
+ /**
543
+ * A versão sob a qual esta máquina assina: a vigente; ou, quando uma emenda a tirou do arco, a última versão autorizada em
544
+ * que ela está com a mesma chave, e só para fechar o que é dela (fim de ordem, vaga, vez e entrega). O leitor confere cada
545
+ * registro com as chaves da versão que ele diz.
546
+ */
547
+ function versaoParaAssinar(raiz, leitura, opcoes, novos) {
548
+ try {
549
+ return euNaVersaoVigente(raiz, leitura, opcoes);
550
+ }
551
+ catch (e) {
552
+ if (!(e instanceof noctum_envelope_1.RecusaDoArco) || e.motivo !== 'noctum.coordenacao.fora-do-envelope' || !novos.every(ehFechamento))
553
+ throw e;
554
+ const eu = identidadeDaMaquina(raiz, opcoes.maquina);
555
+ const antiga = [...leitura.versoes].reverse().find(v => (0, noctum_envelope_1.envelopeMultimaquina)(v.conteudo) &&
556
+ v.conteudo.maquinas.some(m => m.nome === eu.nome && m.chave === eu.chave));
557
+ if (!antiga)
558
+ throw e;
559
+ return { eu, vigente: antiga };
560
+ }
561
+ }
562
+ /** A identidade dos commits do ramo de coordenação: a máquina, num domínio reservado que não entrega e-mail. */
563
+ const identidadeDoCommit = (maquina) => ({ nome: `ork-noctum ${maquina}`, email: 'ork-noctum@noctum.invalid' });
564
+ exports.identidadeDoCommit = identidadeDoCommit;
565
+ /**
566
+ * Grava os registros que a regra pede, assinados por esta máquina sob a versão vigente do envelope, numa versão nova do
567
+ * ramo em cima da ponta lida. Push recusado: relê e roda a regra de novo, até `TENTATIVAS_DE_GRAVAR`. A regra pode lançar a
568
+ * recusa tipada e deve ser idempotente, porque um push de resultado incerto é repetido sobre a leitura seguinte.
569
+ */
570
+ function gravarNoRamo(carregado, arco, opcoes, regra, conferir) {
571
+ const { raiz } = carregado;
572
+ const quando = opcoes.quando ?? new Date().toISOString();
573
+ // S-R3A-1 da rodada 3: o instante vai ao `em` de cada registro, e o leitor o exige como data.
574
+ if (typeof quando !== 'string' || !Number.isFinite(Date.parse(quando))) {
575
+ throw new noctum_envelope_1.RecusaDoArco('noctum.envelope.invalido', 'o instante da gravação não é uma data; nada foi ao ramo');
576
+ }
577
+ for (let tentativa = 1; tentativa <= exports.TENTATIVAS_DE_GRAVAR; tentativa++) {
578
+ const leitura = lerRamo(carregado, arco, { ...opcoes, quando }, conferir);
579
+ const { novos, resultado } = regra(leitura);
580
+ if (!novos.length)
581
+ return { resultado, commit: null, gravados: [], leitura };
582
+ const { eu, vigente } = versaoParaAssinar(raiz, leitura, opcoes, novos);
583
+ const existentes = new Set(leitura.registros.map(r => r.caminho));
584
+ const mudancas = [];
585
+ const gravados = [];
586
+ for (const campos of novos) {
587
+ // B1 da rodada 2: o que se assina é o que o leitor vai ler, sem campo `undefined` nem objeto que o JSON transforma.
588
+ const dados = JSON.parse(JSON.stringify({ ...campos, contrato: exports.CONTRATO_COORDENACAO, arco, envelopeSha256: vigente.envelopeSha256,
589
+ por: eu.nome, em: quando }));
590
+ const caminho = caminhoDoRegistro(dados);
591
+ // S3 da revisão: a batida desta máquina nunca volta no tempo, nem quando o relógio daqui volta.
592
+ const anterior = dados.tipo === 'batida' ? leitura.registros.find(r => r.caminho === caminho)?.dados.em : undefined;
593
+ if (anterior && Date.parse(anterior) > Date.parse(quando))
594
+ dados.em = anterior;
595
+ if ((existentes.has(caminho) && !regravavel(arco, caminho)) || mudancas.some(m => m.caminho === caminho)) {
596
+ throw new noctum_envelope_1.RecusaDoArco('noctum.coordenacao.concorrencia', `o registro ${caminho} já existe; a regra não é idempotente nele`);
597
+ }
598
+ const conteudo = JSON.stringify({ dados, assinatura: assinarDados(raiz, dados) }) + '\n';
599
+ if (Buffer.byteLength(conteudo) > exports.TETO_DO_REGISTRO)
600
+ throw new noctum_envelope_1.RecusaDoArco('noctum.envelope.invalido', `o registro ${caminho} passa de ${exports.TETO_DO_REGISTRO} bytes`);
601
+ mudancas.push({ caminho, conteudo });
602
+ gravados.push(dados);
603
+ }
604
+ // S-R3A-1 da rodada 3: o teto de registros por arco, que o leitor confere, também vale para quem grava.
605
+ if (leitura.registros.length + mudancas.filter(m => !existentes.has(m.caminho)).length > exports.TETO_DE_REGISTROS) {
606
+ throw new noctum_envelope_1.RecusaDoArco('noctum.envelope.invalido', `o arco passaria de ${exports.TETO_DE_REGISTROS} registros; nada foi ao ramo`);
607
+ }
608
+ // B1 da rodada 2: quem grava confere com a régua de quem lê, em memória e antes do push. O registro que as máquinas
609
+ // reprovariam (e que travaria o arco em todas) fica aqui, como pedido inválido, e nada vai ao ramo.
610
+ if (conferir) {
611
+ const trocados = new Set(mudancas.map(m => m.caminho));
612
+ const projetada = { ...leitura, registros: [...leitura.registros.filter(r => !trocados.has(r.caminho)),
613
+ ...gravados.map((dados, i) => ({ caminho: mudancas[i].caminho, dados }))] };
614
+ try {
615
+ conferir(projetada);
616
+ }
617
+ catch (e) {
618
+ if (!(e instanceof noctum_envelope_1.RecusaDoArco) || e.motivo !== 'noctum.coordenacao.adulterado')
619
+ throw e;
620
+ const porque = e.message.slice(`${e.motivo}: `.length).replace(/; o ramo de coordenação exige inspeção$/, '');
621
+ throw new noctum_envelope_1.RecusaDoArco('noctum.envelope.invalido', `as máquinas reprovariam o que esta gravação pede (${porque}); nada foi ao ramo`);
622
+ }
623
+ }
624
+ const tipos = [...new Set(gravados.map(g => g.tipo))].join(', ');
625
+ let commit;
626
+ try {
627
+ // S6 da revisão: o commit leva o nome da máquina, nunca o nome e o e-mail do git de quem a usa.
628
+ commit = (0, branch_de_estado_1.gravarNaBranch)(raiz, leitura.remoto, exports.BRANCH_DE_COORDENACAO, leitura.ponta, mudancas, `noctum: ${arco}, ${tipos} por ${eu.nome}`, PREFIXO, redeDe(raiz, opcoes), (0, exports.identidadeDoCommit)(eu.nome));
629
+ }
630
+ catch (e) {
631
+ throw semRemoto(primeiraLinha(e));
632
+ }
633
+ if (commit) {
634
+ avancarVista(raiz, dirDoArcoLocal(raiz, arco), leitura.remoto, arco, commit, quando);
635
+ return { resultado, commit, gravados, leitura };
636
+ }
637
+ }
638
+ throw new noctum_envelope_1.RecusaDoArco('noctum.coordenacao.concorrencia', `${exports.TENTATIVAS_DE_GRAVAR} pushes recusados seguidos no ramo de coordenação; tente de novo em instantes`);
639
+ }
640
+ /** As cabeças das branches pedidas no remoto, numa chamada de `git ls-remote`, só leitura. */
641
+ function cabecasNoRemoto(carregado, branches, opcoes = {}) {
642
+ const { raiz } = carregado;
643
+ const remoto = remotoDe(carregado, opcoes);
644
+ const rede = redeDe(raiz, opcoes);
645
+ const r = (0, node_child_process_1.spawnSync)('git', ['ls-remote', '--', remoto, ...branches.map(b => `refs/heads/${b}`)], { cwd: raiz, encoding: 'utf8', timeout: rede.timeoutMs ?? 15000, env: { ...process.env, ...rede.env } });
646
+ if (r.status !== 0 || r.error)
647
+ throw semRemoto(`git ls-remote falhou em ${remoto}: ${(r.stderr ?? '').trim().split('\n').pop() ?? ''}`);
648
+ const cabecas = new Map(branches.map(b => [b, null]));
649
+ for (const linha of String(r.stdout).split('\n')) {
650
+ const m = /^([0-9a-f]{40,64})\trefs\/heads\/(.+)$/.exec(linha.trim());
651
+ if (m && cabecas.has(m[2]))
652
+ cabecas.set(m[2], m[1]);
653
+ }
654
+ return cabecas;
655
+ }