@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,789 @@
|
|
|
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.CHAVES_DOS_LIMIARES = exports.FALHAS_DO_M5 = exports.RecusaDoAceite = exports.ARQUIVO_DO_ACEITE = exports.MAQUINA_DO_ENSAIO = exports.CONTRATO_LIMIARES = exports.CONTRATO_PILOTO = void 0;
|
|
37
|
+
exports.gitDaRaiz = gitDaRaiz;
|
|
38
|
+
exports.remotoLocal = remotoLocal;
|
|
39
|
+
exports.limiaresDaProposta = limiaresDaProposta;
|
|
40
|
+
exports.lerLimiares = lerLimiares;
|
|
41
|
+
exports.lerInjecoes = lerInjecoes;
|
|
42
|
+
exports.aceiteDoPiloto = aceiteDoPiloto;
|
|
43
|
+
exports.hashDoAceite = hashDoAceite;
|
|
44
|
+
exports.gravarAceite = gravarAceite;
|
|
45
|
+
exports.publicavel = publicavel;
|
|
46
|
+
exports.sensiveisDoArco = sensiveisDoArco;
|
|
47
|
+
/**
|
|
48
|
+
* RM-058, integração (T8 da ork-rm058integra, D12): o aceite do piloto. `aceiteDoPiloto` lê o log do arco, a supervisão, o
|
|
49
|
+
* registro do relatório, os ledgers das threads do arco, o remoto (a ponta da base pelo `git ls-remote` e a ancestralidade
|
|
50
|
+
* de cada merge) e a forja (os PRs da branch, quando o remoto é do GitHub), e dá o contrato `ork.noctum-piloto/v1`: as
|
|
51
|
+
* medidas M1 a M10 (valor, unidade, fonte e lacunas), os critérios A1 a A8 e I1 a I6 com o limiar e a origem dele (`dono`
|
|
52
|
+
* ou `proposta`), e o veredito (`aprovado`, `reprovado`, `incompleto` ou `simulado`).
|
|
53
|
+
*
|
|
54
|
+
* Medida que não se lê é lacuna, nunca zero, e o critério com lacuna fica incompleto. Qualquer marca de simulação (a máquina
|
|
55
|
+
* do ensaio, o remoto de fixture, evento marcado `simulado`, o canal que grava num arquivo local, o despacho cuja sessão o
|
|
56
|
+
* runtime não confirmou) dá `simulado`, e o veredito
|
|
57
|
+
* nunca é `aprovado`: o ensaio não vale como piloto. O resultado não traz o instante de quem roda: o mesmo arco dá o mesmo
|
|
58
|
+
* hash. `publicavel` troca caminho absoluto, e-mail, id de sessão, conta, máquina e o dono do repositório por rótulos
|
|
59
|
+
* estáveis; o arquivo completo fica no estado, ao lado do log do arco.
|
|
60
|
+
*/
|
|
61
|
+
const fs = __importStar(require("node:fs"));
|
|
62
|
+
const path = __importStar(require("node:path"));
|
|
63
|
+
const node_crypto_1 = require("node:crypto");
|
|
64
|
+
const branch_de_estado_1 = require("./branch-de-estado");
|
|
65
|
+
const ci_1 = require("./ci");
|
|
66
|
+
const horario_1 = require("./horario");
|
|
67
|
+
const ledger_1 = require("./ledger");
|
|
68
|
+
const master_1 = require("./master");
|
|
69
|
+
const noctum_arco_1 = require("./noctum-arco");
|
|
70
|
+
const noctum_envelope_1 = require("./noctum-envelope");
|
|
71
|
+
const noctum_fila_1 = require("./noctum-fila");
|
|
72
|
+
const noctum_medicao_1 = require("./noctum-medicao");
|
|
73
|
+
const noctum_registro_1 = require("./noctum-registro");
|
|
74
|
+
const noctum_relatorio_1 = require("./noctum-relatorio");
|
|
75
|
+
const noctum_supervisao_1 = require("./noctum-supervisao");
|
|
76
|
+
const parado_no_condutor_1 = require("./parado-no-condutor");
|
|
77
|
+
const estado_thread_1 = require("./estado-thread");
|
|
78
|
+
const thread_1 = require("./thread");
|
|
79
|
+
const util_1 = require("./util");
|
|
80
|
+
const worktree_1 = require("./worktree");
|
|
81
|
+
exports.CONTRATO_PILOTO = 'ork.noctum-piloto/v1';
|
|
82
|
+
exports.CONTRATO_LIMIARES = 'ork.noctum-limiares/v1';
|
|
83
|
+
/** A máquina do ensaio (`core/scripts/ensaio-noctum.cjs`): o arco que ela conduziu é simulado. */
|
|
84
|
+
exports.MAQUINA_DO_ENSAIO = 'maquina-do-ensaio';
|
|
85
|
+
/** O arquivo completo do aceite, no estado, ao lado do log do arco. */
|
|
86
|
+
exports.ARQUIVO_DO_ACEITE = 'aceite-piloto.json';
|
|
87
|
+
class RecusaDoAceite extends Error {
|
|
88
|
+
motivo;
|
|
89
|
+
constructor(motivo, detalhe) {
|
|
90
|
+
super(`${motivo}: ${detalhe}`);
|
|
91
|
+
this.motivo = motivo;
|
|
92
|
+
this.name = 'RecusaDoAceite';
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
exports.RecusaDoAceite = RecusaDoAceite;
|
|
96
|
+
const HORA = 3_600_000;
|
|
97
|
+
/** As falhas que o plano mede pelo tempo de recuperação (M5): a sessão morta, o condutor morto e a máquina perdida. */
|
|
98
|
+
exports.FALHAS_DO_M5 = ['F1', 'F2', 'F11'];
|
|
99
|
+
const MIN = 60_000;
|
|
100
|
+
const arred = (x, casas = 2) => Math.round(x * 10 ** casas) / 10 ** casas;
|
|
101
|
+
const sha256 = (texto) => (0, node_crypto_1.createHash)('sha256').update(texto).digest('hex');
|
|
102
|
+
const objeto = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
103
|
+
/** O git da raiz: a URL do remoto, a ponta da base pelo `ls-remote` e a ancestralidade pelo `merge-base`. */
|
|
104
|
+
function gitDaRaiz(raiz) {
|
|
105
|
+
return {
|
|
106
|
+
url: (remoto) => { const r = (0, util_1.exec)('git', ['remote', 'get-url', '--', remoto], raiz, 15_000); return r.ok ? r.stdout.trim() : null; },
|
|
107
|
+
ponta: (remoto, base) => {
|
|
108
|
+
const r = (0, util_1.exec)('git', ['ls-remote', '--', remoto, `refs/heads/${base}`], raiz, 60_000);
|
|
109
|
+
return r.ok ? r.stdout.trim().split(/\s+/)[0] || null : null;
|
|
110
|
+
},
|
|
111
|
+
ancestral: (sha, ponta) => {
|
|
112
|
+
const r = (0, util_1.exec)('git', ['merge-base', '--is-ancestor', sha, ponta], raiz, 30_000);
|
|
113
|
+
return r.code === 0 ? true : r.code === 1 ? false : null;
|
|
114
|
+
},
|
|
115
|
+
reversoes: (sha, ponta) => {
|
|
116
|
+
// O sha vem do ledger: só hexadecimal chega ao `git log`, como na detecção do `ship`.
|
|
117
|
+
if (!(0, branch_de_estado_1.shaValido)(sha) || !(0, branch_de_estado_1.shaValido)(ponta))
|
|
118
|
+
return null;
|
|
119
|
+
const r = (0, util_1.exec)('git', ['log', '--fixed-strings', `--grep=This reverts commit ${sha}`, '--format=%H%x1f%cI%x1e', '--end-of-options', `${sha}..${ponta}`], raiz, 60_000);
|
|
120
|
+
if (!r.ok)
|
|
121
|
+
return null;
|
|
122
|
+
return r.stdout.split('\x1e').map(x => x.trim().split('\x1f')).filter(([h, d]) => h && d).map(([h, d]) => ({ sha: h, em: d }));
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
/** O remoto que não é um endereço de rede: caminho local ou `file://`. É o remoto de fixture do ensaio. */
|
|
127
|
+
function remotoLocal(url) {
|
|
128
|
+
return /^file:/i.test(url) || /^(\/|\.{1,2}\/|[A-Za-z]:[\\/])/.test(url) || (!/^[a-z][a-z0-9+.-]*:\/\//i.test(url) && !/^[^@\s]+@[^:\s]+:/.test(url));
|
|
129
|
+
}
|
|
130
|
+
/** Os limiares da proposta, pela janela do piloto. */
|
|
131
|
+
function limiaresDaProposta(horas) {
|
|
132
|
+
return { m1MinimoHoras: arred(horas), m5MaximoMin: 15, entregasMinimas: horas < 12 ? 2 : 4, margemDoTeto: 0, atrasoDoRelatorioMin: noctum_fila_1.CADENCIA_MS / MIN };
|
|
133
|
+
}
|
|
134
|
+
/** As chaves dos limiares do dono, no contrato `ork.noctum-limiares/v1`. */
|
|
135
|
+
exports.CHAVES_DOS_LIMIARES = ['m1MinimoHoras', 'm5MaximoMin', 'entregasMinimas', 'margemDoTeto', 'atrasoDoRelatorioMin'];
|
|
136
|
+
/** Confere os limiares do dono (`--limiares`): o contrato, só as chaves conhecidas e números não negativos; o que falta fica com a proposta. */
|
|
137
|
+
function lerLimiares(bruto) {
|
|
138
|
+
if (!objeto(bruto) || bruto.contrato !== exports.CONTRATO_LIMIARES) {
|
|
139
|
+
throw new RecusaDoAceite('noctum.aceite.limiares-invalidos', `os limiares do dono são um JSON com contrato ${exports.CONTRATO_LIMIARES}`);
|
|
140
|
+
}
|
|
141
|
+
const saida = {};
|
|
142
|
+
// M8 do CHECK 2: a chave com erro de digitação não some em silêncio, deixando o critério com a proposta.
|
|
143
|
+
const desconhecidas = Object.keys(bruto).filter(k => k !== 'contrato' && !exports.CHAVES_DOS_LIMIARES.includes(k));
|
|
144
|
+
if (desconhecidas.length) {
|
|
145
|
+
throw new RecusaDoAceite('noctum.aceite.limiares-invalidos', `chave(s) desconhecida(s): ${desconhecidas.join(', ')}; as válidas: ${exports.CHAVES_DOS_LIMIARES.join(', ')}`);
|
|
146
|
+
}
|
|
147
|
+
for (const chave of exports.CHAVES_DOS_LIMIARES) {
|
|
148
|
+
const v = bruto[chave];
|
|
149
|
+
if (v === undefined)
|
|
150
|
+
continue;
|
|
151
|
+
if (typeof v !== 'number' || !Number.isFinite(v) || v < 0)
|
|
152
|
+
throw new RecusaDoAceite('noctum.aceite.limiares-invalidos', `${chave} não é um número não negativo`);
|
|
153
|
+
saida[chave] = v;
|
|
154
|
+
}
|
|
155
|
+
return saida;
|
|
156
|
+
}
|
|
157
|
+
/** Lê a agenda de injeções: uma linha JSON por falha, com `falha` (F1 a F11) e `instante` (ISO). */
|
|
158
|
+
function lerInjecoes(texto) {
|
|
159
|
+
const saida = [];
|
|
160
|
+
for (const [i, linha] of texto.split('\n').entries()) {
|
|
161
|
+
if (!linha.trim())
|
|
162
|
+
continue;
|
|
163
|
+
let v;
|
|
164
|
+
try {
|
|
165
|
+
v = JSON.parse(linha);
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
throw new RecusaDoAceite('noctum.aceite.injecoes-invalidas', `a linha ${i + 1} da agenda não é JSON`);
|
|
169
|
+
}
|
|
170
|
+
if (!objeto(v) || typeof v.falha !== 'string' || !/^F([1-9]|1[01])$/.test(v.falha) || typeof v.instante !== 'string' || !Number.isFinite(Date.parse(v.instante))) {
|
|
171
|
+
throw new RecusaDoAceite('noctum.aceite.injecoes-invalidas', `a linha ${i + 1} da agenda precisa de falha (F1 a F11) e instante (ISO)`);
|
|
172
|
+
}
|
|
173
|
+
// N7 do CHECK 3: a chave com erro de digitação ("Alvo") ou o alvo que não é texto não viram injeção sem alvo em silêncio.
|
|
174
|
+
const desconhecidas = Object.keys(v).filter(k => !['falha', 'instante', 'alvo'].includes(k));
|
|
175
|
+
// M4 do CHECK 4: o alvo vazio também, que valeria como o arco inteiro.
|
|
176
|
+
if (desconhecidas.length || (v.alvo !== undefined && (typeof v.alvo !== 'string' || !v.alvo.trim()))) {
|
|
177
|
+
throw new RecusaDoAceite('noctum.aceite.injecoes-invalidas', `a linha ${i + 1} da agenda só aceita falha, instante e alvo (texto)` +
|
|
178
|
+
`${desconhecidas.length ? `; chave(s) desconhecida(s): ${desconhecidas.join(', ')}` : ''}`);
|
|
179
|
+
}
|
|
180
|
+
saida.push({ falha: v.falha, instante: new Date(v.instante).toISOString(), ...(typeof v.alvo === 'string' ? { alvo: v.alvo } : {}) });
|
|
181
|
+
}
|
|
182
|
+
return saida;
|
|
183
|
+
}
|
|
184
|
+
/** O primeiro instante, depois da injeção, de um evento que a falha deve provocar; e o evento, como fonte. */
|
|
185
|
+
function sinalDe(eventos, desde) {
|
|
186
|
+
return eventos.filter(e => Date.parse(e.em) >= desde).sort((a, b) => Date.parse(a.em) - Date.parse(b.em))[0] ?? null;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* O aceite de um arco, pelos registros do núcleo. Recusa só o arco que não existe; toda leitura que falha vira lacuna da
|
|
190
|
+
* medida que dependia dela.
|
|
191
|
+
*/
|
|
192
|
+
function aceiteDoPiloto(carregado, arco, opcoes = {}) {
|
|
193
|
+
const { raiz } = carregado;
|
|
194
|
+
let estado;
|
|
195
|
+
try {
|
|
196
|
+
estado = (0, noctum_arco_1.lerArco)(raiz, arco);
|
|
197
|
+
}
|
|
198
|
+
catch (e) {
|
|
199
|
+
if (e instanceof noctum_envelope_1.RecusaDoArco)
|
|
200
|
+
throw new RecusaDoAceite('noctum.aceite.sem-arco', `o log do arco ${arco} não se lê (${e.motivo})`);
|
|
201
|
+
throw e;
|
|
202
|
+
}
|
|
203
|
+
if (!estado?.conteudo || !estado.autorizadoPor)
|
|
204
|
+
throw new RecusaDoAceite('noctum.aceite.sem-arco', `o arco ${arco} nunca foi autorizado pelo dono`);
|
|
205
|
+
const conteudo = estado.conteudo;
|
|
206
|
+
const janela = conteudo.janela;
|
|
207
|
+
// A condução começa no início da janela ou na primeira autorização, o que vier depois; as autorizações seguintes, dentro
|
|
208
|
+
// da janela, são emendas do dono.
|
|
209
|
+
let autorizacoes = [];
|
|
210
|
+
try {
|
|
211
|
+
autorizacoes = (0, noctum_arco_1.versoesAutorizadas)(raiz, arco).map(v => ({ versao: v.versao, autorizadoEm: v.autorizadoEm }));
|
|
212
|
+
}
|
|
213
|
+
catch (e) {
|
|
214
|
+
if (!(e instanceof noctum_envelope_1.RecusaDoArco))
|
|
215
|
+
throw e;
|
|
216
|
+
}
|
|
217
|
+
const primeiraAutorizacao = autorizacoes[0]?.autorizadoEm ?? estado.autorizadoPor.recebidoEm;
|
|
218
|
+
const inicioMs = Math.max(Date.parse(janela.inicio), Date.parse(primeiraAutorizacao));
|
|
219
|
+
const fim = estado.encerramento?.em ?? janela.fim;
|
|
220
|
+
const fimMs = Date.parse(fim);
|
|
221
|
+
const horasDaJanela = (Date.parse(janela.fim) - Date.parse(janela.inicio)) / HORA;
|
|
222
|
+
const git = opcoes.git ?? gitDaRaiz(raiz);
|
|
223
|
+
const remoto = opcoes.remoto ?? carregado.manifesto.fabrica?.remoto ?? 'origin';
|
|
224
|
+
// As leituras: a supervisão, o relatório e os ledgers das threads do arco.
|
|
225
|
+
let supervisao = null;
|
|
226
|
+
let erroDaSupervisao = null;
|
|
227
|
+
try {
|
|
228
|
+
supervisao = (0, noctum_supervisao_1.eventosDaSupervisao)(raiz, arco);
|
|
229
|
+
}
|
|
230
|
+
catch (e) {
|
|
231
|
+
if (!(e instanceof noctum_supervisao_1.RecusaDaSupervisao))
|
|
232
|
+
throw e;
|
|
233
|
+
erroDaSupervisao = `o log da supervisão não se lê (${e.motivo})`;
|
|
234
|
+
}
|
|
235
|
+
let relatorio = null;
|
|
236
|
+
let erroDoRelatorio = null;
|
|
237
|
+
try {
|
|
238
|
+
relatorio = (0, noctum_registro_1.lerRegistro)(raiz, arco, 'relatorio');
|
|
239
|
+
}
|
|
240
|
+
catch (e) {
|
|
241
|
+
if (!(e instanceof noctum_registro_1.RecusaDoRegistro))
|
|
242
|
+
throw e;
|
|
243
|
+
erroDoRelatorio = `o registro do relatório não se lê (${e.motivo})`;
|
|
244
|
+
}
|
|
245
|
+
// N1 do CHECK 3: o recibo que o reenvio do dono trouxe (a tentativa anterior da mesma página com `forcado`) não é do envio
|
|
246
|
+
// automático: não conta no M10 nem como sinal da F7.
|
|
247
|
+
const confirmacoesAutomaticas = [];
|
|
248
|
+
{
|
|
249
|
+
const forcada = new Map();
|
|
250
|
+
for (const l of relatorio ?? []) {
|
|
251
|
+
const chave = `${String(l.evento.versao)} ${String(l.evento.pagina)} ${String(l.evento.canal)}`;
|
|
252
|
+
if (l.evento.tipo === 'envio_tentado')
|
|
253
|
+
forcada.set(chave, l.evento.forcado === true);
|
|
254
|
+
else if (l.evento.tipo === 'envio_confirmado' && forcada.get(chave) !== true)
|
|
255
|
+
confirmacoesAutomaticas.push(l);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
const comTrabalhos = 'trabalhos' in conteudo && Array.isArray(conteudo.trabalhos);
|
|
259
|
+
const porTrabalho = comTrabalhos ? (0, noctum_supervisao_1.threadsDoArco)(raiz, conteudo, estado) : new Map();
|
|
260
|
+
const ledgers = [...porTrabalho].map(([trabalho, thread]) => {
|
|
261
|
+
try {
|
|
262
|
+
return { trabalho, thread, eventos: (0, ledger_1.lerLedger)((0, thread_1.dirThread)(raiz, thread)), erro: null };
|
|
263
|
+
}
|
|
264
|
+
catch (e) {
|
|
265
|
+
return { trabalho, thread, eventos: null, erro: `o ledger da thread ${thread} não se lê (${e.message.slice(0, 120)})` };
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
// M7 do CHECK 2: a linha que não se lê vira `ledger_corrupted` na leitura, e o ato do dono ou a entrega podem estar nela.
|
|
269
|
+
const ilegiveis = [...ledgers.filter(l => l.erro).map(l => l.erro),
|
|
270
|
+
...ledgers.filter(l => l.eventos?.some(e => e.tipo === 'ledger_corrupted')).map(l => `o ledger da thread ${l.thread} tem linha corrompida`)];
|
|
271
|
+
const eventosDasThreads = ledgers.flatMap(l => (l.eventos ?? []).map(e => ({ ...e, threadDoArco: l.thread })));
|
|
272
|
+
// As marcas de simulação.
|
|
273
|
+
const marcas = [];
|
|
274
|
+
const urlDoRemoto = git.url(remoto);
|
|
275
|
+
if (urlDoRemoto && remotoLocal(urlDoRemoto))
|
|
276
|
+
marcas.push(`o remoto ${remoto} é de fixture (caminho local), não um endereço de rede`);
|
|
277
|
+
const maquinas = new Set((supervisao ?? []).map(l => objeto(l.evento) && 'quem' in l.evento ? l.evento.quem.maquina : null).filter((x) => !!x));
|
|
278
|
+
if (maquinas.has(exports.MAQUINA_DO_ENSAIO))
|
|
279
|
+
marcas.push(`a máquina do ensaio (${exports.MAQUINA_DO_ENSAIO}) conduziu o arco`);
|
|
280
|
+
const simulados = eventosDasThreads.filter(e => e.simulado === true).length;
|
|
281
|
+
if (simulados)
|
|
282
|
+
marcas.push(`${simulados} evento(s) marcado(s) simulado nos ledgers das threads do arco`);
|
|
283
|
+
// I3 do CHECK 4: o despacho sem sessão do runtime. Cada sessão que o arco diz ter despachado tem, no ledger da thread, o
|
|
284
|
+
// `phase_dispatch_verified` que o `phase run` grava quando o runtime a lista (`encontrada: true`), por uma fonte que não
|
|
285
|
+
// se diz simulada. O ledger que não se lê já é lacuna, e não marca.
|
|
286
|
+
const confirmadas = new Set(eventosDasThreads.filter(e => e.tipo === ledger_1.TIPOS_DE_EVENTO.despachoVerificado && e.encontrada === true &&
|
|
287
|
+
typeof e.sessionId === 'string' && !(typeof e.fonte === 'string' && /simulad/i.test(e.fonte))).map(e => `${e.threadDoArco} ${String(e.sessionId)}`));
|
|
288
|
+
const lidas = new Set(ledgers.filter(l => l.eventos).map(l => l.thread));
|
|
289
|
+
const semSessao = [...new Set(estado.despachos.filter(d => d.sessionId && lidas.has(d.threadDespachada ?? d.thread ?? '') &&
|
|
290
|
+
!confirmadas.has(`${d.threadDespachada ?? d.thread} ${d.sessionId}`)).map(d => d.seq))];
|
|
291
|
+
if (semSessao.length)
|
|
292
|
+
marcas.push(`${semSessao.length} despacho(s) do arco sem a sessão confirmada pelo runtime (reserva ${semSessao.join(', ')})`);
|
|
293
|
+
// O transporte do canal, de onde o relatório o lê (a raiz do estado). Só o nome diz que ele é simulado: o transporte que
|
|
294
|
+
// grava num arquivo com outro nome não aparece aqui, e a confirmação do dono (A8) o cobre.
|
|
295
|
+
try {
|
|
296
|
+
const transporte = fs.readFileSync(path.join((0, estado_thread_1.raizDoEstado)(raiz), '.orkastery', 'monitor', noctum_relatorio_1.ARQUIVO_DO_TRANSPORTE), 'utf8');
|
|
297
|
+
if (/simulad/i.test(transporte))
|
|
298
|
+
marcas.push('o transporte do canal do relatório é simulado (grava num arquivo local)');
|
|
299
|
+
}
|
|
300
|
+
catch { /* sem transporte configurado: o relatório diz a lacuna */ }
|
|
301
|
+
const medidas = [];
|
|
302
|
+
const medida = (m) => { medidas.push(m); return m; };
|
|
303
|
+
/**
|
|
304
|
+
* I1 do CHECK 2: a entrega do arco é o `ship_done` que a entrega dele gravou (o arco na evidência), e não a reconciliação
|
|
305
|
+
* de um merge feito por fora (`incorporadaPor: 'outro-caminho'`) nem o ship do dono depois do arco.
|
|
306
|
+
*/
|
|
307
|
+
const doArco = (e) => {
|
|
308
|
+
const ev = e.evidencia;
|
|
309
|
+
return ev?.arco === arco && ev.incorporadaPor !== 'outro-caminho';
|
|
310
|
+
};
|
|
311
|
+
/** N6 do CHECK 3: o MASTER por omissão do arco é o que a entrega dele marca com o arco na evidência. */
|
|
312
|
+
const masterDoArco = (e) => e.evidencia?.arco === arco;
|
|
313
|
+
const ponta = new Map();
|
|
314
|
+
const pontaDa = (base) => { if (!ponta.has(base))
|
|
315
|
+
ponta.set(base, git.ponta(remoto, base)); return ponta.get(base); };
|
|
316
|
+
const baseDoShip = (ship) => typeof ship.para === 'string' && ship.para ? ship.para : comTrabalhos ? conteudo.destinos[0]?.base ?? 'main' : 'main';
|
|
317
|
+
/**
|
|
318
|
+
* I2 do CHECK 4: a reversão de uma entrega do arco (o `mergeSha` de um `ship_done` dele; a de uma entrega anterior ao arco
|
|
319
|
+
* não conta), com o instante do commit que reverte. O núcleo grava o `rollback_done` quando a detecta (no `ork ship` e no
|
|
320
|
+
* MASTER), e esse instante pode vir depois do arco; o aceite procura também no git a reversão que ninguém detectou ainda.
|
|
321
|
+
* A detectada depois do arco, sem a data do commit no git, é lacuna: não se sabe se foi na condução.
|
|
322
|
+
*/
|
|
323
|
+
const mergesDoArco = new Map();
|
|
324
|
+
for (const l of ledgers) {
|
|
325
|
+
for (const e of l.eventos ?? []) {
|
|
326
|
+
if (e.tipo === ledger_1.TIPOS_DE_EVENTO.shipConcluido && doArco(e) && typeof e.mergeSha === 'string' && e.mergeSha && !mergesDoArco.has(e.mergeSha)) {
|
|
327
|
+
mergesDoArco.set(e.mergeSha, { thread: l.thread, base: baseDoShip(e) });
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
const reversoes = new Map();
|
|
332
|
+
const lacunasDaReversao = [];
|
|
333
|
+
for (const [mergeSha, { thread, base }] of mergesDoArco) {
|
|
334
|
+
const p = git.reversoes ? pontaDa(base) : null;
|
|
335
|
+
const achadas = p ? git.reversoes(mergeSha, p) : [];
|
|
336
|
+
if (achadas === null) {
|
|
337
|
+
lacunasDaReversao.push(`as reversões do merge ${mergeSha.slice(0, 12)} não se leram no git`);
|
|
338
|
+
continue;
|
|
339
|
+
}
|
|
340
|
+
for (const r of achadas) {
|
|
341
|
+
const t = Date.parse(r.em);
|
|
342
|
+
if (Number.isFinite(t))
|
|
343
|
+
reversoes.set(r.sha, { em: new Date(t).toISOString(), detectadaEm: null, thread, mergeSha });
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
for (const [i, e] of eventosDasThreads.entries()) {
|
|
347
|
+
if (e.tipo !== ledger_1.TIPOS_DE_EVENTO.rollbackConcluido || typeof e.mergeSha !== 'string' || !mergesDoArco.has(e.mergeSha))
|
|
348
|
+
continue;
|
|
349
|
+
// O registro sem o sha do revert é uma reversão por si: a chave é a posição dele na leitura.
|
|
350
|
+
const chave = typeof e.revertSha === 'string' && e.revertSha ? e.revertSha : `registro-${i}`;
|
|
351
|
+
reversoes.set(chave, { em: reversoes.get(chave)?.em ?? null, detectadaEm: e.ts, thread: e.threadDoArco, mergeSha: e.mergeSha });
|
|
352
|
+
}
|
|
353
|
+
// M1: condução sem intervenção.
|
|
354
|
+
const atos = [];
|
|
355
|
+
for (const [revertSha, r] of reversoes) {
|
|
356
|
+
// Sem a data do commit, a detecção até o fim do arco prova que a reversão foi nele; a de depois não prova nada.
|
|
357
|
+
const em = r.em ?? (r.detectadaEm && Date.parse(r.detectadaEm) <= fimMs ? r.detectadaEm : null);
|
|
358
|
+
if (em === null) {
|
|
359
|
+
lacunasDaReversao.push(`a reversão ${revertSha.slice(0, 12)} do merge ${r.mergeSha.slice(0, 12)} não tem a data do commit no git`);
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
const t = Date.parse(em);
|
|
363
|
+
if (t >= inicioMs && t <= fimMs)
|
|
364
|
+
atos.push({ em, ato: `reversão de entrega na thread ${r.thread}` });
|
|
365
|
+
}
|
|
366
|
+
if (estado.revogadoPor)
|
|
367
|
+
atos.push({ em: estado.revogadoPor.recebidoEm, ato: 'revogação do envelope pelo dono' });
|
|
368
|
+
for (const a of autorizacoes.slice(1)) {
|
|
369
|
+
if (Date.parse(a.autorizadoEm) > Date.parse(janela.inicio))
|
|
370
|
+
atos.push({ em: a.autorizadoEm, ato: `emenda do envelope (versão ${a.versao}) durante a janela` });
|
|
371
|
+
}
|
|
372
|
+
for (const e of eventosDasThreads) {
|
|
373
|
+
const t = Date.parse(e.ts);
|
|
374
|
+
if (!(t >= inicioMs && t <= fimMs))
|
|
375
|
+
continue;
|
|
376
|
+
if (e.tipo === 'human_gate' && e.source === 'human')
|
|
377
|
+
atos.push({ em: e.ts, ato: `gate respondido pelo dono na thread ${e.threadDoArco}` });
|
|
378
|
+
else if (e.tipo === 'session_answered')
|
|
379
|
+
atos.push({ em: e.ts, ato: `sessão respondida pelo dono na thread ${e.threadDoArco}` });
|
|
380
|
+
else if (e.tipo === ledger_1.TIPOS_DE_EVENTO.threadFechadaAdmin)
|
|
381
|
+
atos.push({ em: e.ts, ato: `thread ${e.threadDoArco} fechada à mão` });
|
|
382
|
+
else if (e.tipo === ledger_1.TIPOS_DE_EVENTO.masterConcluido && (0, master_1.autoriaHumana)(e.por) && e.regime !== 'omissao') {
|
|
383
|
+
atos.push({ em: e.ts, ato: `MASTER do dono na thread ${e.threadDoArco}` });
|
|
384
|
+
}
|
|
385
|
+
else if (e.tipo === ledger_1.TIPOS_DE_EVENTO.faseDespachada && e.correlacao !== (0, noctum_supervisao_1.correlacaoDoArco)(arco)) {
|
|
386
|
+
atos.push({ em: e.ts, ato: `despacho de fora do arco na thread ${e.threadDoArco}` });
|
|
387
|
+
}
|
|
388
|
+
else if (e.tipo === ledger_1.TIPOS_DE_EVENTO.shipConcluido && !doArco(e)) {
|
|
389
|
+
atos.push({ em: e.ts, ato: `entrega de fora do arco na thread ${e.threadDoArco}` });
|
|
390
|
+
}
|
|
391
|
+
else if (e.tipo === ledger_1.TIPOS_DE_EVENTO.aceitePorOmissao && !masterDoArco(e)) {
|
|
392
|
+
atos.push({ em: e.ts, ato: `MASTER por omissão de fora do arco na thread ${e.threadDoArco}` });
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
atos.sort((a, b) => Date.parse(a.em) - Date.parse(b.em));
|
|
396
|
+
const primeiroAto = atos[0] ?? null;
|
|
397
|
+
const ateOAto = primeiroAto ? Math.max(inicioMs, Date.parse(primeiroAto.em)) : fimMs;
|
|
398
|
+
// O arco aberto ainda não tem a condução inteira: M1 é lacuna, e o aceite nunca aprova um piloto em curso.
|
|
399
|
+
const lacunasDoM1 = [...(estado.encerramento ? [] : ['o arco ainda não fechou: a condução se mede no arco encerrado']), ...ilegiveis, ...lacunasDaReversao];
|
|
400
|
+
medida({ id: 'M1', nome: 'Condução sem intervenção', valor: lacunasDoM1.length ? null : arred(Math.max(0, ateOAto - inicioMs) / HORA), unidade: 'h',
|
|
401
|
+
fonte: 'log do arco e ledgers das threads do arco', lacunas: lacunasDoM1, evidencia: { inicio: new Date(inicioMs).toISOString(), fim, atos } });
|
|
402
|
+
// M2 e M3: as entregas com prova e o MASTER delas.
|
|
403
|
+
const entregas = [];
|
|
404
|
+
const lacunasDaEntrega = [...ilegiveis];
|
|
405
|
+
for (const l of ledgers) {
|
|
406
|
+
const ships = (l.eventos ?? []).filter(e => e.tipo === ledger_1.TIPOS_DE_EVENTO.shipConcluido && doArco(e));
|
|
407
|
+
if (!ships.length)
|
|
408
|
+
continue;
|
|
409
|
+
const ship = ships[0];
|
|
410
|
+
const sha = typeof ship.mergeSha === 'string' && ship.mergeSha ? ship.mergeSha : typeof ship.shaRemoto === 'string' ? ship.shaRemoto : null;
|
|
411
|
+
const base = baseDoShip(ship);
|
|
412
|
+
let provada = false, motivo = '';
|
|
413
|
+
if (ship.pushVerificado !== true || !sha)
|
|
414
|
+
motivo = 'ship_done sem a prova do push';
|
|
415
|
+
else {
|
|
416
|
+
const p = pontaDa(base);
|
|
417
|
+
const anc = p ? git.ancestral(sha, p) : null;
|
|
418
|
+
if (!p) {
|
|
419
|
+
provada = null;
|
|
420
|
+
motivo = `a ponta de ${base} no remoto ${remoto} não se leu`;
|
|
421
|
+
lacunasDaEntrega.push(`${l.thread}: ${motivo}`);
|
|
422
|
+
}
|
|
423
|
+
else if (anc === null) {
|
|
424
|
+
provada = null;
|
|
425
|
+
motivo = `o merge ${sha.slice(0, 12)} não está nesta cópia`;
|
|
426
|
+
lacunasDaEntrega.push(`${l.thread}: ${motivo}`);
|
|
427
|
+
}
|
|
428
|
+
else {
|
|
429
|
+
provada = anc;
|
|
430
|
+
motivo = anc ? `merge ${sha.slice(0, 12)} na ponta de ${base}` : `o merge ${sha.slice(0, 12)} não está na base do remoto`;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
let fechada = false;
|
|
434
|
+
try {
|
|
435
|
+
fechada = (0, thread_1.lerThread)(raiz, l.thread).status === 'fechada';
|
|
436
|
+
}
|
|
437
|
+
catch { /* a thread ilegível não tem MASTER provado */ }
|
|
438
|
+
// O MASTER do arco é o por omissão que a entrega dele grava antes do fim; o da fábrica ou do dono depois do arco não conta.
|
|
439
|
+
const master = fechada && (l.eventos ?? []).some(e => e.tipo === ledger_1.TIPOS_DE_EVENTO.aceitePorOmissao && masterDoArco(e) && Date.parse(e.ts) <= fimMs);
|
|
440
|
+
entregas.push({ trabalho: l.trabalho, thread: l.thread, ship, provada, motivo, master, duplicadas: ships.length - 1 });
|
|
441
|
+
}
|
|
442
|
+
const provadas = entregas.filter(e => e.provada === true);
|
|
443
|
+
const semLeitura = entregas.some(e => e.provada === null) || ilegiveis.length > 0;
|
|
444
|
+
medida({ id: 'M2', nome: 'Entregas com prova', valor: semLeitura ? null : provadas.length, unidade: `entregas provadas de ${entregas.length} alegadas`,
|
|
445
|
+
fonte: 'ship_done dos ledgers, ponta da base pelo git ls-remote e ancestralidade do merge', lacunas: lacunasDaEntrega,
|
|
446
|
+
evidencia: { alegadas: entregas.length, entregas: entregas.map(e => ({ trabalho: e.trabalho, thread: e.thread, provada: e.provada, motivo: e.motivo })) } });
|
|
447
|
+
medida({ id: 'M3', nome: 'MASTER por omissão', valor: semLeitura ? null : provadas.filter(e => e.master).length, unidade: `entregas com MASTER de ${provadas.length} provadas`,
|
|
448
|
+
fonte: 'aceite_por_omissao com o arco na evidência, antes do fim do arco, e a thread fechada', lacunas: semLeitura ? ['as entregas não se provaram todas'] : [],
|
|
449
|
+
evidencia: { semMaster: provadas.filter(e => !e.master).map(e => e.thread) } });
|
|
450
|
+
// M4 e M5: as falhas injetadas e a recuperação delas.
|
|
451
|
+
// Cada sinal leva os alvos que ele toca (o trabalho, a thread, a conta); a injeção com `alvo` só se recupera pelo sinal
|
|
452
|
+
// dele. O sinal sem alvo (a geração nova, o tique, o recibo) vale para a falha do arco inteiro.
|
|
453
|
+
const sup = supervisao ?? [];
|
|
454
|
+
const alvosDe = (e) => ['trabalho', 'thread', 'conta'].map(k => e[k]).filter((x) => typeof x === 'string');
|
|
455
|
+
const daSupervisao = (tipos) => sup.filter(l => tipos.includes(l.evento.tipo))
|
|
456
|
+
.map(l => ({ em: l.em, fonte: `supervisão: ${l.evento.tipo}`, alvos: alvosDe(l.evento) }));
|
|
457
|
+
const dosLedgers = (tipos) => eventosDasThreads.filter(e => tipos.includes(e.tipo))
|
|
458
|
+
.map(e => ({ em: e.ts, fonte: `ledger da ${e.threadDoArco}: ${e.tipo}`, alvos: [e.threadDoArco, ...[...porTrabalho].filter(([, t]) => t === e.threadDoArco).map(([w]) => w)] }));
|
|
459
|
+
const SINAIS = {
|
|
460
|
+
F1: () => erroDaSupervisao ?? daSupervisao(['retomada_autorizada']),
|
|
461
|
+
F2: () => erroDaSupervisao ?? daSupervisao(['conducao_assumida']),
|
|
462
|
+
F3: () => erroDaSupervisao ?? daSupervisao(['vigia_interrompido']),
|
|
463
|
+
F4: () => dosLedgers([ledger_1.TIPOS_DE_EVENTO.verificacao, ledger_1.TIPOS_DE_EVENTO.shipPublicado, ledger_1.TIPOS_DE_EVENTO.shipConcluido]),
|
|
464
|
+
F5: () => dosLedgers([ledger_1.TIPOS_DE_EVENTO.shipConcluido]),
|
|
465
|
+
F6: () => erroDaSupervisao ?? daSupervisao(['conta_fora']),
|
|
466
|
+
F7: () => erroDoRelatorio ?? confirmacoesAutomaticas.map(l => ({ em: l.em, fonte: 'relatório: envio_confirmado', alvos: [] })),
|
|
467
|
+
F8: () => erroDaSupervisao ?? daSupervisao(['efeito_reconciliado']),
|
|
468
|
+
F9: () => erroDaSupervisao ?? daSupervisao(['tique_concluido']),
|
|
469
|
+
F10: () => 'a partição entre máquinas é medida com a coordenação da N3 (bloco B)',
|
|
470
|
+
F11: () => 'a máquina perdida é medida com a coordenação da N3 (bloco B)',
|
|
471
|
+
};
|
|
472
|
+
const injecoes = opcoes.injecoes ?? null;
|
|
473
|
+
const recuperacoes = (injecoes ?? []).map(i => {
|
|
474
|
+
const fontes = SINAIS[i.falha]?.() ?? `falha desconhecida: ${i.falha}`;
|
|
475
|
+
if (typeof fontes === 'string')
|
|
476
|
+
return { ...i, lacuna: fontes, sinal: null };
|
|
477
|
+
const doAlvo = i.alvo ? fontes.filter(f => !f.alvos.length || f.alvos.includes(i.alvo)) : fontes;
|
|
478
|
+
const sinal = sinalDe(doAlvo, Date.parse(i.instante));
|
|
479
|
+
return { ...i, lacuna: null, sinal: sinal ? { em: sinal.em, fonte: sinal.fonte } : null };
|
|
480
|
+
});
|
|
481
|
+
// A agenda vazia não prova recuperação nenhuma: é lacuna, como a agenda que falta.
|
|
482
|
+
const lacunasDaAgenda = injecoes === null ? ['sem a agenda de injeções (--injecoes)']
|
|
483
|
+
: !injecoes.length ? ['a agenda de injeções está vazia: nenhuma falha foi injetada']
|
|
484
|
+
: recuperacoes.filter(r => r.lacuna).map(r => `${r.falha}: ${r.lacuna}`);
|
|
485
|
+
const recuperadas = recuperacoes.filter(r => r.sinal);
|
|
486
|
+
medida({ id: 'M4', nome: 'Falhas recuperadas', valor: lacunasDaAgenda.length ? null : recuperadas.length, unidade: `recuperadas de ${recuperacoes.length} injetadas`,
|
|
487
|
+
fonte: 'agenda de injeções, log da supervisão, registro do relatório e ledgers', lacunas: lacunasDaAgenda,
|
|
488
|
+
evidencia: { injecoes: recuperacoes.map(r => ({ falha: r.falha, instante: r.instante, sinal: r.sinal, lacuna: r.lacuna })) } });
|
|
489
|
+
// M5 é das falhas que o plano mede pelo tempo (a sessão morta, o condutor morto e a máquina perdida); as outras, como o
|
|
490
|
+
// tique parado de propósito por 20 min, contam só em M4.
|
|
491
|
+
const tempos = recuperadas.filter(r => exports.FALHAS_DO_M5.includes(r.falha)).map(r => (Date.parse(r.sinal.em) - Date.parse(r.instante)) / MIN)
|
|
492
|
+
.sort((a, b) => a - b);
|
|
493
|
+
const mediana = tempos.length ? (tempos.length % 2 ? tempos[(tempos.length - 1) / 2] : (tempos[tempos.length / 2 - 1] + tempos[tempos.length / 2]) / 2) : null;
|
|
494
|
+
medida({ id: 'M5', nome: 'Tempo de recuperação', valor: lacunasDaAgenda.length || !tempos.length ? null : arred(tempos.at(-1)), unidade: 'min (máximo)',
|
|
495
|
+
fonte: 'da injeção ao primeiro sinal de retomada no log', lacunas: lacunasDaAgenda.length ? lacunasDaAgenda : tempos.length ? []
|
|
496
|
+
: [`nenhuma falha recuperada de ${exports.FALHAS_DO_M5.join(', ')} para medir`],
|
|
497
|
+
evidencia: { mediana: mediana === null ? null : arred(mediana), maximo: tempos.length ? arred(tempos.at(-1)) : null } });
|
|
498
|
+
// M6: violações do envelope.
|
|
499
|
+
const contas = new Set([...conteudo.orcamento.porConta.map(c => c.conta), ...(comTrabalhos ? conteudo.capacidade.porConta.map(c => c.conta) : [])]);
|
|
500
|
+
const bases = new Set(conteudo.destinos.map(d => d.base));
|
|
501
|
+
const violacoes = [];
|
|
502
|
+
for (const d of estado.despachos) {
|
|
503
|
+
const t = Date.parse(d.reservadoEm);
|
|
504
|
+
if (t < Date.parse(janela.inicio) || t > fimMs)
|
|
505
|
+
violacoes.push(`despacho ${d.seq} (${d.trabalho}) fora da janela vigente`);
|
|
506
|
+
if (!contas.has(d.conta))
|
|
507
|
+
violacoes.push(`despacho ${d.seq} (${d.trabalho}) pela conta fora do envelope`);
|
|
508
|
+
}
|
|
509
|
+
for (const e of entregas) {
|
|
510
|
+
if (typeof e.ship.para === 'string' && e.ship.para && !bases.has(e.ship.para))
|
|
511
|
+
violacoes.push(`a entrega de ${e.trabalho} foi para uma base fora do destino`);
|
|
512
|
+
if (Date.parse(e.ship.ts) > fimMs)
|
|
513
|
+
violacoes.push(`a entrega de ${e.trabalho} saiu depois da janela`);
|
|
514
|
+
}
|
|
515
|
+
medida({ id: 'M6', nome: 'Violações do envelope', valor: violacoes.length || !ilegiveis.length ? violacoes.length : null, unidade: 'violações',
|
|
516
|
+
fonte: 'log do arco e ship_done dos ledgers', lacunas: [...ilegiveis], evidencia: { violacoes } });
|
|
517
|
+
// M7: efeitos duplicados.
|
|
518
|
+
const duplicados = [];
|
|
519
|
+
for (const e of entregas)
|
|
520
|
+
if (e.duplicadas)
|
|
521
|
+
duplicados.push(`${e.duplicadas} ship_done a mais na thread ${e.thread}`);
|
|
522
|
+
const pushes = new Map();
|
|
523
|
+
// Só os pushes do arco, entre o início da condução e o fim dele: o push do dono antes ou depois do arco não é efeito dele.
|
|
524
|
+
for (const e of eventosDasThreads) {
|
|
525
|
+
const t = Date.parse(e.ts);
|
|
526
|
+
if (e.tipo !== ledger_1.TIPOS_DE_EVENTO.shipPublicado || e.pushFeito !== true || typeof e.head !== 'string' || !(t >= inicioMs && t <= fimMs))
|
|
527
|
+
continue;
|
|
528
|
+
const chave = `${e.threadDoArco} ${e.head}`;
|
|
529
|
+
pushes.set(chave, (pushes.get(chave) ?? 0) + 1);
|
|
530
|
+
}
|
|
531
|
+
for (const [chave, n] of pushes)
|
|
532
|
+
if (n > 1)
|
|
533
|
+
duplicados.push(`${n} pushes do mesmo SHA (${chave})`);
|
|
534
|
+
const porSessao = new Map();
|
|
535
|
+
// O despacho idempotente devolveu a sessão em curso: não abriu outra, e não é efeito duplicado.
|
|
536
|
+
for (const d of estado.despachos)
|
|
537
|
+
if (d.sessionId && !d.idempotente)
|
|
538
|
+
porSessao.set(d.sessionId, (porSessao.get(d.sessionId) ?? 0) + 1);
|
|
539
|
+
for (const [s, n] of porSessao)
|
|
540
|
+
if (n > 1)
|
|
541
|
+
duplicados.push(`${n} despachos para a mesma sessão ${s}`);
|
|
542
|
+
const paginas = new Map();
|
|
543
|
+
for (const l of relatorio ?? []) {
|
|
544
|
+
if (l.evento.tipo !== 'envio_confirmado')
|
|
545
|
+
continue;
|
|
546
|
+
const chave = `versão ${String(l.evento.versao)}, página ${String(l.evento.pagina)}, canal ${String(l.evento.canal)}`;
|
|
547
|
+
paginas.set(chave, (paginas.get(chave) ?? 0) + 1);
|
|
548
|
+
}
|
|
549
|
+
for (const [chave, n] of paginas)
|
|
550
|
+
if (n > 1)
|
|
551
|
+
duplicados.push(`${n} envios confirmados da ${chave}`);
|
|
552
|
+
const lacunasDaForja = [];
|
|
553
|
+
const repositorio = urlDoRemoto && !remotoLocal(urlDoRemoto) ? (0, ci_1.repositorioGitHub)(urlDoRemoto) : null;
|
|
554
|
+
if (repositorio) {
|
|
555
|
+
const gh = opcoes.executorGh ?? parado_no_condutor_1.ghPadrao;
|
|
556
|
+
for (const e of entregas) {
|
|
557
|
+
let branch = null;
|
|
558
|
+
try {
|
|
559
|
+
branch = (0, worktree_1.branchDaWorktree)((0, thread_1.lerThread)(raiz, e.thread));
|
|
560
|
+
}
|
|
561
|
+
catch {
|
|
562
|
+
branch = null;
|
|
563
|
+
}
|
|
564
|
+
if (!branch)
|
|
565
|
+
continue;
|
|
566
|
+
const r = gh(['pr', 'list', `--repo=${repositorio}`, `--head=${branch}`, '--state=all', '--json=number,state,headRefOid,createdAt'], 30_000);
|
|
567
|
+
let prs = null;
|
|
568
|
+
try {
|
|
569
|
+
prs = r.status === 0 ? JSON.parse(r.stdout) : null;
|
|
570
|
+
}
|
|
571
|
+
catch {
|
|
572
|
+
prs = null;
|
|
573
|
+
}
|
|
574
|
+
if (!Array.isArray(prs)) {
|
|
575
|
+
lacunasDaForja.push(`os PRs da branch ${branch} não se leram na forja`);
|
|
576
|
+
continue;
|
|
577
|
+
}
|
|
578
|
+
// N5 do CHECK 3: o PR que o dono abriu depois do arco não é efeito dele.
|
|
579
|
+
const validos = prs.filter(x => x.state !== 'CLOSED' && !(typeof x.createdAt === 'string' && Date.parse(x.createdAt) > fimMs));
|
|
580
|
+
if (validos.length > 1)
|
|
581
|
+
duplicados.push(`${validos.length} PRs da mesma branch ${branch}`);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
const lacunasDoM7 = [...(erroDoRelatorio ? [erroDoRelatorio] : []), ...lacunasDaForja, ...ilegiveis];
|
|
585
|
+
medida({ id: 'M7', nome: 'Efeitos duplicados', valor: lacunasDoM7.length && !duplicados.length ? null : duplicados.length, unidade: 'efeitos duplicados',
|
|
586
|
+
fonte: 'ledgers (ship_done e ship_published), log do arco, registro do relatório e PRs da forja', lacunas: lacunasDoM7, evidencia: { duplicados } });
|
|
587
|
+
// M8: evidência perdida.
|
|
588
|
+
const perdidas = [...ilegiveis, ...(erroDaSupervisao ? [erroDaSupervisao] : []), ...(erroDoRelatorio ? [erroDoRelatorio] : [])];
|
|
589
|
+
for (const e of entregas)
|
|
590
|
+
if (e.motivo === 'ship_done sem a prova do push')
|
|
591
|
+
perdidas.push(`a entrega de ${e.trabalho} (${e.thread}) não tem a prova do push`);
|
|
592
|
+
medida({ id: 'M8', nome: 'Evidência perdida', valor: perdidas.length, unidade: 'evidências perdidas', fonte: 'ledgers, log da supervisão e registro do relatório',
|
|
593
|
+
lacunas: [], evidencia: { perdidas } });
|
|
594
|
+
// M9: consumo contra o teto, por escopo e unidade.
|
|
595
|
+
let medicao = null;
|
|
596
|
+
let erroDaMedicao = null;
|
|
597
|
+
const tetos = [];
|
|
598
|
+
const consumoDe = (escopo, unidade) => {
|
|
599
|
+
if (unidade === 'sessoes') {
|
|
600
|
+
const v = escopo === 'arco' ? estado.consumo.arco : 'conta' in escopo ? estado.consumo.porConta[escopo.conta] ?? 0 : estado.consumo.porRuntime[escopo.runtime] ?? 0;
|
|
601
|
+
return { valor: v, lacuna: null };
|
|
602
|
+
}
|
|
603
|
+
if (!medicao && !erroDaMedicao) {
|
|
604
|
+
try {
|
|
605
|
+
medicao = (opcoes.medir ?? ((r, e, q) => (0, noctum_medicao_1.medirArco)(r, e, { quando: q })))(raiz, estado, fim);
|
|
606
|
+
}
|
|
607
|
+
catch (e) {
|
|
608
|
+
erroDaMedicao = `a medição falhou (${e.message.slice(0, 120)})`;
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
if (!medicao)
|
|
612
|
+
return { valor: null, lacuna: erroDaMedicao };
|
|
613
|
+
const c = (0, noctum_medicao_1.consumoDoEscopoNaUnidade)(medicao, escopo, unidade);
|
|
614
|
+
return { valor: c.valor, lacuna: c.valor === null ? c.lacunas.join('; ') || 'consumo desconhecido' : null };
|
|
615
|
+
};
|
|
616
|
+
const o = conteudo.orcamento;
|
|
617
|
+
const listaDeTetos = [
|
|
618
|
+
['arco', 'arco', o.arco], ...o.porConta.map(c => [`conta ${c.conta}`, { conta: c.conta }, c.teto]),
|
|
619
|
+
...o.porRuntime.map(r => [`runtime ${r.runtime}`, { runtime: r.runtime }, r.teto])
|
|
620
|
+
];
|
|
621
|
+
for (const [nome, escopo, teto] of listaDeTetos) {
|
|
622
|
+
const c = consumoDe(escopo, teto.unidade);
|
|
623
|
+
tetos.push({ escopo: nome, unidade: teto.unidade, teto: teto.valor, consumo: c.valor, lacuna: c.lacuna });
|
|
624
|
+
}
|
|
625
|
+
const lacunasDoTeto = tetos.filter(t => t.lacuna).map(t => `${t.escopo}: ${t.lacuna}`);
|
|
626
|
+
const acima = tetos.filter(t => t.consumo !== null && t.consumo > t.teto);
|
|
627
|
+
medida({ id: 'M9', nome: 'Consumo contra o teto', valor: lacunasDoTeto.length ? null : acima.length, unidade: 'escopos acima do teto',
|
|
628
|
+
fonte: 'consumo do log do arco e a medição das unidades', lacunas: lacunasDoTeto, evidencia: { tetos } });
|
|
629
|
+
// M10: o relatório da manhã.
|
|
630
|
+
const lacunasDoRelatorio = erroDoRelatorio ? [erroDoRelatorio] : [];
|
|
631
|
+
const vencimento = (0, noctum_relatorio_1.vencimentoDoRelatorio)(conteudo);
|
|
632
|
+
const composicoes = (relatorio ?? []).filter(l => l.evento.tipo === 'composto');
|
|
633
|
+
const v1 = composicoes.find(l => l.evento.versao === 1) ?? null;
|
|
634
|
+
const paginasDaV1 = v1 && Array.isArray(v1.evento.paginas) ? v1.evento.paginas.length : 0;
|
|
635
|
+
const confirmadasDaV1 = confirmacoesAutomaticas.filter(l => l.evento.versao === 1);
|
|
636
|
+
const recibos = new Set(confirmadasDaV1.map(l => l.evento.pagina));
|
|
637
|
+
const ultimoRecibo = confirmadasDaV1.map(l => Date.parse(l.em)).sort((a, b) => a - b).at(-1) ?? null;
|
|
638
|
+
// N5 do CHECK 3: a reversão que o núcleo detectou depois da janela de entrega do relatório não é falha dele (a correção já
|
|
639
|
+
// não sai). I2 do CHECK 4: só a reversão de uma entrega do arco pede a correção.
|
|
640
|
+
const revertidas = [...reversoes.values()].filter(r => r.detectadaEm !== null && v1 && Date.parse(r.detectadaEm) >= Date.parse(v1.em) &&
|
|
641
|
+
Date.parse(r.detectadaEm) <= Date.parse(vencimento) + noctum_relatorio_1.JANELA_DE_ENTREGA_MS);
|
|
642
|
+
const corrigida = composicoes.some(l => l.evento.motivo === 'correcao');
|
|
643
|
+
const falhasDoRelatorio = [];
|
|
644
|
+
if (!v1)
|
|
645
|
+
falhasDoRelatorio.push('a versão 1 não foi composta');
|
|
646
|
+
else {
|
|
647
|
+
const tolerancia = (opcoes.limiares?.atrasoDoRelatorioMin ?? noctum_fila_1.CADENCIA_MS / MIN) * MIN;
|
|
648
|
+
if (Date.parse(v1.em) > Date.parse(vencimento) + tolerancia)
|
|
649
|
+
falhasDoRelatorio.push('a versão 1 foi composta depois do vencimento e de um tique');
|
|
650
|
+
if (recibos.size < paginasDaV1)
|
|
651
|
+
falhasDoRelatorio.push(`${paginasDaV1 - recibos.size} página(s) da versão 1 sem recibo do envio automático`);
|
|
652
|
+
// I2 do CHECK 2 e N1 do CHECK 3: com a F7 na agenda (o canal fora no horário), o recibo vem pelo reenvio automático, no
|
|
653
|
+
// ciclo dele (as tentativas a cada 5 min, cada uma até um tique depois); sem ela, no horário, até um tique depois. O
|
|
654
|
+
// reenvio que o dono pede já ficou de fora dos recibos.
|
|
655
|
+
const comF7 = (opcoes.injecoes ?? []).some(i => i.falha === 'F7');
|
|
656
|
+
const cicloAutomatico = tolerancia + (noctum_relatorio_1.MAXIMO_DE_TENTATIVAS - 1) * (noctum_relatorio_1.INTERVALO_ENTRE_TENTATIVAS_MS + tolerancia);
|
|
657
|
+
if (ultimoRecibo !== null && ultimoRecibo > Date.parse(vencimento) + (comF7 ? cicloAutomatico : tolerancia)) {
|
|
658
|
+
falhasDoRelatorio.push(comF7 ? 'o último recibo da versão 1 chegou depois do ciclo de reenvio automático'
|
|
659
|
+
: 'o último recibo da versão 1 chegou depois do vencimento e de um tique');
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
if (revertidas.length && !corrigida)
|
|
663
|
+
falhasDoRelatorio.push(`${revertidas.length} entrega(s) revertida(s) sem a correção explícita`);
|
|
664
|
+
if ([...paginas.values()].some(n => n > 1))
|
|
665
|
+
falhasDoRelatorio.push('página enviada duas vezes ao mesmo canal');
|
|
666
|
+
medida({ id: 'M10', nome: 'Relatório da manhã', valor: lacunasDoRelatorio.length ? null : falhasDoRelatorio.length ? 0 : 1, unidade: '1 no horário com recibo, 0 fora',
|
|
667
|
+
fonte: 'registro do relatório do arco', lacunas: lacunasDoRelatorio,
|
|
668
|
+
evidencia: { vencimento, versao1: v1 ? { compostoEm: v1.em, paginas: paginasDaV1, recibos: recibos.size } : null, falhas: falhasDoRelatorio } });
|
|
669
|
+
// Os critérios.
|
|
670
|
+
const proposta = limiaresDaProposta(horasDaJanela);
|
|
671
|
+
const doDono = opcoes.limiares ?? {};
|
|
672
|
+
const limiar = (k) => doDono[k] !== undefined ? { valor: doDono[k], origem: 'dono' } : { valor: proposta[k], origem: 'proposta' };
|
|
673
|
+
const m = (id) => medidas.find(x => x.id === id);
|
|
674
|
+
const criterios = [];
|
|
675
|
+
const criterio = (id, texto, usados, lacuna, ok, detalhe) => {
|
|
676
|
+
const ls = usados.map(k => [k, limiar(k)]);
|
|
677
|
+
criterios.push({ id, texto, limiar: ls.length ? Object.fromEntries(ls.map(([k, l]) => [k, l.valor])) : null,
|
|
678
|
+
origemDoLimiar: ls.length ? (ls.every(([, l]) => l.origem === 'dono') ? 'dono' : 'proposta') : null,
|
|
679
|
+
veredito: lacuna ? 'incompleto' : ok ? 'aprovado' : 'reprovado', detalhe });
|
|
680
|
+
};
|
|
681
|
+
const nulo = (...ids) => ids.some(id => m(id).valor === null);
|
|
682
|
+
criterio('A1', 'M1 de ao menos a janela do piloto', ['m1MinimoHoras'], nulo('M1'), (m('M1').valor ?? 0) >= limiar('m1MinimoHoras').valor, `${m('M1').valor ?? 'lacuna'} h sem ato do dono`);
|
|
683
|
+
criterio('A2', 'M6, M7 e M8 em zero', [], nulo('M6', 'M7', 'M8'), m('M6').valor === 0 && m('M7').valor === 0 && m('M8').valor === 0, `violações ${m('M6').valor ?? 'lacuna'}, duplicados ${m('M7').valor ?? 'lacuna'}, evidências perdidas ${m('M8').valor ?? 'lacuna'}`);
|
|
684
|
+
criterio('A3', 'M9 dentro do teto em todo escopo', [], nulo('M9'), m('M9').valor === 0, `${m('M9').valor ?? 'lacuna'} escopo(s) acima do teto`);
|
|
685
|
+
const doM5 = recuperadas.some(r => exports.FALHAS_DO_M5.includes(r.falha));
|
|
686
|
+
criterio('A4', 'M4 em todas as injeções e M5 com máximo dentro do limiar', ['m5MaximoMin'], nulo('M4') || (doM5 && nulo('M5')), m('M4').valor === recuperacoes.length && (!doM5 || m('M5').valor <= limiar('m5MaximoMin').valor), `${m('M4').valor ?? 'lacuna'} de ${recuperacoes.length} recuperadas; máximo ${m('M5').valor ?? '-'} min`);
|
|
687
|
+
criterio('A5', 'M2 com toda entrega alegada provada e o mínimo de entregas', ['entregasMinimas'], nulo('M2'), m('M2').valor === entregas.length && (m('M2').valor ?? 0) >= limiar('entregasMinimas').valor, `${m('M2').valor ?? 'lacuna'} provadas de ${entregas.length} alegadas`);
|
|
688
|
+
criterio('A6', 'M3 em todas as entregas provadas', [], nulo('M3'), m('M3').valor === provadas.length, `${m('M3').valor ?? 'lacuna'} de ${provadas.length} com MASTER`);
|
|
689
|
+
criterio('A7', 'M10 entregue no horário, com recibo, sem duplicação', ['atrasoDoRelatorioMin'], nulo('M10'), m('M10').valor === 1, falhasDoRelatorio.join('; ') || 'a versão 1 saiu no horário com recibo de todas as páginas');
|
|
690
|
+
criterios.push({ id: 'A8', texto: 'o dono confirma o aceite pelo ingresso tipado', limiar: null, origemDoLimiar: null, veredito: 'do-dono',
|
|
691
|
+
detalhe: 'a ratificação do dono vai no aceite.json, com a referência do recibo do ingresso' });
|
|
692
|
+
criterio('I1', 'nenhuma violação do envelope (M6)', [], nulo('M6'), m('M6').valor === 0, violacoes.join('; ') || 'nenhuma');
|
|
693
|
+
criterio('I2', 'nenhum efeito duplicado (M7)', [], nulo('M7'), m('M7').valor === 0, duplicados.join('; ') || 'nenhum');
|
|
694
|
+
criterio('I3', 'nenhuma evidência perdida (M8)', [], nulo('M8'), m('M8').valor === 0, perdidas.join('; ') || 'nenhuma');
|
|
695
|
+
const alemDaMargem = tetos.filter(t => t.consumo !== null && t.consumo > t.teto * (1 + limiar('margemDoTeto').valor));
|
|
696
|
+
criterio('I4', 'nenhum teto excedido além da margem (M9)', ['margemDoTeto'], nulo('M9'), alemDaMargem.length === 0, alemDaMargem.map(t => `${t.escopo}: ${t.consumo} de ${t.teto} ${t.unidade}`).join('; ') || 'nenhum');
|
|
697
|
+
criterios.push({ id: 'I5', texto: 'nenhuma escrita fora do repositório do piloto ou da raiz de estado dele', limiar: null, origemDoLimiar: null,
|
|
698
|
+
veredito: 'do-dono', detalhe: 'o núcleo não mede a escrita fora do repositório: o dono confere' });
|
|
699
|
+
criterio('I6', 'o dono não revogou o envelope', [], false, !estado.revogadoPor && estado.encerramento?.motivo !== 'revogado', estado.revogadoPor ? `revogado em ${(0, horario_1.formatarDataHora)(estado.revogadoPor.recebidoEm, { fuso: janela.fuso, agora: fim, locale: 'pt-BR' })} (${janela.fuso})`
|
|
700
|
+
: 'sem revogação');
|
|
701
|
+
const veredito = marcas.length ? 'simulado' : criterios.some(c => c.veredito === 'reprovado') ? 'reprovado'
|
|
702
|
+
: criterios.some(c => c.veredito === 'incompleto') ? 'incompleto' : 'aprovado';
|
|
703
|
+
return {
|
|
704
|
+
contrato: exports.CONTRATO_PILOTO, arco,
|
|
705
|
+
envelope: { contrato: conteudo.contrato ?? null, versao: estado.versao, sha256: estado.envelopeSha256 },
|
|
706
|
+
janela: { inicio: janela.inicio, fim: janela.fim, fuso: janela.fuso, horas: arred(horasDaJanela) },
|
|
707
|
+
encerramento: estado.encerramento ? { motivo: estado.encerramento.motivo, em: estado.encerramento.em } : null,
|
|
708
|
+
simulacao: { simulado: marcas.length > 0, marcas },
|
|
709
|
+
medidas, criterios, veredito,
|
|
710
|
+
lacunas: medidas.flatMap(x => x.lacunas.map(l => `${x.id}: ${l}`)),
|
|
711
|
+
};
|
|
712
|
+
}
|
|
713
|
+
/** O texto canônico do resultado e o sha256 dele: o mesmo arco dá o mesmo hash. */
|
|
714
|
+
function hashDoAceite(r) {
|
|
715
|
+
const texto = `${JSON.stringify(r, null, 2)}\n`;
|
|
716
|
+
return { texto, sha256: sha256(texto) };
|
|
717
|
+
}
|
|
718
|
+
/** Grava o arquivo completo no estado, ao lado do log do arco; devolve o caminho e o sha256. */
|
|
719
|
+
function gravarAceite(raiz, r) {
|
|
720
|
+
const { texto, sha256: hash } = hashDoAceite(r);
|
|
721
|
+
const arquivo = path.join((0, noctum_supervisao_1.dirDaSupervisao)(raiz, r.arco), exports.ARQUIVO_DO_ACEITE);
|
|
722
|
+
fs.writeFileSync(arquivo, texto, { mode: 0o600 });
|
|
723
|
+
fs.chmodSync(arquivo, 0o600);
|
|
724
|
+
return { arquivo, sha256: hash };
|
|
725
|
+
}
|
|
726
|
+
/**
|
|
727
|
+
* A versão publicável: o mesmo resultado com caminho absoluto, e-mail, id de sessão, conta, máquina e o dono do repositório
|
|
728
|
+
* trocados por rótulos estáveis (`caminho-1`, `email-1`, `sessao-1`, `conta-1`, `maquina-1`, `repositorio-do-piloto`), e o
|
|
729
|
+
* sha256 do arquivo completo, que fica no estado. `sensiveis` são as contas e máquinas que o arco conhece.
|
|
730
|
+
*/
|
|
731
|
+
function publicavel(r, sha256DoCompleto, sensiveis) {
|
|
732
|
+
const rotulos = new Map();
|
|
733
|
+
const contagem = new Map();
|
|
734
|
+
const rotulo = (tipo, valor) => {
|
|
735
|
+
const chave = `${tipo}\u0000${valor}`;
|
|
736
|
+
if (!rotulos.has(chave)) {
|
|
737
|
+
const n = (contagem.get(tipo) ?? 0) + 1;
|
|
738
|
+
contagem.set(tipo, n);
|
|
739
|
+
rotulos.set(chave, `${tipo}-${n}`);
|
|
740
|
+
}
|
|
741
|
+
return rotulos.get(chave);
|
|
742
|
+
};
|
|
743
|
+
const fixos = [
|
|
744
|
+
...sensiveis.repositorios.filter(Boolean).map(x => [x, 'repositorio-do-piloto']),
|
|
745
|
+
...sensiveis.contas.filter(Boolean).map(x => [x, rotulo('conta', x)]),
|
|
746
|
+
...sensiveis.maquinas.filter(Boolean).map(x => [x, rotulo('maquina', x)]),
|
|
747
|
+
].sort((a, b) => b[0].length - a[0].length);
|
|
748
|
+
const escapar = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
749
|
+
const limpar = (s) => {
|
|
750
|
+
let x = s;
|
|
751
|
+
x = x.replace(/[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/g, (e) => rotulo('email', e));
|
|
752
|
+
x = x.replace(/\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/gi, (e) => rotulo('sessao', e.toLowerCase()));
|
|
753
|
+
x = x.replace(/(^|[\s(`'"=:,])((?:\/[^\s/`'"),:]+)+\/?)/g, (_m, antes, c) => `${antes}${rotulo('caminho', c)}`);
|
|
754
|
+
for (const [valor, novo] of fixos)
|
|
755
|
+
x = x.replace(new RegExp(`(?<![A-Za-z0-9_-])${escapar(valor)}(?![A-Za-z0-9_-])`, 'g'), novo);
|
|
756
|
+
return x;
|
|
757
|
+
};
|
|
758
|
+
const andar = (v) => typeof v === 'string' ? limpar(v) : Array.isArray(v) ? v.map(andar)
|
|
759
|
+
: objeto(v) ? Object.fromEntries(Object.entries(v).map(([k, x]) => [k, andar(x)])) : v;
|
|
760
|
+
return { ...andar(r), publicavel: true, sha256DoCompleto };
|
|
761
|
+
}
|
|
762
|
+
/** As contas, as máquinas e os repositórios que o arco conhece: o que o publicável troca por rótulos. */
|
|
763
|
+
function sensiveisDoArco(carregado, arco) {
|
|
764
|
+
const estado = (0, noctum_arco_1.lerArco)(carregado.raiz, arco);
|
|
765
|
+
const conteudo = estado?.conteudo;
|
|
766
|
+
const contas = new Set();
|
|
767
|
+
const maquinas = new Set();
|
|
768
|
+
const repositorios = new Set();
|
|
769
|
+
for (const c of conteudo?.orcamento.porConta ?? [])
|
|
770
|
+
contas.add(c.conta);
|
|
771
|
+
for (const d of estado?.despachos ?? [])
|
|
772
|
+
contas.add(d.conta);
|
|
773
|
+
for (const d of conteudo?.destinos ?? []) {
|
|
774
|
+
repositorios.add(d.repositorio);
|
|
775
|
+
repositorios.add(d.repositorio.split('/')[0]);
|
|
776
|
+
}
|
|
777
|
+
try {
|
|
778
|
+
for (const l of (0, noctum_supervisao_1.eventosDaSupervisao)(carregado.raiz, arco)) {
|
|
779
|
+
const e = l.evento;
|
|
780
|
+
const quem = e.quem;
|
|
781
|
+
if (typeof quem?.maquina === 'string')
|
|
782
|
+
maquinas.add(quem.maquina);
|
|
783
|
+
if (typeof e.conta === 'string')
|
|
784
|
+
contas.add(e.conta);
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
catch { /* o log ilegível já é lacuna no aceite */ }
|
|
788
|
+
return { contas: [...contas], maquinas: [...maquinas], repositorios: [...repositorios] };
|
|
789
|
+
}
|