@orkastery/cli 0.5.3 → 0.6.0
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.
- package/adapters/README.md +18 -0
- package/adapters/claude-code/.claude-plugin/plugin.json +1 -0
- package/adapters/claude-code/README.md +195 -5
- package/adapters/claude-code/agents/ork-check.md +1 -0
- package/adapters/claude-code/agents/ork-go.md +1 -0
- package/adapters/claude-code/agents/ork-goal.md +1 -0
- package/adapters/claude-code/agents/ork-master.md +1 -0
- package/adapters/claude-code/agents/ork-plan.md +1 -0
- package/adapters/claude-code/agents/ork-ship.md +1 -0
- package/adapters/claude-code/hooks/hooks.json +2 -1
- package/adapters/claude-code/hooks/ork-guard.js +15 -1
- package/adapters/claude-code/hooks/ork-resposta.js +91 -0
- package/adapters/claude-code/hooks/ork-sensor.js +18 -2
- package/adapters/claude-code/hooks/quota-signal.js +14 -0
- package/adapters/claude-code/mods/autoridade.json +56 -0
- package/adapters/claude-code/mods/ork-clima/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-clima/hooks/contas.ts +283 -0
- package/adapters/claude-code/mods/ork-clima/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-clima/hooks/previsao.ts +393 -0
- package/adapters/claude-code/mods/ork-clima/hooks/register.ts +205 -0
- package/adapters/claude-code/mods/ork-clima/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-clima/types/index.d.ts +80 -0
- package/adapters/claude-code/mods/ork-freio/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-freio/hooks/carga.ts +160 -0
- package/adapters/claude-code/mods/ork-freio/hooks/conferencia.ts +309 -0
- package/adapters/claude-code/mods/ork-freio/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-freio/hooks/lote.ts +270 -0
- package/adapters/claude-code/mods/ork-freio/hooks/register.ts +285 -0
- package/adapters/claude-code/mods/ork-freio/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-freio/types/index.d.ts +127 -0
- package/adapters/claude-code/mods/ork-mensageiro/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-mensageiro/hooks/caixa.ts +215 -0
- package/adapters/claude-code/mods/ork-mensageiro/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-mensageiro/hooks/painel.tsx +165 -0
- package/adapters/claude-code/mods/ork-mensageiro/hooks/recepcao.ts +256 -0
- package/adapters/claude-code/mods/ork-mensageiro/hooks/register.tsx +481 -0
- package/adapters/claude-code/mods/ork-mensageiro/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-mensageiro/types/index.d.ts +85 -0
- package/adapters/claude-code/mods/ork-mesa/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-mesa/hooks/cartoes.tsx +246 -0
- package/adapters/claude-code/mods/ork-mesa/hooks/decidir.ts +110 -0
- package/adapters/claude-code/mods/ork-mesa/hooks/fila.ts +253 -0
- package/adapters/claude-code/mods/ork-mesa/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-mesa/hooks/obvia.ts +98 -0
- package/adapters/claude-code/mods/ork-mesa/hooks/register.tsx +493 -0
- package/adapters/claude-code/mods/ork-mesa/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-mesa/types/index.d.ts +131 -0
- package/adapters/claude-code/mods/ork-pista/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-pista/hooks/acoes.ts +71 -0
- package/adapters/claude-code/mods/ork-pista/hooks/ci.ts +121 -0
- package/adapters/claude-code/mods/ork-pista/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-pista/hooks/pista.tsx +356 -0
- package/adapters/claude-code/mods/ork-pista/hooks/register.tsx +439 -0
- package/adapters/claude-code/mods/ork-pista/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-pista/types/index.d.ts +85 -0
- package/adapters/claude-code/mods/ork-ponte/.claude-plugin/plugin.json +13 -0
- package/adapters/claude-code/mods/ork-ponte/hooks/faixa.tsx +57 -0
- package/adapters/claude-code/mods/ork-ponte/hooks/guarda.ts +410 -0
- package/adapters/claude-code/mods/ork-ponte/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-ponte/hooks/mostrado.ts +55 -0
- package/adapters/claude-code/mods/ork-ponte/hooks/nucleo.ts +157 -0
- package/adapters/claude-code/mods/ork-ponte/hooks/papel.ts +42 -0
- package/adapters/claude-code/mods/ork-ponte/hooks/register.ts +331 -0
- package/adapters/claude-code/mods/ork-ponte/hooks/vigia.ts +76 -0
- package/adapters/claude-code/mods/ork-ponte/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-ponte/types/index.d.ts +178 -0
- package/adapters/claude-code/mods/ork-portao/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-portao/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-portao/hooks/lease.ts +201 -0
- package/adapters/claude-code/mods/ork-portao/hooks/plano.ts +304 -0
- package/adapters/claude-code/mods/ork-portao/hooks/register.ts +339 -0
- package/adapters/claude-code/mods/ork-portao/hooks/verify.ts +152 -0
- package/adapters/claude-code/mods/ork-portao/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-portao/types/index.d.ts +60 -0
- package/adapters/claude-code/mods/ork-radar/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-radar/hooks/colisao.ts +420 -0
- package/adapters/claude-code/mods/ork-radar/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-radar/hooks/mapa.tsx +322 -0
- package/adapters/claude-code/mods/ork-radar/hooks/register.tsx +470 -0
- package/adapters/claude-code/mods/ork-radar/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-radar/types/index.d.ts +72 -0
- package/adapters/claude-code/mods/ork-ritual/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-ritual/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-ritual/hooks/nota.ts +88 -0
- package/adapters/claude-code/mods/ork-ritual/hooks/register.tsx +494 -0
- package/adapters/claude-code/mods/ork-ritual/hooks/replay.tsx +271 -0
- package/adapters/claude-code/mods/ork-ritual/hooks/ritual.ts +218 -0
- package/adapters/claude-code/mods/ork-ritual/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-ritual/types/index.d.ts +106 -0
- package/adapters/claude-code/mods/ork-sentinela/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-sentinela/hooks/alertas.ts +85 -0
- package/adapters/claude-code/mods/ork-sentinela/hooks/despacho.ts +156 -0
- package/adapters/claude-code/mods/ork-sentinela/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-sentinela/hooks/register.ts +511 -0
- package/adapters/claude-code/mods/ork-sentinela/hooks/vigia.ts +186 -0
- package/adapters/claude-code/mods/ork-sentinela/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-sentinela/types/index.d.ts +65 -0
- package/adapters/claude-code/mods/ork-torre/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-torre/hooks/agenda.ts +60 -0
- package/adapters/claude-code/mods/ork-torre/hooks/detalhe.tsx +236 -0
- package/adapters/claude-code/mods/ork-torre/hooks/frases.ts +42 -0
- package/adapters/claude-code/mods/ork-torre/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-torre/hooks/painel.tsx +344 -0
- package/adapters/claude-code/mods/ork-torre/hooks/rede.ts +97 -0
- package/adapters/claude-code/mods/ork-torre/hooks/register.tsx +437 -0
- package/adapters/claude-code/mods/ork-torre/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-torre/types/index.d.ts +108 -0
- package/adapters/codex/skills/ork/SKILL.md +2 -0
- package/adapters/hermes/README.md +64 -1
- package/adapters/hermes/bin/ork-conhecimento-colecoes.sh +14 -0
- package/adapters/hermes/bin/ork-conhecimento-listar.sh +15 -0
- package/adapters/hermes/bin/ork-conhecimento-mostrar.sh +16 -0
- package/adapters/hermes/bin/ork-conhecimento-verificar.sh +15 -0
- package/adapters/hermes/bin/ork-vigia-batida.py +127 -0
- package/adapters/hermes/hermes.plugin.json +4 -0
- package/adapters/hermes/hitl-ingress/__init__.py +101 -8
- package/adapters/hermes/skills/orkastery-devmaster/SKILL.md +3 -3
- package/adapters/openclaw/README.md +83 -10
- package/adapters/openclaw/bin/ork-vigia-batida.js +86 -0
- package/adapters/openclaw/dist/hitl-ingress.js +106 -9
- package/adapters/openclaw/dist/index.js +86 -0
- package/adapters/openclaw/openclaw.plugin.json +34 -4
- package/adapters/openclaw/package.json +2 -2
- package/adapters/openclaw/src/hitl-ingress.ts +103 -9
- package/adapters/openclaw/src/index.ts +94 -0
- package/assets/adapters-en/claude-code/README.md +403 -0
- package/assets/adapters-en/claude-code/agents/ork-check.md +33 -0
- package/assets/adapters-en/claude-code/agents/ork-go.md +33 -0
- package/assets/adapters-en/claude-code/agents/ork-goal.md +34 -0
- package/assets/adapters-en/claude-code/agents/ork-master.md +35 -0
- package/assets/adapters-en/claude-code/agents/ork-plan.md +33 -0
- package/assets/adapters-en/claude-code/agents/ork-ship.md +33 -0
- package/assets/adapters-en/claude-code/commands/check.md +46 -0
- package/assets/adapters-en/claude-code/commands/go.md +46 -0
- package/assets/adapters-en/claude-code/commands/goal.md +55 -0
- package/assets/adapters-en/claude-code/commands/master.md +54 -0
- package/assets/adapters-en/claude-code/commands/onboarding.md +22 -0
- package/assets/adapters-en/claude-code/commands/ork.md +126 -0
- package/assets/adapters-en/claude-code/commands/plan.md +47 -0
- package/assets/adapters-en/claude-code/commands/ship.md +45 -0
- package/assets/adapters-en/codex/skills/ork/SKILL.md +85 -0
- package/assets/adapters-en/hermes/README.md +273 -0
- package/assets/adapters-en/hermes/skills/orkastery-devmaster/SKILL.md +169 -0
- package/assets/docs-en/markdownlint-cli2.jsonc +22 -0
- package/assets/docs-en/padroes/documentacao-de-produto.md +135 -0
- package/assets/docs-en/padroes/roadmap-de-produto.md +100 -0
- package/assets/docs-en/produto/README.md +14 -0
- package/assets/docs-en/produto/_modelo-feature.md +62 -0
- package/assets/docs-en/roadmap/README.md +14 -0
- package/assets/docs-en/roadmap/_modelo-item.md +75 -0
- package/assets/references-en/README.md +25 -0
- package/assets/references-en/code-review-axes.md +82 -0
- package/assets/references-en/definition-of-done.md +74 -0
- package/assets/references-en/performance-checklist.md +53 -0
- package/assets/references-en/security-checklist.md +101 -0
- package/assets/references-en/testing-patterns.md +51 -0
- package/assets/security-scan/defaults/.golangci.yml +2 -0
- package/assets/security-scan/defaults/eslint.config.mjs +11 -0
- package/assets/security-scan/defaults/gitleaks.toml +3 -0
- package/assets/security-scan/defaults/ruff.toml +1 -0
- package/assets/security-scan/tools-agil.json +87 -0
- package/assets/security-scan/tools.json +625 -0
- package/assets/skills-en/core/onboarding/SKILL.md +58 -0
- package/assets/skills-en/core/orkastery-bootstrap/SKILL.md +119 -0
- package/assets/skills-en/core/thread-state/SKILL.md +88 -0
- package/assets/skills-en/governance/decision-triage/SKILL.md +65 -0
- package/assets/skills-en/governance/narrative-guardian/SKILL.md +66 -0
- package/assets/skills-en/governance/roadmap-keeper/SKILL.md +60 -0
- package/assets/skills-en/governance/scope-check-capability-map/SKILL.md +71 -0
- package/assets/skills-en/observability/thread-tracing/SKILL.md +63 -0
- package/assets/skills-en/phases/check-quality/SKILL.md +103 -0
- package/assets/skills-en/phases/go-implementation/SKILL.md +78 -0
- package/assets/skills-en/phases/goal-definition/SKILL.md +69 -0
- package/assets/skills-en/phases/master-metrics/SKILL.md +72 -0
- package/assets/skills-en/phases/plan-specification/SKILL.md +67 -0
- package/assets/skills-en/phases/security-remediation/SKILL.md +70 -0
- package/assets/skills-en/phases/ship-release/SKILL.md +71 -0
- package/assets/skills-en/reviewers/code-reviewer/SKILL.md +61 -0
- package/assets/skills-en/reviewers/security-auditor/SKILL.md +93 -0
- package/assets/skills-en/reviewers/test-engineer/SKILL.md +66 -0
- package/assets/skills-en/reviewers/web-performance-auditor/SKILL.md +63 -0
- package/dist/adapters/claude-bg.js +193 -15
- package/dist/adapters/codex-controller-worker.js +12 -5
- package/dist/adapters/codex-events.js +16 -4
- package/dist/adapters/codex.js +13 -2
- package/dist/agents-md.js +24 -15
- package/dist/atualizar-branch.js +245 -0
- package/dist/auditoria.js +13 -11
- package/dist/auditrun.js +68 -48
- package/dist/autorizacao-duravel.js +327 -0
- package/dist/board.js +98 -43
- package/dist/branch-de-estado.js +39 -9
- package/dist/busca-semantica.js +7 -5
- package/dist/catalogo.js +35 -1
- package/dist/changelog-reconciliacao.js +290 -0
- package/dist/check-agil.js +500 -0
- package/dist/check-modo.js +41 -0
- package/dist/check-paralelo.js +183 -0
- package/dist/ci.js +255 -46
- package/dist/ciclos.js +8 -6
- package/dist/claim-lint.js +12 -8
- package/dist/claims-conferir.js +350 -0
- package/dist/claims.js +13 -10
- package/dist/company-brain-client.js +45 -3
- package/dist/company-brain-journal.js +2 -1
- package/dist/company-brain-migration.js +47 -6
- package/dist/conducao-batida.js +759 -0
- package/dist/conducao-sentinela.js +405 -0
- package/dist/conducao-texto.js +12 -18
- package/dist/conducao.js +206 -59
- package/dist/conhecimento-consulta.js +270 -0
- package/dist/conhecimento.js +514 -0
- package/dist/conta-esgotada.js +127 -0
- package/dist/dado-privado.js +816 -0
- package/dist/decisao-autonoma.js +5 -4
- package/dist/demo.js +14 -12
- package/dist/dependencias-da-worktree.js +25 -0
- package/dist/divida.js +38 -36
- package/dist/docs.js +103 -82
- package/dist/doctor-forja.js +345 -0
- package/dist/doctor-pulse-cron.js +294 -27
- package/dist/doctor-rede.js +18 -17
- package/dist/doctor.js +278 -116
- package/dist/dsn-da-memoria.js +417 -0
- package/dist/entrega-por-mod.js +503 -0
- package/dist/entrega-pr.js +158 -57
- package/dist/escopo-escrita.js +10 -1
- package/dist/esgotamentos.js +13 -11
- package/dist/estado-thread.js +42 -17
- package/dist/evalrunner.js +35 -32
- package/dist/experiencia-instalacao.js +45 -25
- package/dist/fabrica-batida.js +323 -0
- package/dist/fabrica-estado.js +49 -16
- package/dist/fabrica-publicar.js +3 -1
- package/dist/fila-pesada.js +444 -0
- package/dist/fix.js +24 -21
- package/dist/forja/adaptador.js +6 -0
- package/dist/forja/ambiente-de-ci.js +27 -0
- package/dist/forja/api.js +256 -0
- package/dist/forja/cache.js +89 -0
- package/dist/forja/cli.js +60 -0
- package/dist/forja/contrato.js +9 -0
- package/dist/forja/endereco.js +43 -0
- package/dist/forja/estado.js +22 -0
- package/dist/forja/executor.js +73 -0
- package/dist/forja/github/ci.js +95 -0
- package/dist/forja/github/dono-da-rede.js +18 -0
- package/dist/forja/github/metadados.js +15 -0
- package/dist/forja/github/pedidos.js +200 -0
- package/dist/forja/gitlab/ci.js +141 -0
- package/dist/forja/gitlab/consultas.js +59 -0
- package/dist/forja/gitlab/dono-da-rede.js +43 -0
- package/dist/forja/gitlab/metadados.js +15 -0
- package/dist/forja/gitlab/pedidos-abertos.js +158 -0
- package/dist/forja/gitlab/pedidos.js +141 -0
- package/dist/forja/produtor.js +32 -0
- package/dist/forja/registro.js +31 -0
- package/dist/forja/resolver.js +121 -0
- package/dist/forja/texto.js +13 -0
- package/dist/forja/versao.js +71 -0
- package/dist/forja.js +124 -81
- package/dist/gates.js +3 -28
- package/dist/handoff.js +33 -19
- package/dist/hitl-contract.js +46 -43
- package/dist/hitl-curto.js +37 -25
- package/dist/hitl-gates.js +13 -11
- package/dist/hitl-local-receipt.js +53 -0
- package/dist/hitl-local.js +11 -2
- package/dist/hitl-lock.js +5 -11
- package/dist/hitl-public-receipt.js +11 -0
- package/dist/hitl-resumo.js +72 -3
- package/dist/hitl-sessions.js +62 -9
- package/dist/hitl-tempo-parado.js +4 -4
- package/dist/hitl-texto-livre.js +7 -4
- package/dist/hitl.js +63 -64
- package/dist/horario.js +30 -7
- package/dist/hosts.js +155 -134
- package/dist/impedimento.js +108 -14
- package/dist/index.js +1421 -1019
- package/dist/indice-vetorial.js +14 -12
- package/dist/indice.js +7 -1
- package/dist/init.js +54 -49
- package/dist/instabilidade.js +144 -0
- package/dist/integracoes-locais.js +4 -0
- package/dist/intelligence-graph-cli.js +213 -88
- package/dist/intelligence-graph-contexto.js +56 -17
- package/dist/intelligence-graph-contract.js +2 -1
- package/dist/intelligence-graph-extract-md.js +56 -4
- package/dist/intelligence-graph-extract-ts.js +71 -3
- package/dist/intelligence-graph-query.js +96 -34
- package/dist/leases.js +19 -18
- package/dist/ledger-stats.js +17 -16
- package/dist/ledger.js +46 -2
- package/dist/licoes.js +12 -11
- package/dist/liveness.js +6 -3
- package/dist/locale-accounts.js +220 -0
- package/dist/locale-adapter.js +291 -0
- package/dist/locale-ajuda.js +833 -0
- package/dist/locale-audit.js +199 -0
- package/dist/locale-claims.js +213 -0
- package/dist/locale-conducao.js +138 -0
- package/dist/locale-conhecimento.js +181 -0
- package/dist/locale-decisao.js +205 -0
- package/dist/locale-docs.js +230 -0
- package/dist/locale-estado.js +101 -0
- package/dist/locale-forja.js +290 -0
- package/dist/locale-gerados.js +165 -0
- package/dist/locale-grafo.js +129 -0
- package/dist/locale-master.js +182 -0
- package/dist/locale-network.js +524 -0
- package/dist/locale-onboarding.js +92 -0
- package/dist/locale-operacao-restante.js +931 -0
- package/dist/locale-phase.js +151 -0
- package/dist/locale-projetos.js +100 -0
- package/dist/locale-prompts.js +194 -0
- package/dist/locale-pulse.js +349 -0
- package/dist/locale-reservas.js +87 -0
- package/dist/locale-retry.js +524 -0
- package/dist/locale-roadmap.js +102 -0
- package/dist/locale-security-scan.js +69 -0
- package/dist/locale-setup.js +192 -0
- package/dist/locale-ship.js +597 -0
- package/dist/locale-thread.js +421 -0
- package/dist/locale-verify.js +201 -0
- package/dist/locale-worktree.js +246 -0
- package/dist/locale.js +1258 -0
- package/dist/maestro-runtime.js +22 -3
- package/dist/manifest.js +415 -13
- package/dist/maquina-ociosa.js +211 -0
- package/dist/maquina.js +31 -7
- package/dist/master-digest.js +6 -1
- package/dist/master-nota-local.js +200 -0
- package/dist/master-nota.js +14 -8
- package/dist/master.js +139 -65
- package/dist/mcp-artifacts.js +11 -2
- package/dist/mcp-conhecimento.js +52 -0
- package/dist/mcp-experiencia.js +26 -5
- package/dist/mcp-git.js +23 -6
- package/dist/mcp-grafo.js +7 -4
- package/dist/mcp-install.js +73 -8
- package/dist/mcp-noctum.js +101 -0
- package/dist/mcp-server.js +90 -16
- package/dist/mcp-ship-transporte.js +977 -0
- package/dist/mcp-ship.js +71 -25
- package/dist/mcp-transportes.js +17 -0
- package/dist/mcp-verify.js +6 -1
- package/dist/memoria.js +63 -58
- package/dist/modos-migracao.js +8 -6
- package/dist/modos.js +20 -24
- package/dist/network-roadmap.js +90 -51
- package/dist/noctum-aceite.js +789 -0
- package/dist/noctum-arco.js +1418 -0
- package/dist/noctum-capacidade.js +141 -0
- package/dist/noctum-changelog.js +399 -0
- package/dist/noctum-cli.js +482 -0
- package/dist/noctum-coordenacao.js +1225 -0
- package/dist/noctum-entrega.js +1169 -0
- package/dist/noctum-envelope.js +1046 -0
- package/dist/noctum-execucao.js +376 -0
- package/dist/noctum-fechamento.js +405 -0
- package/dist/noctum-fila.js +314 -0
- package/dist/noctum-guarda.js +256 -0
- package/dist/noctum-integracao.js +507 -0
- package/dist/noctum-maquinas.js +383 -0
- package/dist/noctum-medicao.js +450 -0
- package/dist/noctum-modo.js +362 -0
- package/dist/noctum-pergunta-canal.js +96 -0
- package/dist/noctum-pergunta.js +115 -0
- package/dist/noctum-provas.js +838 -0
- package/dist/noctum-reconciliacao.js +591 -0
- package/dist/noctum-registro.js +215 -0
- package/dist/noctum-relatorio.js +827 -0
- package/dist/noctum-resposta.js +207 -0
- package/dist/noctum-sessoes-em-voo.js +93 -0
- package/dist/noctum-supervisao.js +649 -0
- package/dist/noctum-terminal.js +459 -0
- package/dist/noctum-transporte.js +655 -0
- package/dist/noctum-vigia.js +639 -0
- package/dist/ocupacao.js +32 -3
- package/dist/onboarding.js +56 -26
- package/dist/orkmind.js +48 -25
- package/dist/orquestracao.js +44 -47
- package/dist/parado-no-condutor.js +174 -127
- package/dist/phase.js +144 -63
- package/dist/policies.js +37 -4
- package/dist/portfolio.js +4 -2
- package/dist/postura-local.js +235 -0
- package/dist/preflight.js +11 -10
- package/dist/procedencia.js +220 -0
- package/dist/projeto-alvo.js +186 -24
- package/dist/prompts.js +140 -42
- package/dist/prova-minima.js +5 -6
- package/dist/pulse-cadencia.js +5 -4
- package/dist/pulse-consentimento.js +53 -1
- package/dist/pulse-delivery.js +151 -24
- package/dist/pulse-resposta.js +368 -4
- package/dist/pulse.js +188 -22
- package/dist/ratelimit.js +16 -6
- package/dist/recall.js +14 -13
- package/dist/redacao-saida.js +63 -0
- package/dist/rede-adesao.js +13 -8
- package/dist/rede-forja.js +108 -24
- package/dist/rede-status.js +66 -47
- package/dist/rede.js +86 -46
- package/dist/retomada-checkpoint.js +101 -0
- package/dist/retry.js +451 -212
- package/dist/roadmap-reservas.js +45 -28
- package/dist/roadmap-status.js +129 -33
- package/dist/runtime-ambiente.js +5 -0
- package/dist/runtime-context.js +284 -32
- package/dist/runtime-escada.js +423 -0
- package/dist/runtime-profiles.js +103 -46
- package/dist/runtimes.js +2 -1
- package/dist/saida-segura.js +3 -2
- package/dist/security-scan/agil.js +355 -0
- package/dist/security-scan/cli.js +76 -0
- package/dist/security-scan/compare.js +149 -0
- package/dist/security-scan/config.js +93 -0
- package/dist/security-scan/detect.js +97 -0
- package/dist/security-scan/executor.js +188 -0
- package/dist/security-scan/guard.js +180 -0
- package/dist/security-scan/imagem-de-lint.js +96 -0
- package/dist/security-scan/normalize/actionlint.js +12 -0
- package/dist/security-scan/normalize/eslint.js +12 -0
- package/dist/security-scan/normalize/gitleaks.js +13 -0
- package/dist/security-scan/normalize/golangci-lint.js +13 -0
- package/dist/security-scan/normalize/govulncheck.js +36 -0
- package/dist/security-scan/normalize/index.js +25 -0
- package/dist/security-scan/normalize/npm-audit.js +30 -0
- package/dist/security-scan/normalize/osv-scanner.js +32 -0
- package/dist/security-scan/normalize/oxlint.js +51 -0
- package/dist/security-scan/normalize/pip-audit.js +16 -0
- package/dist/security-scan/normalize/ruff.js +12 -0
- package/dist/security-scan/normalize/semgrep.js +20 -0
- package/dist/security-scan/normalize/trivy.js +33 -0
- package/dist/security-scan/normalize/util.js +204 -0
- package/dist/security-scan/normalize/zizmor.js +19 -0
- package/dist/security-scan/report.js +156 -0
- package/dist/security-scan/run.js +448 -0
- package/dist/security-scan/tools.js +62 -0
- package/dist/security-scan/trabalhador.js +74 -0
- package/dist/security-scan/types.js +15 -0
- package/dist/session-events.js +26 -5
- package/dist/session-watcher-claude.js +81 -20
- package/dist/session-watcher.js +340 -42
- package/dist/sessoes-contas.js +10 -1
- package/dist/sessoes-inventario.js +1 -1
- package/dist/sessoes.js +25 -23
- package/dist/setup.js +59 -58
- package/dist/ship-publicar.js +971 -0
- package/dist/ship.js +243 -97
- package/dist/steal.js +77 -0
- package/dist/superficie.js +14 -11
- package/dist/thread-close.js +12 -11
- package/dist/thread.js +274 -100
- package/dist/tokens.js +15 -11
- package/dist/trabalho-thread.js +297 -0
- package/dist/transporte-do-canal.js +189 -0
- package/dist/types.js +12 -1
- package/dist/util.js +13 -3
- package/dist/varredura-acoes.js +507 -0
- package/dist/verify.js +608 -76
- package/dist/vida-da-thread.js +254 -0
- package/dist/worktree-local.js +167 -0
- package/dist/worktree.js +99 -83
- package/dist/yaml.js +27 -8
- package/eval/casos/check-quality.json +77 -0
- package/eval/casos/security-auditor.json +148 -0
- package/eval/casos/security-remediation.json +161 -0
- package/eval/fixtures/security-scan/README.md +8 -0
- package/eval/fixtures/security-scan/go/.github/workflows/ci.yml +12 -0
- package/eval/fixtures/security-scan/go/Dockerfile +4 -0
- package/eval/fixtures/security-scan/go/README.md +4 -0
- package/eval/fixtures/security-scan/go/config.go +4 -0
- package/eval/fixtures/security-scan/go/go.mod +5 -0
- package/eval/fixtures/security-scan/go/go.sum +2 -0
- package/eval/fixtures/security-scan/go/main.go +31 -0
- package/eval/fixtures/security-scan/js/.github/workflows/ci.yml +12 -0
- package/eval/fixtures/security-scan/js/Dockerfile +3 -0
- package/eval/fixtures/security-scan/js/README.md +4 -0
- package/eval/fixtures/security-scan/js/package-lock.json +844 -0
- package/eval/fixtures/security-scan/js/package.json +10 -0
- package/eval/fixtures/security-scan/js/src/config.js +4 -0
- package/eval/fixtures/security-scan/js/src/server.js +17 -0
- package/eval/fixtures/security-scan/js/src/util.ts +4 -0
- package/eval/fixtures/security-scan/js/tsconfig.json +4 -0
- package/eval/fixtures/security-scan/python/.github/workflows/ci.yml +12 -0
- package/eval/fixtures/security-scan/python/Dockerfile +3 -0
- package/eval/fixtures/security-scan/python/README.md +4 -0
- package/eval/fixtures/security-scan/python/app/__init__.py +0 -0
- package/eval/fixtures/security-scan/python/app/config.py +2 -0
- package/eval/fixtures/security-scan/python/app/main.py +19 -0
- package/eval/fixtures/security-scan/python/requirements.txt +1 -0
- package/package.json +9 -4
- package/references/security-checklist.md +3 -3
- package/schemas/code-artifact-graph.v1.schema.json +2 -1
- package/skills/README.md +1 -0
- package/skills/core/orkastery-bootstrap/SKILL.md +2 -2
- package/skills/phases/check-quality/SKILL.md +16 -0
- package/skills/phases/go-implementation/SKILL.md +6 -0
- package/skills/phases/security-remediation/SKILL.md +70 -0
- package/skills/reviewers/security-auditor/SKILL.md +25 -0
|
@@ -0,0 +1,759 @@
|
|
|
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.ESPERA_LONGA_MS = exports.VALIDADE_DO_RETRATO_MS = exports.ARQUIVO_DA_BATIDA = exports.CONTRATO_DA_BATIDA = void 0;
|
|
37
|
+
exports.passosDaBatida = passosDaBatida;
|
|
38
|
+
exports.lerPrsAbertos = lerPrsAbertos;
|
|
39
|
+
exports.lerChecksObrigatorios = lerChecksObrigatorios;
|
|
40
|
+
exports.assuntoDoMerge = assuntoDoMerge;
|
|
41
|
+
exports.comandoDoMerge = comandoDoMerge;
|
|
42
|
+
exports.avaliarPr = avaliarPr;
|
|
43
|
+
exports.alinhamentoDaBranch = alinhamentoDaBranch;
|
|
44
|
+
exports.conduzirBatida = conduzirBatida;
|
|
45
|
+
exports.lerRetratoDaBatida = lerRetratoDaBatida;
|
|
46
|
+
exports.baterConducao = baterConducao;
|
|
47
|
+
exports.pendentesDaUltimaBatida = pendentesDaUltimaBatida;
|
|
48
|
+
exports.textoDaBatida = textoDaBatida;
|
|
49
|
+
/**
|
|
50
|
+
* RM-063 (F7): a batida da conducao, o passo deterministico que a varredura chama para avancar as threads dentro da
|
|
51
|
+
* autorizacao duravel do dono (`ork conducao batida`).
|
|
52
|
+
*
|
|
53
|
+
* Incidente de 12 e 13/10/2026: os PRs #153 e #154 ficaram prontos, com os checks verdes, e esperaram de 5 a 6 horas pelo
|
|
54
|
+
* condutor, que era uma sessao interativa (P6 da pagina da RM-063: acabou a conversa, acabou a conducao). A batida faz,
|
|
55
|
+
* a cada rodada do cron e no `#OrkRetomar` do canal, o que o condutor fazia, e so o que o dono autorizou:
|
|
56
|
+
*
|
|
57
|
+
* - `merge`: o PR aberto de branch `ork/*` de uma thread #Auto ou #Maestro, com todos os checks obrigatorios verdes e o
|
|
58
|
+
* PR limpo (`mergeStateStatus` CLEAN), entra na base por `gh pr merge --merge --match-head-commit`, com o assunto
|
|
59
|
+
* `ship(<thread>): ...` que o registro da entrega procura; o merge e provado na copia da base. O PR que ficou para
|
|
60
|
+
* tras, ou em conflito so no CHANGELOG, ganha o merge da base na branch (`atualizar-branch.ts`) e espera o CI de novo;
|
|
61
|
+
* conflito em codigo vira item do dono;
|
|
62
|
+
* - `registrar`, `master` e `limpar`: os da varredura da F5 (`varredura-acoes.ts`), na mesma rodada, depois do merge;
|
|
63
|
+
* - `despachar`: a fase seguinte que o nucleo calcula (`passosDoCondutor`, o mesmo passo da Sentinela), so com o pedido
|
|
64
|
+
* guardado na criacao da thread, e a retomada pelo retry com o prompt gravado (F3) quando o plano e automatico. A
|
|
65
|
+
* fase que precisa de LLM sai como sessao de fundo pelo `ork phase run`, nunca por cron LLM. Com a Sentinela ligada
|
|
66
|
+
* nesta maquina, o despacho comum e dela; a retomada por cota continua na batida da maquina.
|
|
67
|
+
*
|
|
68
|
+
* Nada aqui faz tag nem publicacao. Autorizacao ausente, vencida, de outra maquina ou sem a acao, check pendente ou
|
|
69
|
+
* vermelho, PR em rascunho e trabalho na thread recusam com motivo tipado, e cada recusa vira item da fila do pulse
|
|
70
|
+
* pelo retrato da ultima batida (`.orkastery/monitor/conducao-batida.json`). Cada acao feita ou recusada vai ao ledger
|
|
71
|
+
* da thread como `varredura_acao`, com a autorizacao que a permitiu.
|
|
72
|
+
*/
|
|
73
|
+
const fs = __importStar(require("node:fs"));
|
|
74
|
+
const path = __importStar(require("node:path"));
|
|
75
|
+
const node_crypto_1 = require("node:crypto");
|
|
76
|
+
const autorizacao_duravel_1 = require("./autorizacao-duravel");
|
|
77
|
+
const atualizar_branch_1 = require("./atualizar-branch");
|
|
78
|
+
const conducao_1 = require("./conducao");
|
|
79
|
+
const conducao_sentinela_1 = require("./conducao-sentinela");
|
|
80
|
+
const entrega_pr_1 = require("./entrega-pr");
|
|
81
|
+
const estado_thread_1 = require("./estado-thread");
|
|
82
|
+
const ledger_1 = require("./ledger");
|
|
83
|
+
const locale_1 = require("./locale");
|
|
84
|
+
const maquina_1 = require("./maquina");
|
|
85
|
+
const monitor_lock_1 = require("./monitor-lock");
|
|
86
|
+
const doctor_pulse_cron_1 = require("./doctor-pulse-cron");
|
|
87
|
+
const parado_no_condutor_1 = require("./parado-no-condutor");
|
|
88
|
+
const ship_publicar_1 = require("./ship-publicar");
|
|
89
|
+
const phase_1 = require("./phase");
|
|
90
|
+
const retry_1 = require("./retry");
|
|
91
|
+
const thread_1 = require("./thread");
|
|
92
|
+
const util_1 = require("./util");
|
|
93
|
+
const varredura_acoes_1 = require("./varredura-acoes");
|
|
94
|
+
const worktree_1 = require("./worktree");
|
|
95
|
+
const session_watcher_1 = require("./session-watcher");
|
|
96
|
+
const noctum_guarda_1 = require("./noctum-guarda");
|
|
97
|
+
exports.CONTRATO_DA_BATIDA = 'ork.conducao-batida/v1';
|
|
98
|
+
exports.ARQUIVO_DA_BATIDA = 'conducao-batida.json';
|
|
99
|
+
/** O retrato da ultima batida vale para o pulse por tres batidas do template (`*\/15`); depois, nada se afirma dele. */
|
|
100
|
+
exports.VALIDADE_DO_RETRATO_MS = 45 * 60 * 1000;
|
|
101
|
+
/** A espera da batida (check pendente, estado do merge desconhecido) que passa disto vira item do dono. */
|
|
102
|
+
exports.ESPERA_LONGA_MS = 2 * 60 * 60 * 1000;
|
|
103
|
+
/** Os modos em que a #TAG ja autoriza o push (`MODOS_QUE_AUTORIZAM_PUSH`, `ship.ts`): so neles a batida mescla e despacha. */
|
|
104
|
+
const MODOS_QUE_ANDAM_SOZINHOS = ['auto', 'maestro'];
|
|
105
|
+
const PRAZO_DO_GH_MS = 30000;
|
|
106
|
+
const CAMPOS_DO_PR = 'number,title,headRefName,headRefOid,baseRefName,isDraft,isCrossRepository,mergeable,mergeStateStatus,url';
|
|
107
|
+
/**
|
|
108
|
+
* RM-064 T5: a Sentinela pode conduzir fases, mas a conta dela também pode esgotar. A reação ao sinal tipado de cota
|
|
109
|
+
* pertence à batida da máquina; todo o resto continua cedido, evitando dois condutores gerais concorrentes.
|
|
110
|
+
*/
|
|
111
|
+
function passosDaBatida(passos, sentinela) {
|
|
112
|
+
if (!sentinela)
|
|
113
|
+
return [...passos];
|
|
114
|
+
return passos.filter((p) => p.acao === 'retomar' && p.bloqueio?.motivo === 'runtime.quota-exhausted');
|
|
115
|
+
}
|
|
116
|
+
// ---------------------------------------------------------------------------
|
|
117
|
+
// A forja: os PRs abertos e os checks obrigatorios.
|
|
118
|
+
// ---------------------------------------------------------------------------
|
|
119
|
+
const texto = (v) => (typeof v === 'string' ? v : '');
|
|
120
|
+
/** Os PRs abertos da base, so do mesmo repositorio. */
|
|
121
|
+
function lerPrsAbertos(executor, forja, base) {
|
|
122
|
+
const r = executor(['pr', 'list', `--repo=${forja.host}/${forja.repositorio}`, `--base=${base}`, '--state=open', '--limit=100',
|
|
123
|
+
`--json=${CAMPOS_DO_PR}`], PRAZO_DO_GH_MS);
|
|
124
|
+
if (r.status !== 0)
|
|
125
|
+
return { ok: false, erro: `gh pr list saiu ${r.status ?? 'sem código'}: ${r.stderr.trim().split('\n')[0]?.slice(0, 160) ?? ''}` };
|
|
126
|
+
let bruto;
|
|
127
|
+
try {
|
|
128
|
+
bruto = JSON.parse(r.stdout);
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
return { ok: false, erro: 'gh pr list respondeu JSON inválido' };
|
|
132
|
+
}
|
|
133
|
+
if (!Array.isArray(bruto))
|
|
134
|
+
return { ok: false, erro: 'gh pr list não respondeu uma lista' };
|
|
135
|
+
const prs = [];
|
|
136
|
+
for (const x of bruto) {
|
|
137
|
+
if (!x || typeof x !== 'object' || x.isCrossRepository === true || !Number.isInteger(x.number))
|
|
138
|
+
continue;
|
|
139
|
+
if (!/^[0-9a-f]{40}$/.test(texto(x.headRefOid)))
|
|
140
|
+
continue;
|
|
141
|
+
prs.push({ numero: x.number, titulo: texto(x.title).slice(0, 240), branch: texto(x.headRefName), head: texto(x.headRefOid),
|
|
142
|
+
base: texto(x.baseRefName), rascunho: x.isDraft === true, mergeable: texto(x.mergeable) || 'UNKNOWN',
|
|
143
|
+
estado: texto(x.mergeStateStatus) || 'UNKNOWN', url: texto(x.url) || null });
|
|
144
|
+
}
|
|
145
|
+
return { ok: true, prs };
|
|
146
|
+
}
|
|
147
|
+
/** Os checks obrigatorios do PR. Sem nenhum obrigatorio na base, a lista vem vazia (e o merge fica com o dono). */
|
|
148
|
+
function lerChecksObrigatorios(executor, forja, numero) {
|
|
149
|
+
const r = executor(['pr', 'checks', String(numero), `--repo=${forja.host}/${forja.repositorio}`, '--required', '--json=name,bucket,state'], PRAZO_DO_GH_MS);
|
|
150
|
+
// O `gh pr checks` sai 8 com check pendente e 1 com check falho: o JSON vem nos dois casos.
|
|
151
|
+
try {
|
|
152
|
+
const lido = JSON.parse(r.stdout);
|
|
153
|
+
if (Array.isArray(lido)) {
|
|
154
|
+
return { ok: true, checks: lido.map((c) => ({ nome: texto(c.name), bucket: texto(c.bucket) })) };
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
catch { /* sem JSON: a base sem check obrigatorio, ou falha do gh */ }
|
|
158
|
+
if (/no required checks/i.test(`${r.stderr}\n${r.stdout}`))
|
|
159
|
+
return { ok: true, checks: [] };
|
|
160
|
+
return { ok: false, erro: `gh pr checks --required saiu ${r.status ?? 'sem código'}: ${(r.stderr || r.stdout).trim().split('\n')[0]?.slice(0, 160) ?? ''}` };
|
|
161
|
+
}
|
|
162
|
+
/** O assunto do merge: o `ship(<thread>)` que o registro da entrega e a varredura procuram na base. */
|
|
163
|
+
function assuntoDoMerge(thread, pr) {
|
|
164
|
+
const titulo = pr.titulo.trim();
|
|
165
|
+
const comPrefixo = titulo.startsWith(`ship(${thread})`) ? titulo : `ship(${thread}): ${titulo || `entrega do PR #${pr.numero}`}`;
|
|
166
|
+
return `${comPrefixo} (#${pr.numero})`;
|
|
167
|
+
}
|
|
168
|
+
/** O comando do merge que o dono roda (e que a batida roda com a autorizacao). */
|
|
169
|
+
function comandoDoMerge(forja, thread, pr) {
|
|
170
|
+
// O titulo do PR vem do agente que o abriu: entre aspas simples, `$(...)` e crase colados no terminal nao rodam.
|
|
171
|
+
return `gh pr merge ${pr.numero} --repo ${forja.host}/${forja.repositorio} --merge --match-head-commit ${pr.head} ` +
|
|
172
|
+
`--subject ${(0, doctor_pulse_cron_1.citarNoShell)(assuntoDoMerge(thread, pr))}`;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* O estado do merge, puro: o que o PR precisa antes de entrar. `pronto` so com os checks obrigatorios todos verdes (ou
|
|
176
|
+
* pulados) e o PR limpo; `atualizar` quando a branch ficou para tras ou conflita (o merge da base resolve, ou diz o
|
|
177
|
+
* conflito em codigo); o resto e espera da batida ou bloqueio do dono.
|
|
178
|
+
*/
|
|
179
|
+
function avaliarPr(pr, checks) {
|
|
180
|
+
if (pr.rascunho || pr.estado === 'DRAFT')
|
|
181
|
+
return { estado: 'dono', motivo: 'pr-rascunho', detalhe: `o PR #${pr.numero} está em rascunho` };
|
|
182
|
+
if (pr.mergeable === 'CONFLICTING' || pr.estado === 'DIRTY' || pr.estado === 'BEHIND')
|
|
183
|
+
return { estado: 'atualizar' };
|
|
184
|
+
const vermelhos = checks.filter((c) => c.bucket === 'fail' || c.bucket === 'cancel');
|
|
185
|
+
if (vermelhos.length) {
|
|
186
|
+
return { estado: 'dono', motivo: 'checks-vermelhos', detalhe: `check obrigatório vermelho: ${vermelhos.map((c) => c.nome).join(', ').slice(0, 200)}` };
|
|
187
|
+
}
|
|
188
|
+
const pendentes = checks.filter((c) => c.bucket === 'pending' || !['pass', 'skipping'].includes(c.bucket));
|
|
189
|
+
if (pendentes.length) {
|
|
190
|
+
return { estado: 'espera', motivo: 'checks-pendentes', detalhe: `${pendentes.length} de ${checks.length} check(s) obrigatório(s) pendente(s)` };
|
|
191
|
+
}
|
|
192
|
+
if (!checks.length) {
|
|
193
|
+
return { estado: 'dono', motivo: 'sem-checks-obrigatorios', detalhe: 'a base não exige check nenhum: o merge sem check obrigatório verde fica com o dono' };
|
|
194
|
+
}
|
|
195
|
+
if (pr.estado === 'CLEAN' || pr.estado === 'HAS_HOOKS')
|
|
196
|
+
return { estado: 'pronto' };
|
|
197
|
+
if (pr.estado === 'BLOCKED')
|
|
198
|
+
return { estado: 'dono', motivo: 'pr-bloqueado', detalhe: 'o GitHub bloqueia o merge com os checks obrigatórios verdes' };
|
|
199
|
+
if (pr.estado === 'UNSTABLE')
|
|
200
|
+
return { estado: 'dono', motivo: 'pr-instavel', detalhe: 'há check não obrigatório vermelho' };
|
|
201
|
+
return { estado: 'espera', motivo: 'estado-desconhecido', detalhe: `o GitHub ainda não calculou o estado do merge (${pr.estado})` };
|
|
202
|
+
}
|
|
203
|
+
// ---------------------------------------------------------------------------
|
|
204
|
+
// O registro de cada acao no ledger da thread.
|
|
205
|
+
// ---------------------------------------------------------------------------
|
|
206
|
+
/** As recusas de uma tentativa da batida: so elas (e o que ela fez) vao ao ledger; sem autorizacao ou com bloqueio, so a fila. */
|
|
207
|
+
const TENTATIVAS_RECUSADAS = ['merge-recusado', 'atualizacao-recusada', 'despacho-recusado', 'retomada-recusada',
|
|
208
|
+
'conflito-em-codigo', 'merge-sem-prova'];
|
|
209
|
+
function registrarAcao(raiz, item, leitura) {
|
|
210
|
+
// O `gh pr merge` que respondeu sem a prova na base (fila de merge, merge automatico) e uma tentativa: vai ao ledger.
|
|
211
|
+
if (item.resultado !== 'feita' && !TENTATIVAS_RECUSADAS.includes(item.motivo ?? ''))
|
|
212
|
+
return;
|
|
213
|
+
const referencia = (0, autorizacao_duravel_1.referenciaDaAutorizacao)(leitura);
|
|
214
|
+
const resultado = item.resultado === 'feita' ? 'feita' : 'recusada';
|
|
215
|
+
const detalhe = item.detalhe.slice(0, 400);
|
|
216
|
+
let anterior;
|
|
217
|
+
try {
|
|
218
|
+
anterior = [...(0, ledger_1.lerLedger)((0, thread_1.dirThread)(raiz, item.thread))].reverse()
|
|
219
|
+
.find((e) => e.tipo === ledger_1.TIPOS_DE_EVENTO.acaoDaVarredura && e.acao === item.acao);
|
|
220
|
+
}
|
|
221
|
+
catch { /* sem ledger, a linha vai */ }
|
|
222
|
+
// A mesma recusa da rodada anterior nao vira linha nova a cada batida.
|
|
223
|
+
if (resultado === 'recusada' && anterior?.resultado === 'recusada' && anterior.detalhe === detalhe && anterior.motivo === item.motivo)
|
|
224
|
+
return;
|
|
225
|
+
try {
|
|
226
|
+
(0, ledger_1.registrar)((0, thread_1.dirThread)(raiz, item.thread), item.thread, ledger_1.TIPOS_DE_EVENTO.acaoDaVarredura, {
|
|
227
|
+
fase: item.fase, acao: item.acao, resultado, motivo: item.motivo, detalhe, comando: item.comando, origem: 'conducao.batida',
|
|
228
|
+
...(item.pr ? { pr: item.pr } : {}), autorizacao: referencia,
|
|
229
|
+
quemDecidiu: referencia ? `batida da condução do ork, pela autorização durável de ${String(referencia.por)}` : null,
|
|
230
|
+
evidencia: item.evidencia,
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
catch { /* a acao vale; o ledger e o rastro */ }
|
|
234
|
+
}
|
|
235
|
+
// ---------------------------------------------------------------------------
|
|
236
|
+
// As acoes.
|
|
237
|
+
// ---------------------------------------------------------------------------
|
|
238
|
+
const andaSozinha = (t) => MODOS_QUE_ANDAM_SOZINHOS.includes(t.modo);
|
|
239
|
+
/** O motivo da autorizacao que impede a acao, ou `null` quando ela esta autorizada. */
|
|
240
|
+
function semAutorizacao(leitura, acao) {
|
|
241
|
+
if (!leitura.valida)
|
|
242
|
+
return leitura.motivo;
|
|
243
|
+
return (0, autorizacao_duravel_1.autorizada)(leitura, acao) ? null : 'acao-fora-da-autorizacao';
|
|
244
|
+
}
|
|
245
|
+
function threadsAbertas(raiz) {
|
|
246
|
+
const saida = [];
|
|
247
|
+
for (const id of (0, thread_1.listarIds)(raiz)) {
|
|
248
|
+
try {
|
|
249
|
+
const t = (0, thread_1.lerThread)(raiz, id);
|
|
250
|
+
if (t.status !== 'fechada')
|
|
251
|
+
saida.push(t);
|
|
252
|
+
}
|
|
253
|
+
catch { /* thread ilegivel fica de fora */ }
|
|
254
|
+
}
|
|
255
|
+
return saida;
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* A branch local da thread contra o head do PR. `igual`; `atras` (o PR tem commits que a local nao tem: o "Update branch"
|
|
259
|
+
* do GitHub ou um push de outra maquina); `merges-da-base` (a local so tem, alem do PR, merges da base, como os que a
|
|
260
|
+
* batida faz e ainda nao empurrou); `fora-do-pr` (a local tem trabalho que o PR nao tem: o merge levaria a entrega sem
|
|
261
|
+
* ele, e a atualizacao o empurraria sem CHECK); `sem-resposta` (o git nao respondeu, ou o head do PR nao chegou aqui).
|
|
262
|
+
*/
|
|
263
|
+
function alinhamentoDaBranch(raiz, remoto, branch, head, baseRef) {
|
|
264
|
+
const local = (0, util_1.exec)('git', ['rev-parse', '--verify', '-q', `refs/heads/${branch}^{commit}`], raiz, 30000);
|
|
265
|
+
if (!local.ok)
|
|
266
|
+
return { estado: 'igual', fora: 0 };
|
|
267
|
+
if (local.stdout.trim() === head)
|
|
268
|
+
return { estado: 'igual', fora: 0 };
|
|
269
|
+
if (!(0, util_1.exec)('git', ['cat-file', '-e', `${head}^{commit}`], raiz, 30000).ok) {
|
|
270
|
+
const env = { ...process.env, GIT_TERMINAL_PROMPT: '0', GIT_SSH_COMMAND: process.env.GIT_SSH_COMMAND ?? 'ssh -o BatchMode=yes' };
|
|
271
|
+
(0, util_1.exec)('git', ['fetch', '-q', '--no-tags', '--', remoto, `+refs/heads/${branch}:refs/remotes/${remoto}/${branch}`], raiz, 60000, env);
|
|
272
|
+
if (!(0, util_1.exec)('git', ['cat-file', '-e', `${head}^{commit}`], raiz, 30000).ok)
|
|
273
|
+
return { estado: 'sem-resposta', fora: 0 };
|
|
274
|
+
}
|
|
275
|
+
// So o que a local tem alem do PR e da base: os commits que o merge da base trouxe sao da base, nao trabalho da thread.
|
|
276
|
+
const r = (0, util_1.exec)('git', ['rev-list', '--parents', `${head}..refs/heads/${branch}`, '--not', baseRef], raiz, 30000);
|
|
277
|
+
if (!r.ok)
|
|
278
|
+
return { estado: 'sem-resposta', fora: 0 };
|
|
279
|
+
const fora = r.stdout.split('\n').map((l) => l.trim().split(/\s+/)).filter((l) => l[0]);
|
|
280
|
+
if (!fora.length)
|
|
281
|
+
return { estado: 'atras', fora: 0 };
|
|
282
|
+
const daBase = (c) => c.length === 3 && (0, util_1.exec)('git', ['merge-base', '--is-ancestor', c[2], baseRef], raiz, 30000).ok;
|
|
283
|
+
return fora.every(daBase) ? { estado: 'merges-da-base', fora: fora.length } : { estado: 'fora-do-pr', fora: fora.filter((c) => !daBase(c)).length };
|
|
284
|
+
}
|
|
285
|
+
function anotar(ctx, item) {
|
|
286
|
+
(item.resultado === 'feita' ? ctx.feitas : ctx.pendentes).push(item);
|
|
287
|
+
registrarAcao(ctx.raiz, item, ctx.leitura);
|
|
288
|
+
}
|
|
289
|
+
/** O merge dos PRs prontos (e a atualizacao dos que ficaram para tras), thread por thread. */
|
|
290
|
+
function mesclarProntos(ctx, threads, opcoes) {
|
|
291
|
+
const { carregado, raiz } = ctx;
|
|
292
|
+
const remoto = carregado.manifesto.fabrica.remoto, base = carregado.manifesto.worktree.base_branch;
|
|
293
|
+
const candidatas = threads.filter((t) => andaSozinha(t) && (0, worktree_1.branchDaWorktree)(t).startsWith('ork/'));
|
|
294
|
+
if (!candidatas.length)
|
|
295
|
+
return null;
|
|
296
|
+
const forja = opcoes.forja === undefined ? (0, parado_no_condutor_1.githubDoRemoto)(raiz, remoto) : opcoes.forja;
|
|
297
|
+
if (!forja)
|
|
298
|
+
return { ok: false, detalhe: `o remoto ${remoto} não é um GitHub que o gh lê: o merge fica com o dono` };
|
|
299
|
+
const executor = opcoes.executorGh ?? parado_no_condutor_1.ghPadrao;
|
|
300
|
+
const lidos = lerPrsAbertos(executor, forja, base);
|
|
301
|
+
if (!lidos.ok)
|
|
302
|
+
return { ok: false, detalhe: lidos.erro };
|
|
303
|
+
for (const t of candidatas) {
|
|
304
|
+
const branch = (0, worktree_1.branchDaWorktree)(t);
|
|
305
|
+
const pr = lidos.prs.filter((p) => p.branch === branch && p.base === base).sort((a, b) => b.numero - a.numero)[0];
|
|
306
|
+
if (!pr)
|
|
307
|
+
continue;
|
|
308
|
+
const item = (resultado, motivo, detalhe, oQue, comando) => ({
|
|
309
|
+
acao: 'merge', thread: t.id, fase: t.faseAtual, resultado, motivo, detalhe,
|
|
310
|
+
oQue: oQue ?? (0, locale_1.textoDoCatalogo)((m) => m.pulse.varredura.oQue.merge(pr.numero, branch, `${remoto}/${base}`)),
|
|
311
|
+
comando: comando ?? comandoDoMerge(forja, t.id, pr), evidencia: `gh pr view ${pr.numero}: ${pr.estado}, mergeable ${pr.mergeable}`,
|
|
312
|
+
desdeEm: null, pr: pr.numero
|
|
313
|
+
});
|
|
314
|
+
let eventos = [];
|
|
315
|
+
try {
|
|
316
|
+
eventos = (0, ledger_1.lerLedger)((0, thread_1.dirThread)(raiz, t.id));
|
|
317
|
+
}
|
|
318
|
+
catch {
|
|
319
|
+
eventos = [];
|
|
320
|
+
}
|
|
321
|
+
// RM-058, integracao (T1.2, D3 e D4): o PR da thread que um arco #Noctum segura ou retem nao e mesclado nem atualizado
|
|
322
|
+
// por aqui: a entrega e do passo da entrega do arco. O pulo fica no retrato, como as esperas, e nao no ledger da thread.
|
|
323
|
+
const arco = (0, noctum_guarda_1.arcoDoPasso)(raiz, ctx.arcos, t.id, eventos);
|
|
324
|
+
if (arco) {
|
|
325
|
+
anotar(ctx, item('espera', arco.motivo, arco.detalhe));
|
|
326
|
+
continue;
|
|
327
|
+
}
|
|
328
|
+
const checks = lerChecksObrigatorios(executor, forja, pr.numero);
|
|
329
|
+
if (!checks.ok) {
|
|
330
|
+
anotar(ctx, item('espera', 'estado-desconhecido', checks.erro));
|
|
331
|
+
continue;
|
|
332
|
+
}
|
|
333
|
+
const avaliacao = avaliarPr(pr, checks.checks);
|
|
334
|
+
if (avaliacao.estado === 'espera') {
|
|
335
|
+
anotar(ctx, item('espera', avaliacao.motivo, avaliacao.detalhe));
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
if (avaliacao.estado === 'dono') {
|
|
339
|
+
anotar(ctx, item('dono', avaliacao.motivo, avaliacao.detalhe));
|
|
340
|
+
continue;
|
|
341
|
+
}
|
|
342
|
+
// Pronto ou para atualizar: primeiro o estado (quem trabalha na thread, o que espera o dono, a branch local e o
|
|
343
|
+
// CHECK), e so depois a autorizacao: o dono so recebe "pronto" do PR que esta pronto de verdade.
|
|
344
|
+
const atualizar = avaliacao.estado === 'atualizar';
|
|
345
|
+
const oQue = atualizar ? (0, locale_1.textoDoCatalogo)((m) => m.pulse.varredura.oQue.atualizar(pr.numero, branch, `${remoto}/${base}`)) : undefined;
|
|
346
|
+
const comando = atualizar ? `ork conducao atualizar ${t.id} --enviar` : undefined;
|
|
347
|
+
const trabalho = (0, varredura_acoes_1.trabalhoNaWorktree)(raiz, t, ctx.quando, ctx.vivas);
|
|
348
|
+
if (trabalho) {
|
|
349
|
+
anotar(ctx, item('dono', trabalho.motivo, trabalho.texto, oQue, `ork thread status ${t.id}`));
|
|
350
|
+
continue;
|
|
351
|
+
}
|
|
352
|
+
const doDono = (0, parado_no_condutor_1.pendenciaDoDono)(t, eventos, ctx.quando);
|
|
353
|
+
if (doDono) {
|
|
354
|
+
anotar(ctx, item('dono', 'pendencia-do-dono', doDono, oQue, `ork thread status ${t.id}`));
|
|
355
|
+
continue;
|
|
356
|
+
}
|
|
357
|
+
// A branch local nao pode ter trabalho que o PR nao tem: o merge o deixaria de fora, e a atualizacao o empurraria
|
|
358
|
+
// para o PR sem CHECK. Os merges da base que a batida fez e nao empurrou nao contam.
|
|
359
|
+
const alinhada = alinhamentoDaBranch(raiz, remoto, branch, pr.head, `refs/remotes/${remoto}/${base}`);
|
|
360
|
+
if (alinhada.estado === 'fora-do-pr' || alinhada.estado === 'sem-resposta') {
|
|
361
|
+
anotar(ctx, item('dono', alinhada.estado === 'sem-resposta' ? 'git-sem-resposta' : 'branch-nao-publicada', alinhada.estado === 'sem-resposta'
|
|
362
|
+
? 'o git não respondeu sobre a branch' : `a branch local tem ${alinhada.fora} commit(s) que o PR #${pr.numero} não tem`, oQue, `ork thread status ${t.id}`));
|
|
363
|
+
continue;
|
|
364
|
+
}
|
|
365
|
+
// O merge so leva o que passou pelo CHECK: o veredito PASSOU gravado depois do ultimo commit de produto do PR.
|
|
366
|
+
if (!atualizar) {
|
|
367
|
+
const check = (0, ship_publicar_1.conferirCheck)(raiz, t, `refs/remotes/${remoto}/${base}`, pr.head);
|
|
368
|
+
if (check.etapa.estado !== 'feita' && check.etapa.estado !== 'nao-se-aplica') {
|
|
369
|
+
anotar(ctx, item('dono', 'check-pendente', check.etapa.texto, undefined, `ork thread status ${t.id}`));
|
|
370
|
+
continue;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
const fora = semAutorizacao(ctx.leitura, 'merge');
|
|
374
|
+
if (fora) {
|
|
375
|
+
anotar(ctx, item('dono', fora, atualizar ? `o PR #${pr.numero} precisa da base (${pr.estado})` : `o PR #${pr.numero} está pronto`, oQue, comando));
|
|
376
|
+
continue;
|
|
377
|
+
}
|
|
378
|
+
if (atualizar) {
|
|
379
|
+
anotar(ctx, atualizacaoDoPr(ctx, t, pr, oQue, comando, alinhada.estado === 'atras' ? pr.head : null));
|
|
380
|
+
continue;
|
|
381
|
+
}
|
|
382
|
+
const r = executor(['pr', 'merge', String(pr.numero), `--repo=${forja.host}/${forja.repositorio}`, '--merge',
|
|
383
|
+
'--match-head-commit', pr.head, '--subject', assuntoDoMerge(t.id, pr)], 120000);
|
|
384
|
+
if (r.status !== 0) {
|
|
385
|
+
anotar(ctx, item('dono', 'merge-recusado', `gh pr merge saiu ${r.status ?? 'sem código'}: ${(r.stderr || r.stdout).trim().split('\n')[0]?.slice(0, 200) ?? ''}`));
|
|
386
|
+
continue;
|
|
387
|
+
}
|
|
388
|
+
// A prova: o merge `ship(<thread>)` com o head do PR como segundo pai, na copia da base trazida agora.
|
|
389
|
+
(0, varredura_acoes_1.buscarBase)(carregado);
|
|
390
|
+
const merge = (0, entrega_pr_1.mergeDaEntrega)(raiz, t.id, remoto, base);
|
|
391
|
+
if (!merge || merge.headSha !== pr.head) {
|
|
392
|
+
anotar(ctx, item('espera', 'merge-sem-prova', `gh pr merge respondeu, mas ${remoto}/${base} ainda não tem o merge ship(${t.id}) do head ` +
|
|
393
|
+
`${pr.head.slice(0, 8)} (fila de merge ou merge automático)`));
|
|
394
|
+
continue;
|
|
395
|
+
}
|
|
396
|
+
anotar(ctx, { ...item('feita', null, `PR #${pr.numero} mesclado em ${remoto}/${base}: ${merge.mergeSha.slice(0, 8)}`),
|
|
397
|
+
evidencia: `git log ${remoto}/${base}: merge ship(${t.id}) ${merge.mergeSha.slice(0, 8)} com o head ${pr.head.slice(0, 8)}` });
|
|
398
|
+
}
|
|
399
|
+
return { ok: true, detalhe: `${lidos.prs.length} PR(s) aberto(s) lidos em ${forja.host}/${forja.repositorio}` };
|
|
400
|
+
}
|
|
401
|
+
/** O PR para tras ou em conflito: o merge da base na branch, com o CHANGELOG reconciliado, e a branch empurrada. */
|
|
402
|
+
function atualizacaoDoPr(ctx, t, pr, oQue, comando, alinharCom) {
|
|
403
|
+
const base = { acao: 'merge', thread: t.id, fase: t.faseAtual, oQue, comando,
|
|
404
|
+
evidencia: `gh pr view ${pr.numero}: ${pr.estado}, mergeable ${pr.mergeable}`, desdeEm: null, pr: pr.numero };
|
|
405
|
+
let r;
|
|
406
|
+
try {
|
|
407
|
+
r = (0, atualizar_branch_1.atualizarBranchDaThread)(ctx.carregado, t.id, { buscar: false, enviar: true, canal: ctx.canal, correlacao: ctx.correlacao,
|
|
408
|
+
...(alinharCom ? { alinharCom } : {}) });
|
|
409
|
+
}
|
|
410
|
+
catch (e) {
|
|
411
|
+
return { ...base, resultado: 'dono', motivo: 'atualizacao-recusada', detalhe: e.message.slice(0, 300) };
|
|
412
|
+
}
|
|
413
|
+
switch (r.acao) {
|
|
414
|
+
case 'mesclada':
|
|
415
|
+
return { ...base, resultado: 'feita', motivo: 'branch-atualizada', detalhe: `${r.base} mesclada na ${r.branch} (${r.sha.slice(0, 8)})` +
|
|
416
|
+
(r.changelog?.devolvidos ? `, ${r.changelog.devolvidos} item(ns) da thread de volta a "Não publicado"` : '') +
|
|
417
|
+
`; o PR #${pr.numero} espera o CI do novo head` };
|
|
418
|
+
case 'ja-atualizada':
|
|
419
|
+
// O merge da base que ja estava na branch local e nao tinha chegado ao PR (push falho) foi empurrado agora.
|
|
420
|
+
if (r.enviada) {
|
|
421
|
+
return { ...base, resultado: 'feita', motivo: 'branch-atualizada', detalhe: `a ${r.branch} com ${r.base} foi empurrada (${r.sha.slice(0, 8)}); ` +
|
|
422
|
+
`o PR #${pr.numero} espera o CI do novo head` };
|
|
423
|
+
}
|
|
424
|
+
return { ...base, resultado: 'espera', motivo: 'estado-desconhecido', detalhe: `a branch já contém ${r.base}; o GitHub ainda não recalculou o PR` };
|
|
425
|
+
case 'conflito-em-codigo':
|
|
426
|
+
return { ...base, resultado: 'dono', motivo: 'conflito-em-codigo', detalhe: `o merge de ${r.base} conflita em: ${r.arquivos.join(', ').slice(0, 200)}`,
|
|
427
|
+
comando: `ork thread status ${t.id}` };
|
|
428
|
+
default:
|
|
429
|
+
return { ...base, resultado: 'dono', motivo: r.motivo === 'conduzida' || r.motivo === 'worktree-suja' ? r.motivo : 'atualizacao-recusada',
|
|
430
|
+
detalhe: r.detalhe.slice(0, 300) };
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
/** O despacho da fase seguinte e a retomada pelo retry, pelo passo do nucleo. */
|
|
434
|
+
function despacharPassos(ctx, passos, opcoes) {
|
|
435
|
+
const { carregado, raiz } = ctx;
|
|
436
|
+
for (const p of passos) {
|
|
437
|
+
if (p.conduzida || p.modo !== 'auto' || (p.acao !== 'despachar' && p.acao !== 'retomar'))
|
|
438
|
+
continue;
|
|
439
|
+
const fase = p.acao === 'despachar' ? p.fase : null;
|
|
440
|
+
const oQue = p.acao === 'despachar' ? (0, locale_1.textoDoCatalogo)((m) => m.pulse.varredura.oQue.despachar(String(fase)))
|
|
441
|
+
: (0, locale_1.textoDoCatalogo)((m) => m.pulse.varredura.oQue.retomar(p.faseAtual));
|
|
442
|
+
const comando = p.acao === 'despachar' ? `ork phase run ${p.thread} ${fase} --prompt "$(ork prompt render ${p.thread} ${fase})"`
|
|
443
|
+
: `ork retry run ${p.thread}`;
|
|
444
|
+
const item = (resultado, motivo, detalhe) => ({
|
|
445
|
+
acao: 'despachar', thread: p.thread, fase: fase ?? p.faseAtual, resultado, motivo, detalhe, oQue, comando,
|
|
446
|
+
evidencia: `passo do núcleo: ${p.porque}`.slice(0, 300), desdeEm: p.desdeEm, pr: null
|
|
447
|
+
});
|
|
448
|
+
// RM-058, integracao (T1.2, D3 e D4): a thread que um arco segura ou retem nao e despachada nem retomada por aqui.
|
|
449
|
+
if (p.arco) {
|
|
450
|
+
anotar(ctx, item('espera', p.arco.motivo, p.arco.detalhe));
|
|
451
|
+
continue;
|
|
452
|
+
}
|
|
453
|
+
if (p.acao === 'despachar' && !p.pedidoGuardado) {
|
|
454
|
+
anotar(ctx, item('dono', 'sem-prompt-gravado', 'a thread não tem o pedido guardado na criação: sem prompt gravado, nada sai'));
|
|
455
|
+
continue;
|
|
456
|
+
}
|
|
457
|
+
const fora = semAutorizacao(ctx.leitura, 'despachar');
|
|
458
|
+
// A retomada sem autorização não vira item: o pulse já leva o bloqueio da thread com o `ork retry run` (ou o
|
|
459
|
+
// `retry plan`), e um segundo item da mesma parada seria ruído no resumo do dono.
|
|
460
|
+
if (fora && p.acao === 'retomar')
|
|
461
|
+
continue;
|
|
462
|
+
if (fora) {
|
|
463
|
+
anotar(ctx, item('dono', fora, p.porque));
|
|
464
|
+
continue;
|
|
465
|
+
}
|
|
466
|
+
if (p.acao === 'despachar' && fase) {
|
|
467
|
+
let pedido = null;
|
|
468
|
+
try {
|
|
469
|
+
pedido = (0, thread_1.pedidoDaThread)(raiz, (0, thread_1.lerThread)(raiz, p.thread));
|
|
470
|
+
}
|
|
471
|
+
catch (e) {
|
|
472
|
+
anotar(ctx, item('dono', 'sem-prompt-gravado', e.message.slice(0, 200)));
|
|
473
|
+
continue;
|
|
474
|
+
}
|
|
475
|
+
if (!pedido) {
|
|
476
|
+
anotar(ctx, item('dono', 'sem-prompt-gravado', 'a thread não tem o pedido guardado na criação'));
|
|
477
|
+
continue;
|
|
478
|
+
}
|
|
479
|
+
const despachar = opcoes.despachar ?? despachoPeloNucleo;
|
|
480
|
+
const r = despachar(carregado, p.thread, fase, pedido.texto, ctx.canal, ctx.correlacao);
|
|
481
|
+
anotar(ctx, r.ok ? item('feita', null, `fase ${fase} despachada${r.sessionId ? ` (sessão ${r.sessionId.slice(0, 8)})` : ''}`)
|
|
482
|
+
: item('dono', 'despacho-recusado', r.detalhe));
|
|
483
|
+
continue;
|
|
484
|
+
}
|
|
485
|
+
const retomar = opcoes.retomar ?? retomadaPeloNucleo;
|
|
486
|
+
const r = retomar(carregado, p.thread);
|
|
487
|
+
// O plano que não é automático já é item do dono no pulse (o bloqueio, com o `retry plan`): a batida só o deixa.
|
|
488
|
+
if (!r.automatica)
|
|
489
|
+
continue;
|
|
490
|
+
anotar(ctx, r.ok ? item('feita', null, r.detalhe) : item('dono', 'retomada-recusada', r.detalhe));
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
/** O despacho pelo nucleo, como o #Noctum: o pedido guardado, o canal de quem pediu e a correlacao da batida. */
|
|
494
|
+
function despachoPeloNucleo(carregado, thread, fase, prompt, canal, correlacao) {
|
|
495
|
+
try {
|
|
496
|
+
const r = (0, phase_1.rodarFase)(carregado, thread, { fase, prompt, canal, correlacao, baselinePeloDespacho: false });
|
|
497
|
+
if (r.idempotente)
|
|
498
|
+
return { ok: true, detalhe: 'o mesmo pedido já está em andamento', sessionId: r.sessionId };
|
|
499
|
+
if (r.bloqueado || !r.sessionId) {
|
|
500
|
+
return { ok: false, detalhe: (r.recusa?.texto.split('\n')[0] ?? r.erro ?? `despacho recusado (${r.motivo ?? 'sem sessão'})`).slice(0, 300),
|
|
501
|
+
sessionId: null };
|
|
502
|
+
}
|
|
503
|
+
return { ok: true, detalhe: `sessão ${r.sessionId.slice(0, 8)}`, sessionId: r.sessionId };
|
|
504
|
+
}
|
|
505
|
+
catch (e) {
|
|
506
|
+
return { ok: false, detalhe: e.message.slice(0, 300), sessionId: null };
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
/** A retomada pelo retry do nucleo, so com o plano automatico e sem bloqueio (a mesma regra da Sentinela). */
|
|
510
|
+
function retomadaPeloNucleo(carregado, thread) {
|
|
511
|
+
try {
|
|
512
|
+
const plano = (0, retry_1.planejarRetry)(carregado, thread);
|
|
513
|
+
if (!plano.automatica || plano.bloqueio)
|
|
514
|
+
return { automatica: false, ok: false, detalhe: `o retry plan não é automático: ${plano.razao}`.slice(0, 300) };
|
|
515
|
+
const r = (0, retry_1.executarRetry)(carregado, thread);
|
|
516
|
+
return { automatica: true, ok: r.executada, detalhe: r.detalhe.slice(0, 300) };
|
|
517
|
+
}
|
|
518
|
+
catch (e) {
|
|
519
|
+
return { automatica: true, ok: false, detalhe: e.message.slice(0, 300) };
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
/** As acoes da varredura da F5, no formato da batida. */
|
|
523
|
+
function daVarredura(v, ctx) {
|
|
524
|
+
for (const f of v.feitas) {
|
|
525
|
+
const item = { acao: f.acao, thread: f.thread, fase: null, resultado: f.resultado === 'feita' ? 'feita' : 'dono',
|
|
526
|
+
motivo: f.resultado === 'feita' ? null : 'acao-recusada', oQue: f.acao, detalhe: f.detalhe, comando: '', evidencia: '', desdeEm: null, pr: null };
|
|
527
|
+
if (item.resultado === 'feita')
|
|
528
|
+
ctx.feitas.push(item);
|
|
529
|
+
}
|
|
530
|
+
for (const p of v.pendentes) {
|
|
531
|
+
// RM-058, integracao (T1.2): a acao da thread que um arco segura espera o arco, e nao o dono.
|
|
532
|
+
if (p.motivoDoBloqueio === 'thread-do-arco') {
|
|
533
|
+
ctx.pendentes.push({ acao: p.acao, thread: p.thread, fase: p.fase, resultado: 'espera',
|
|
534
|
+
motivo: p.bloqueio?.startsWith(noctum_guarda_1.MOTIVO_DO_ARCO_ILEGIVEL) ? noctum_guarda_1.MOTIVO_DO_ARCO_ILEGIVEL : noctum_guarda_1.MOTIVO_DA_GUARDA, oQue: p.oQue,
|
|
535
|
+
detalhe: p.bloqueio ?? p.oQue, comando: `ork thread status ${p.thread}`, evidencia: p.evidencia, desdeEm: p.desdeEm, pr: null });
|
|
536
|
+
continue;
|
|
537
|
+
}
|
|
538
|
+
ctx.pendentes.push({ acao: p.acao, thread: p.thread, fase: p.fase, resultado: 'dono',
|
|
539
|
+
motivo: p.motivoDoBloqueio ?? (p.recusa ? 'acao-recusada' : semAutorizacao(ctx.leitura, p.acao)),
|
|
540
|
+
oQue: p.oQue, detalhe: p.recusa?.detalhe ?? p.bloqueio ?? p.oQue, comando: p.bloqueio ? `ork thread status ${p.thread}` : p.comando,
|
|
541
|
+
evidencia: p.evidencia, desdeEm: p.desdeEm, pr: null });
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
/**
|
|
545
|
+
* A batida da conducao: traz a base, mescla os PRs prontos, roda a varredura da F5 (limpar, registrar, master) e
|
|
546
|
+
* despacha a fase seguinte. Le a autorizacao uma vez; o que ela nao cobre fica pendente, com o motivo e o comando.
|
|
547
|
+
*/
|
|
548
|
+
function conduzirBatida(carregado, opcoes = {}) {
|
|
549
|
+
const raiz = (0, estado_thread_1.raizDoEstado)(carregado.raiz);
|
|
550
|
+
const doProjeto = { ...carregado, raiz };
|
|
551
|
+
const quando = opcoes.quando ?? new Date().toISOString();
|
|
552
|
+
const maquina = (0, maquina_1.nomeDaMaquina)();
|
|
553
|
+
const leitura = opcoes.autorizacao ?? (0, autorizacao_duravel_1.lerAutorizacao)(raiz, { projeto: carregado.manifesto.project.name, agora: new Date(quando) });
|
|
554
|
+
const busca = opcoes.buscar === false ? null : (0, varredura_acoes_1.buscarBase)(doProjeto);
|
|
555
|
+
const sessoes = (0, varredura_acoes_1.lerSessoes)(raiz, opcoes);
|
|
556
|
+
const ctx = { carregado: doProjeto, raiz, quando, leitura, vivas: sessoes.vivas, canal: opcoes.canal ?? (0, conducao_1.canalDoProcesso)(),
|
|
557
|
+
correlacao: `batida:${maquina}`, arcos: (0, noctum_guarda_1.lerArcosDaRaiz)(raiz, quando), feitas: [], pendentes: [] };
|
|
558
|
+
const threads = threadsAbertas(raiz);
|
|
559
|
+
// RM-064 T9: um watcher que falhou no despacho não pode ocultar o fim nativo da sessão.
|
|
560
|
+
// A reconciliação é local e fail-closed; a rodada seguinte deriva retomar ou pendência do dono.
|
|
561
|
+
for (const thread of threads) {
|
|
562
|
+
try {
|
|
563
|
+
(0, session_watcher_1.reconciliarSessaoSemWatcher)(doProjeto, thread.id, Date.parse(quando));
|
|
564
|
+
}
|
|
565
|
+
catch { /* o diagnóstico da observação fica no ledger; uma thread não interrompe as outras */ }
|
|
566
|
+
}
|
|
567
|
+
// 1. O merge dos PRs prontos: o que entra na base agora e registrado pela varredura nesta mesma batida.
|
|
568
|
+
const forja = mesclarProntos(ctx, threads, opcoes);
|
|
569
|
+
// 2. A varredura da F5, depois do merge e sobre a mesma leitura das sessoes.
|
|
570
|
+
const mesclou = ctx.feitas.some((f) => f.acao === 'merge' && f.motivo === null);
|
|
571
|
+
const v = (0, varredura_acoes_1.agirNaVarredura)(doProjeto, { ...opcoes, quando, autorizacao: leitura, buscar: mesclou,
|
|
572
|
+
fantasmas: () => sessoes.fantasmas, vivas: () => sessoes.vivas });
|
|
573
|
+
daVarredura(v, ctx);
|
|
574
|
+
// 3. O despacho comum: com a Sentinela ligada nesta maquina, ela conduz. Cota sempre fica com a batida.
|
|
575
|
+
let sentinela = false;
|
|
576
|
+
try {
|
|
577
|
+
sentinela = (0, conducao_sentinela_1.presencaDaSentinela)(raiz, { agora: new Date(quando), maquina })?.ativa === true;
|
|
578
|
+
}
|
|
579
|
+
catch {
|
|
580
|
+
sentinela = false;
|
|
581
|
+
}
|
|
582
|
+
let passos = [];
|
|
583
|
+
try {
|
|
584
|
+
passos = (0, conducao_sentinela_1.passosDoCondutor)(doProjeto, { quando });
|
|
585
|
+
}
|
|
586
|
+
catch {
|
|
587
|
+
passos = [];
|
|
588
|
+
}
|
|
589
|
+
despacharPassos(ctx, passosDaBatida(passos, sentinela), opcoes);
|
|
590
|
+
const a = leitura.valida ? leitura.autorizacao : null;
|
|
591
|
+
return {
|
|
592
|
+
contrato: exports.CONTRATO_DA_BATIDA, consultadoEm: quando, maquina,
|
|
593
|
+
autorizacao: { valida: leitura.valida, motivo: leitura.valida ? null : leitura.motivo, detalhe: leitura.valida ? null : leitura.detalhe,
|
|
594
|
+
por: a?.por ?? null, ate: a?.ate ?? null, pode: a ? [...a.pode] : [] },
|
|
595
|
+
busca, forja, sentinela, feitas: ctx.feitas, pendentes: ctx.pendentes,
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
// ---------------------------------------------------------------------------
|
|
599
|
+
// O retrato da ultima batida, a trava e o registro da rodada.
|
|
600
|
+
// ---------------------------------------------------------------------------
|
|
601
|
+
function caminhoDoRetrato(raiz) {
|
|
602
|
+
return path.join((0, estado_thread_1.raizDoEstado)(raiz), '.orkastery', 'monitor', exports.ARQUIVO_DA_BATIDA);
|
|
603
|
+
}
|
|
604
|
+
function lerRetratoDaBatida(raiz) {
|
|
605
|
+
try {
|
|
606
|
+
const r = JSON.parse(fs.readFileSync(caminhoDoRetrato(raiz), 'utf8'));
|
|
607
|
+
return r && r.contrato === exports.CONTRATO_DA_BATIDA && Array.isArray(r.pendentes) && Array.isArray(r.feitas) ? r : null;
|
|
608
|
+
}
|
|
609
|
+
catch {
|
|
610
|
+
return null;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
/**
|
|
614
|
+
* Sem a leitura dos PRs nesta rodada (o `gh` fora, a rede caida), os merges pendentes da rodada anterior continuam
|
|
615
|
+
* pendentes, com o `desdeEm` deles: a falha passageira nao apaga a fila do dono e nao reabre a mesma noticia depois.
|
|
616
|
+
*/
|
|
617
|
+
function comMergesDaRodadaAnterior(nova, anterior, raiz) {
|
|
618
|
+
if (!anterior || !nova.forja || nova.forja.ok)
|
|
619
|
+
return nova;
|
|
620
|
+
const jaTem = new Set(nova.pendentes.filter((i) => i.acao === 'merge').map((i) => i.thread));
|
|
621
|
+
const herdados = anterior.pendentes.filter((i) => i.acao === 'merge' && !jaTem.has(i.thread)).filter((i) => {
|
|
622
|
+
try {
|
|
623
|
+
return (0, thread_1.lerThread)(raiz, i.thread).status !== 'fechada';
|
|
624
|
+
}
|
|
625
|
+
catch {
|
|
626
|
+
return false;
|
|
627
|
+
}
|
|
628
|
+
});
|
|
629
|
+
return herdados.length ? { ...nova, pendentes: [...nova.pendentes, ...herdados] } : nova;
|
|
630
|
+
}
|
|
631
|
+
/** O `desdeEm` de cada pendente fica o da primeira batida que o viu: o resumo ao dono nao repete a mesma noticia. */
|
|
632
|
+
function comDesdeEstavel(nova, anterior) {
|
|
633
|
+
const chave = (i) => [i.acao, i.thread, i.motivo, i.pr ?? ''].join('|');
|
|
634
|
+
const antes = new Map((anterior?.pendentes ?? []).map((i) => [chave(i), i.desdeEm]));
|
|
635
|
+
return { ...nova, pendentes: nova.pendentes.map((i) => ({ ...i, desdeEm: i.desdeEm ?? antes.get(chave(i)) ?? nova.consultadoEm })) };
|
|
636
|
+
}
|
|
637
|
+
function gravarRetrato(raiz, r) {
|
|
638
|
+
const arquivo = caminhoDoRetrato(raiz);
|
|
639
|
+
fs.mkdirSync(path.dirname(arquivo), { recursive: true });
|
|
640
|
+
const temporario = `${arquivo}.${process.pid}.${(0, node_crypto_1.randomUUID)().slice(0, 8)}.tmp`;
|
|
641
|
+
try {
|
|
642
|
+
fs.writeFileSync(temporario, JSON.stringify(r, null, 2) + '\n', { encoding: 'utf8', mode: 0o600, flag: 'wx' });
|
|
643
|
+
fs.renameSync(temporario, arquivo);
|
|
644
|
+
}
|
|
645
|
+
finally {
|
|
646
|
+
fs.rmSync(temporario, { force: true });
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
/**
|
|
650
|
+
* A batida inteira, como o cron e o `ork conducao batida` a rodam: uma por vez nesta maquina (a mesma trava da varredura
|
|
651
|
+
* que age, `varredura-acoes.lock`), o retrato gravado para o pulse e a linha `varredura_acoes` no `hitl.log`.
|
|
652
|
+
*/
|
|
653
|
+
function baterConducao(carregado, opcoes = {}) {
|
|
654
|
+
const raiz = (0, estado_thread_1.raizDoEstado)(carregado.raiz);
|
|
655
|
+
const dir = path.join(raiz, '.orkastery', 'monitor');
|
|
656
|
+
const quando = opcoes.quando ?? new Date().toISOString();
|
|
657
|
+
const log = (linha) => {
|
|
658
|
+
try {
|
|
659
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
660
|
+
fs.appendFileSync(path.join(dir, 'hitl.log'), JSON.stringify({ ts: quando, tipo: 'varredura_acoes', ...linha }) + '\n', { mode: 0o600 });
|
|
661
|
+
}
|
|
662
|
+
catch { /* o log e da batida; o ledger da thread guarda cada acao */ }
|
|
663
|
+
};
|
|
664
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
665
|
+
const trava = (0, monitor_lock_1.adquirirLockMonitor)(path.join(dir, 'varredura-acoes.lock'));
|
|
666
|
+
if (!trava.ok) {
|
|
667
|
+
log({ ocupada: true, detalhe: trava.detalhe });
|
|
668
|
+
return { ocupada: true, detalhe: trava.detalhe };
|
|
669
|
+
}
|
|
670
|
+
let r;
|
|
671
|
+
try {
|
|
672
|
+
const anterior = lerRetratoDaBatida(raiz);
|
|
673
|
+
r = comDesdeEstavel(comMergesDaRodadaAnterior(conduzirBatida(carregado, { ...opcoes, quando }), anterior, raiz), anterior);
|
|
674
|
+
try {
|
|
675
|
+
gravarRetrato(raiz, r);
|
|
676
|
+
}
|
|
677
|
+
catch { /* sem retrato, o pulse so nao mostra os pendentes do merge e do despacho */ }
|
|
678
|
+
}
|
|
679
|
+
catch (e) {
|
|
680
|
+
log({ erro: e.message.slice(0, 300) });
|
|
681
|
+
throw e;
|
|
682
|
+
}
|
|
683
|
+
finally {
|
|
684
|
+
trava.liberar();
|
|
685
|
+
}
|
|
686
|
+
log({ autorizacao: r.autorizacao.valida ? 'valida' : r.autorizacao.motivo, busca: r.busca, forja: r.forja, sentinela: r.sentinela,
|
|
687
|
+
feitas: r.feitas.map((f) => ({ acao: f.acao, thread: f.thread, ...(f.pr ? { pr: f.pr } : {}) })),
|
|
688
|
+
pendentes: r.pendentes.map((p) => ({ acao: p.acao, thread: p.thread, resultado: p.resultado, motivo: p.motivo })) });
|
|
689
|
+
return { ocupada: false, ...r };
|
|
690
|
+
}
|
|
691
|
+
/**
|
|
692
|
+
* RM-063 (F7): o que a ultima batida deixou pendente no merge e no despacho, no formato das acoes da varredura, para o
|
|
693
|
+
* pulse levar a fila. So o retrato fresco (`VALIDADE_DO_RETRATO_MS`) conta; o pendente de thread ja fechada sai.
|
|
694
|
+
*/
|
|
695
|
+
function pendentesDaUltimaBatida(raiz, quando) {
|
|
696
|
+
const r = lerRetratoDaBatida(raiz);
|
|
697
|
+
if (!r || !(Date.parse(quando) - Date.parse(r.consultadoEm) <= exports.VALIDADE_DO_RETRATO_MS))
|
|
698
|
+
return [];
|
|
699
|
+
const saida = [];
|
|
700
|
+
for (const i of r.pendentes) {
|
|
701
|
+
if (i.acao !== 'merge' && i.acao !== 'despachar')
|
|
702
|
+
continue;
|
|
703
|
+
// RM-058, integracao (T1.2): o pulo da thread de um arco #Noctum fica no retrato; quem diz dela ao dono e o relatorio do
|
|
704
|
+
// arco. O arco que nao se le nao tem relatorio (A6 do CHECK): o pulo dele vai a fila do dono, com o texto da guarda.
|
|
705
|
+
if (i.motivo === noctum_guarda_1.MOTIVO_DA_GUARDA)
|
|
706
|
+
continue;
|
|
707
|
+
const ilegivel = i.motivo === noctum_guarda_1.MOTIVO_DO_ARCO_ILEGIVEL;
|
|
708
|
+
try {
|
|
709
|
+
if ((0, thread_1.lerThread)((0, estado_thread_1.raizDoEstado)(raiz), i.thread).status === 'fechada')
|
|
710
|
+
continue;
|
|
711
|
+
}
|
|
712
|
+
catch {
|
|
713
|
+
continue;
|
|
714
|
+
}
|
|
715
|
+
// A espera que passa de 2 h (check obrigatorio parado, o GitHub sem o estado do merge) vira item do dono. O arco que nao
|
|
716
|
+
// se le fica com o texto da guarda mesmo depois de 2 h (N3 do CHECK 2): a espera longa diria outra causa.
|
|
717
|
+
const longa = i.resultado === 'espera' && !!i.desdeEm && Date.parse(quando) - Date.parse(i.desdeEm) > exports.ESPERA_LONGA_MS;
|
|
718
|
+
const motivo = ilegivel ? 'thread-do-arco' : longa ? 'espera-longa' : i.motivo;
|
|
719
|
+
const bloqueio = ilegivel ? i.detalhe
|
|
720
|
+
: ehBloqueio(motivo) ? (0, locale_1.textoDoCatalogo)((m) => m.pulse.varredura.bloqueios[motivo]) : null;
|
|
721
|
+
const recusa = i.motivo && /-recusad[oa]$/.test(i.motivo) ? { em: r.consultadoEm, detalhe: i.detalhe } : null;
|
|
722
|
+
saida.push({ acao: i.acao, thread: i.thread, fase: i.fase, sessionId: null, desdeEm: i.desdeEm, comando: i.comando, oQue: i.oQue,
|
|
723
|
+
evidencia: i.evidencia, bloqueio, motivoDoBloqueio: bloqueio ? motivo : null, recusa });
|
|
724
|
+
}
|
|
725
|
+
return saida;
|
|
726
|
+
}
|
|
727
|
+
function ehBloqueio(motivo) {
|
|
728
|
+
return !!motivo && varredura_acoes_1.MOTIVOS_DO_BLOQUEIO.includes(motivo);
|
|
729
|
+
}
|
|
730
|
+
/** O texto do `ork conducao batida` e da resposta ao `#OrkRetomar`: o que voltou a andar e o que ficou com o dono. */
|
|
731
|
+
function textoDaBatida(r) {
|
|
732
|
+
const m = (0, locale_1.msg)().conducao.batida;
|
|
733
|
+
if (r.ocupada)
|
|
734
|
+
return m.ocupada(r.detalhe);
|
|
735
|
+
const linhas = [];
|
|
736
|
+
const a = r.autorizacao;
|
|
737
|
+
linhas.push(a.valida ? m.autorizada(a.por ?? '-', a.ate ?? '-', a.pode.join(', ')) : m.semAutorizacao(a.motivo ?? '-'));
|
|
738
|
+
if (r.sentinela)
|
|
739
|
+
linhas.push(m.sentinela);
|
|
740
|
+
if (r.forja && !r.forja.ok)
|
|
741
|
+
linhas.push(m.prs(r.forja.detalhe));
|
|
742
|
+
if (r.busca && !r.busca.ok)
|
|
743
|
+
linhas.push(m.base(r.busca.detalhe));
|
|
744
|
+
const andou = r.feitas;
|
|
745
|
+
linhas.push(andou.length ? m.voltou(andou.length) : m.nada);
|
|
746
|
+
for (const f of andou)
|
|
747
|
+
linhas.push(m.item(f.thread, f.acao, f.pr ?? null, f.detalhe));
|
|
748
|
+
const espera = r.pendentes.filter((p) => p.resultado === 'espera');
|
|
749
|
+
if (espera.length)
|
|
750
|
+
linhas.push(m.espera(espera.length));
|
|
751
|
+
for (const p of espera)
|
|
752
|
+
linhas.push(m.item(p.thread, p.acao, p.pr ?? null, p.detalhe));
|
|
753
|
+
const dono = r.pendentes.filter((p) => p.resultado === 'dono');
|
|
754
|
+
if (dono.length)
|
|
755
|
+
linhas.push(m.comVoce(dono.length));
|
|
756
|
+
for (const p of dono)
|
|
757
|
+
linhas.push(m.itemDono(p.thread, p.acao, p.pr ?? null, p.motivo ?? '-', p.detalhe, p.comando));
|
|
758
|
+
return linhas.join('\n');
|
|
759
|
+
}
|