@orkastery/cli 0.2.0 → 0.3.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/LICENSE +1 -1
- package/README.md +86 -65
- package/adapters/README.md +30 -15
- package/adapters/claude-code/.claude-plugin/plugin.json +11 -5
- package/adapters/claude-code/README.md +110 -8
- package/adapters/claude-code/commands/onboarding.md +21 -0
- package/adapters/claude-code/commands/ork.md +99 -27
- package/adapters/claude-code/hooks/hooks.json +6 -1
- package/adapters/claude-code/hooks/ork-sensor.js +66 -0
- package/adapters/codex/skills/ork/SKILL.md +57 -0
- package/adapters/hermes/README.md +163 -0
- package/adapters/hermes/bin/ork-abrir-thread.sh +2 -0
- package/adapters/hermes/bin/ork-brain.sh +4 -0
- package/adapters/hermes/bin/ork-hitl-answer.py +82 -0
- package/adapters/hermes/bin/ork-maestro.sh +6 -0
- package/adapters/hermes/bin/ork-master-enviar.py +84 -0
- package/adapters/hermes/bin/ork-objective-message.sh +17 -0
- package/adapters/hermes/bin/ork-objective-status.sh +12 -0
- package/adapters/hermes/bin/ork-pulse-enviar.py +47 -0
- package/adapters/hermes/hermes.plugin.json +26 -4
- package/adapters/hermes/hitl-ingress/__init__.py +356 -0
- package/adapters/hermes/hitl-ingress/plugin.yaml +4 -0
- package/adapters/hermes/skills/orkastery-devmaster/SKILL.md +75 -22
- package/adapters/openclaw/README.md +135 -26
- package/adapters/openclaw/bin/ork-brain.sh +4 -0
- package/adapters/openclaw/construir.sh +19 -0
- package/adapters/openclaw/dist/hitl-ingress.js +199 -0
- package/adapters/openclaw/dist/index.js +437 -0
- package/adapters/openclaw/openclaw.plugin.json +39 -134
- package/adapters/openclaw/package.json +26 -0
- package/adapters/openclaw/src/hitl-ingress.ts +180 -0
- package/adapters/openclaw/src/index.ts +458 -0
- package/adapters/openclaw/src/tipos-openclaw.d.ts +58 -0
- package/adapters/openclaw/tsconfig.json +15 -0
- package/assets/docs/markdownlint-cli2.jsonc +22 -0
- package/assets/docs/padroes/documentacao-de-produto.md +133 -0
- package/assets/docs/padroes/roadmap-de-produto.md +98 -0
- package/assets/docs/produto/README.md +14 -0
- package/assets/docs/produto/_modelo-feature.md +59 -0
- package/assets/docs/roadmap/README.md +14 -0
- package/assets/docs/roadmap/_modelo-item.md +73 -0
- package/assets/orkmind-native-schema.json +32 -0
- package/assets/orkmind_bridge.py +357 -0
- package/assets/orkmind_fixture.py +67 -0
- package/assets/orkmind_prospective.py +138 -0
- package/assets/reference-tariffs-i07.json +23 -0
- package/dist/adapters/claude-bg.js +488 -39
- package/dist/adapters/codex-controller-sensor.js +449 -0
- package/dist/adapters/codex-controller-worker.js +361 -0
- package/dist/adapters/codex-controller.js +252 -0
- package/dist/adapters/codex-events.js +205 -0
- package/dist/adapters/codex-question.js +26 -0
- package/dist/adapters/codex-runner.js +133 -0
- package/dist/adapters/codex.js +394 -0
- package/dist/agents-md.js +84 -0
- package/dist/auditoria.js +11 -8
- package/dist/auditrun.js +5 -4
- package/dist/board.js +173 -22
- package/dist/branch-de-estado.js +136 -0
- package/dist/canarios-hitl.js +177 -0
- package/dist/canarios-i43.js +543 -0
- package/dist/canarios-pulse.js +147 -0
- package/dist/canarios-sensores.js +129 -0
- package/dist/canarios.js +111 -2
- package/dist/catalogo.js +9 -0
- package/dist/ci.js +216 -0
- package/dist/ciclos.js +2 -1
- package/dist/claim-lint.js +64 -0
- package/dist/claims.js +60 -0
- package/dist/company-brain-capture.js +195 -0
- package/dist/company-brain-cli.js +123 -0
- package/dist/company-brain-client.js +61 -0
- package/dist/company-brain-contract.js +166 -0
- package/dist/company-brain-journal.js +192 -0
- package/dist/company-brain-mcp.js +33 -0
- package/dist/company-brain-migration.js +60 -0
- package/dist/company-brain-source.js +177 -0
- package/dist/company-brain-worker.js +18 -0
- package/dist/conducao-texto.js +61 -0
- package/dist/conducao.js +876 -0
- package/dist/contrato-publico.js +39 -0
- package/dist/creation-operation-store.js +251 -0
- package/dist/creation-operation.js +148 -0
- package/dist/decisao-autonoma.js +183 -0
- package/dist/delegation.js +79 -0
- package/dist/demo.js +120 -0
- package/dist/docs.js +828 -0
- package/dist/doctor.js +183 -24
- package/dist/entrega-pr.js +117 -0
- package/dist/escopo-escrita.js +71 -0
- package/dist/estado-thread.js +222 -0
- package/dist/evalrunner.js +12 -0
- package/dist/fabrica-estado.js +329 -0
- package/dist/fabrica-publicar.js +76 -0
- package/dist/fix.js +21 -0
- package/dist/gates.js +55 -9
- package/dist/handoff.js +39 -23
- package/dist/hitl-canais.js +333 -0
- package/dist/hitl-classificacao.js +131 -0
- package/dist/hitl-contract.js +465 -0
- package/dist/hitl-estado.js +136 -0
- package/dist/hitl-gates.js +554 -0
- package/dist/hitl-ingress-receipt.js +382 -0
- package/dist/hitl-local-atestado.js +57 -0
- package/dist/hitl-local-receipt.js +328 -0
- package/dist/hitl-local.js +143 -0
- package/dist/hitl-lock.js +139 -0
- package/dist/hitl-lote.js +223 -0
- package/dist/hitl-native-offer.js +97 -0
- package/dist/hitl-native.js +65 -0
- package/dist/hitl-presentation.js +209 -0
- package/dist/hitl-public-receipt.js +176 -0
- package/dist/hitl-resumo.js +209 -0
- package/dist/hitl-sessions.js +475 -0
- package/dist/hitl.js +711 -0
- package/dist/horario.js +269 -0
- package/dist/hosts.js +198 -22
- package/dist/index.js +1736 -93
- package/dist/indice.js +99 -0
- package/dist/init.js +28 -3
- package/dist/integracoes-locais.js +17 -0
- package/dist/leases.js +65 -21
- package/dist/ledger-stats.js +272 -0
- package/dist/ledger.js +107 -2
- package/dist/licoes.js +215 -0
- package/dist/liveness.js +218 -0
- package/dist/maestro-actions.js +51 -0
- package/dist/maestro-authority.js +152 -0
- package/dist/maestro-cli.js +97 -0
- package/dist/maestro-contract.js +55 -0
- package/dist/maestro-discovery.js +132 -0
- package/dist/maestro-runtime.js +268 -0
- package/dist/maestro-snapshot.js +86 -0
- package/dist/maestro-sources.js +224 -0
- package/dist/manifest.js +165 -6
- package/dist/maquina.js +102 -0
- package/dist/master-audit.js +101 -0
- package/dist/master-batch.js +43 -0
- package/dist/master-digest.js +149 -0
- package/dist/master-migracao.js +155 -0
- package/dist/master.js +361 -77
- package/dist/mcp-artifacts.js +234 -0
- package/dist/mcp-git.js +433 -0
- package/dist/mcp-install.js +310 -0
- package/dist/mcp-maestro.js +25 -0
- package/dist/mcp-server.js +469 -0
- package/dist/mcp-ship.js +436 -0
- package/dist/mcp-verify.js +173 -0
- package/dist/memoria-humana.js +207 -0
- package/dist/memoria.js +314 -78
- package/dist/memory-migration.js +279 -0
- package/dist/memory-prospective.js +148 -0
- package/dist/modos-migracao.js +191 -0
- package/dist/modos.js +151 -15
- package/dist/monitor-lock.js +110 -0
- package/dist/objective.js +449 -0
- package/dist/ocupacao.js +267 -0
- package/dist/onboarding.js +303 -0
- package/dist/orkmind.js +389 -90
- package/dist/orquestracao.js +96 -69
- package/dist/phase.js +596 -150
- package/dist/playbook-capabilities.js +150 -0
- package/dist/playbook-contracts.js +188 -0
- package/dist/portfolio-context.js +61 -0
- package/dist/portfolio.js +180 -0
- package/dist/preflight.js +207 -0
- package/dist/process-audit.js +112 -0
- package/dist/project-state.js +93 -0
- package/dist/prompts.js +25 -5
- package/dist/prova-minima.js +112 -0
- package/dist/pulse-cadencia.js +163 -0
- package/dist/pulse-consentimento.js +260 -0
- package/dist/pulse-delivery.js +323 -0
- package/dist/pulse-resposta.js +542 -0
- package/dist/pulse.js +262 -0
- package/dist/ratelimit.js +35 -2
- package/dist/recall.js +80 -3
- package/dist/redacao-saida.js +45 -0
- package/dist/redacao-url.js +66 -0
- package/dist/retry.js +637 -115
- package/dist/roadmap-reservas.js +243 -0
- package/dist/runtime-ambiente.js +38 -0
- package/dist/runtime-context.js +215 -0
- package/dist/runtime-profiles.js +869 -0
- package/dist/runtimes.js +103 -0
- package/dist/sandbox.js +9 -0
- package/dist/session-events.js +203 -0
- package/dist/session-watcher-claude.js +637 -0
- package/dist/session-watcher.js +683 -0
- package/dist/sessoes-adopt.js +154 -0
- package/dist/sessoes-inventario.js +159 -0
- package/dist/sessoes.js +16 -35
- package/dist/setup.js +691 -0
- package/dist/ship.js +150 -15
- package/dist/slug.js +1 -1
- package/dist/thread-close.js +115 -0
- package/dist/thread.js +142 -28
- package/dist/tokens.js +1 -1
- package/dist/util.js +4 -2
- package/dist/verify-sandbox.js +265 -0
- package/dist/verify.js +270 -25
- package/dist/versao.js +60 -0
- package/dist/worktree.js +31 -2
- package/dist/write-activation.js +291 -0
- package/dist/yaml.js +2 -0
- package/eval/casos/onboarding.json +102 -0
- package/eval/casos/orkastery-bootstrap.json +35 -1
- package/eval/casos/scope-check-capability-map.json +45 -7
- package/eval/casos/ship-release.json +2 -2
- package/eval/fixtures/b0-slug-e-modos/caso.json +35 -22
- package/eval/fixtures/b2-master-log/caso.json +1 -1
- package/eval/fixtures/fx-adapter-editado-detectado/caso.json +19 -0
- package/eval/fixtures/fx-auto-quiet/caso.json +11 -0
- package/eval/fixtures/fx-blanket-approve/caso.json +10 -0
- package/eval/fixtures/fx-check-runtime-cruzado/caso.json +20 -0
- package/eval/fixtures/fx-codex-dry/caso.json +18 -0
- package/eval/fixtures/fx-donewhen-executavel/caso.json +15 -0
- package/eval/fixtures/fx-estado-dividido/caso.json +15 -0
- package/eval/fixtures/fx-fase-orfa/caso.json +116 -0
- package/eval/fixtures/fx-happy/caso.json +6 -4
- package/eval/fixtures/fx-hitl-latency/caso.json +20 -0
- package/eval/fixtures/fx-indice-reversao/caso.json +25 -0
- package/eval/fixtures/fx-listagem-abertas/caso.json +16 -0
- package/eval/fixtures/fx-maestro-bootstrap/caso.json +21 -0
- package/eval/fixtures/fx-modo-aposentado-escritor/caso.json +21 -0
- package/eval/fixtures/fx-modo-aposentado-leitor/caso.json +16 -0
- package/eval/fixtures/fx-objective-oscillation/caso.json +14 -0
- package/eval/fixtures/fx-omnicanal/caso.json +17 -0
- package/eval/fixtures/fx-sensores-runtime/caso.json +17 -0
- package/monitor/company-brain.cjs +17 -0
- package/monitor/pulse-scope.cjs +45 -0
- package/monitor/pulse.cron +18 -0
- package/monitor/varredura-pulse.sh +27 -0
- package/package.json +25 -4
- package/schemas/claims.schema.json +25 -0
- package/schemas/company-brain.schema.json +1158 -0
- package/schemas/creation-operation.schema.json +365 -0
- package/schemas/maestro-snapshot.schema.json +2716 -0
- package/skills/core/onboarding/SKILL.md +50 -0
- package/skills/core/orkastery-bootstrap/SKILL.md +95 -65
- package/skills/core/thread-state/SKILL.md +3 -1
- package/skills/governance/scope-check-capability-map/SKILL.md +17 -7
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.fingerprintCapacidade = fingerprintCapacidade;
|
|
4
|
+
exports.validarReciboCapacidade = validarReciboCapacidade;
|
|
5
|
+
exports.avaliarCapacidades = avaliarCapacidades;
|
|
6
|
+
exports.exigirCapacidadesRequeridas = exigirCapacidadesRequeridas;
|
|
7
|
+
/** Matriz I09 sem transporte, spawn, persistencia ou ativacao. */
|
|
8
|
+
const playbook_contracts_1 = require("./playbook-contracts");
|
|
9
|
+
const niveis = ['help', 'transporte', 'teste', 'homologacao'];
|
|
10
|
+
const versao = { type: 'integer', const: playbook_contracts_1.PLAYBOOK_VERSION };
|
|
11
|
+
const schemaContexto = (0, playbook_contracts_1.objetoFechado)({
|
|
12
|
+
runtime: { type: 'string', enum: ['codex', 'claude'] },
|
|
13
|
+
versaoRuntime: playbook_contracts_1.textoPlaybook, binarioSha256: playbook_contracts_1.textoPlaybook, transporte: playbook_contracts_1.textoPlaybook,
|
|
14
|
+
configSha256: playbook_contracts_1.textoPlaybook, schemaSha256: playbook_contracts_1.textoPlaybook, codigoSha256: playbook_contracts_1.textoPlaybook,
|
|
15
|
+
escopo: (0, playbook_contracts_1.objetoFechado)({
|
|
16
|
+
projeto: playbook_contracts_1.textoPlaybook, thread: playbook_contracts_1.textoPlaybook,
|
|
17
|
+
fase: { type: 'string', enum: ['GOAL', 'PLAN', 'GO', 'CHECK', 'SHIP', 'MASTER'] },
|
|
18
|
+
dispatchId: playbook_contracts_1.textoPlaybook, sessionId: playbook_contracts_1.textoPlaybook, base: playbook_contracts_1.textoPlaybook, head: playbook_contracts_1.textoPlaybook, prova: playbook_contracts_1.textoPlaybook,
|
|
19
|
+
}),
|
|
20
|
+
});
|
|
21
|
+
const schemaMatriz = (0, playbook_contracts_1.objetoFechado)({
|
|
22
|
+
schemaVersion: versao, contexto: schemaContexto,
|
|
23
|
+
capacidades: (0, playbook_contracts_1.listaPlaybook)((0, playbook_contracts_1.objetoFechado)({
|
|
24
|
+
id: playbook_contracts_1.textoPlaybook,
|
|
25
|
+
evidencias: (0, playbook_contracts_1.objetoFechado)(Object.fromEntries(niveis.map(n => [n, playbook_contracts_1.textoPlaybook])), []),
|
|
26
|
+
})),
|
|
27
|
+
});
|
|
28
|
+
const schemaRecibo = (0, playbook_contracts_1.objetoFechado)({
|
|
29
|
+
schemaVersion: versao, id: playbook_contracts_1.textoPlaybook, fingerprint: playbook_contracts_1.textoPlaybook, capacidade: playbook_contracts_1.textoPlaybook,
|
|
30
|
+
nivel: { type: 'string', enum: niveis },
|
|
31
|
+
origem: { type: 'string', enum: ['help', 'transporte', 'deterministica', 'nativa'] },
|
|
32
|
+
fonte: (0, playbook_contracts_1.objetoFechado)({ id: playbook_contracts_1.textoPlaybook, sha256: playbook_contracts_1.textoPlaybook }),
|
|
33
|
+
comando: (0, playbook_contracts_1.listaPlaybook)(playbook_contracts_1.textoPlaybook, 1), saida: { type: 'string', maxLength: 131072 },
|
|
34
|
+
inicio: playbook_contracts_1.textoPlaybook, fim: playbook_contracts_1.textoPlaybook,
|
|
35
|
+
resultado: (0, playbook_contracts_1.objetoFechado)({
|
|
36
|
+
estado: { type: 'string', enum: ['aprovado', 'reprovado', 'unavailable'] },
|
|
37
|
+
exitCode: { type: 'integer' }, esperados: (0, playbook_contracts_1.listaPlaybook)(playbook_contracts_1.textoPlaybook),
|
|
38
|
+
executados: (0, playbook_contracts_1.listaPlaybook)(playbook_contracts_1.textoPlaybook), aprovados: (0, playbook_contracts_1.listaPlaybook)(playbook_contracts_1.textoPlaybook),
|
|
39
|
+
skipped: { type: 'integer' }, todo: { type: 'integer' },
|
|
40
|
+
}),
|
|
41
|
+
});
|
|
42
|
+
function exigir(condicao, campo, detalhe) {
|
|
43
|
+
if (!condicao)
|
|
44
|
+
throw new playbook_contracts_1.ContratoPlaybookInvalido(campo, detalhe);
|
|
45
|
+
}
|
|
46
|
+
function sha256(valor, campo) {
|
|
47
|
+
exigir(/^[a-f0-9]{64}$/.test(valor), campo, 'SHA-256 invalido');
|
|
48
|
+
}
|
|
49
|
+
function nomesUnicos(valores, campo) {
|
|
50
|
+
exigir(valores.every(v => v.trim().length > 0), campo, 'nome vazio');
|
|
51
|
+
exigir(new Set(valores).size === valores.length, campo, 'nome repetido');
|
|
52
|
+
}
|
|
53
|
+
function fingerprintCapacidade(contexto) {
|
|
54
|
+
(0, playbook_contracts_1.validarContratoPlaybook)(contexto, schemaContexto);
|
|
55
|
+
for (const k of ['binarioSha256', 'configSha256', 'schemaSha256', 'codigoSha256'])
|
|
56
|
+
sha256(contexto[k], k);
|
|
57
|
+
for (const k of ['base', 'head'])
|
|
58
|
+
exigir(/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/.test(contexto.escopo[k]), k, 'commit invalido');
|
|
59
|
+
exigir(Object.values(contexto.escopo).every(v => v.trim().length > 0) &&
|
|
60
|
+
contexto.versaoRuntime.trim().length > 0 && contexto.transporte.trim().length > 0, 'contexto', 'identidade vazia');
|
|
61
|
+
return (0, playbook_contracts_1.hashJsonPlaybook)({ schemaVersion: playbook_contracts_1.PLAYBOOK_VERSION, contexto });
|
|
62
|
+
}
|
|
63
|
+
function validarReciboCapacidade(valor) {
|
|
64
|
+
(0, playbook_contracts_1.validarContratoPlaybook)(valor, schemaRecibo);
|
|
65
|
+
const r = valor;
|
|
66
|
+
sha256(r.fingerprint, 'fingerprint');
|
|
67
|
+
sha256(r.fonte.sha256, 'fonte.sha256');
|
|
68
|
+
for (const campo of ['inicio', 'fim']) {
|
|
69
|
+
const instante = Date.parse(r[campo]);
|
|
70
|
+
exigir(Number.isFinite(instante) && new Date(instante).toISOString() === r[campo], campo, 'instante UTC invalido');
|
|
71
|
+
}
|
|
72
|
+
exigir(Date.parse(r.fim) >= Date.parse(r.inicio), 'fim', 'recibo termina antes de iniciar');
|
|
73
|
+
nomesUnicos([r.id], 'id');
|
|
74
|
+
nomesUnicos([r.capacidade], 'capacidade');
|
|
75
|
+
nomesUnicos([r.fonte.id], 'fonte.id');
|
|
76
|
+
const resultado = r.resultado;
|
|
77
|
+
for (const k of ['esperados', 'executados', 'aprovados'])
|
|
78
|
+
nomesUnicos(resultado[k], `resultado.${k}`);
|
|
79
|
+
exigir(resultado.exitCode >= 0 && resultado.skipped >= 0 && resultado.todo >= 0, 'resultado', 'contagem negativa');
|
|
80
|
+
exigir(resultado.aprovados.every(c => resultado.executados.includes(c)) &&
|
|
81
|
+
resultado.executados.every(c => resultado.esperados.includes(c)), 'resultado', 'casos sem correspondencia');
|
|
82
|
+
const origens = {
|
|
83
|
+
help: ['help'], transporte: ['transporte'], teste: ['deterministica', 'nativa'], homologacao: ['nativa'],
|
|
84
|
+
};
|
|
85
|
+
exigir(origens[r.nivel].includes(r.origem), 'origem', 'origem incompativel com nivel');
|
|
86
|
+
return r;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* O consumidor Ork fornece contexto ATUAL e resolve fontes externas exatas, verificadas
|
|
90
|
+
* independentemente. Nunca construir resolverRecibo a partir do JSON do candidato.
|
|
91
|
+
* Ausencia de resolver ou de fonte nao concede selo. T07/T20 implementarao essa fronteira.
|
|
92
|
+
*/
|
|
93
|
+
function avaliarCapacidades(candidato, contextoAtual, requeridas, resolverRecibo) {
|
|
94
|
+
(0, playbook_contracts_1.validarContratoPlaybook)(candidato, schemaMatriz);
|
|
95
|
+
const matriz = candidato;
|
|
96
|
+
const fingerprint = fingerprintCapacidade(contextoAtual);
|
|
97
|
+
exigir(fingerprintCapacidade(matriz.contexto) === fingerprint, 'contexto', 'fingerprint atual divergente');
|
|
98
|
+
(0, playbook_contracts_1.validarContratoPlaybook)(requeridas, (0, playbook_contracts_1.listaPlaybook)(playbook_contracts_1.textoPlaybook));
|
|
99
|
+
nomesUnicos(requeridas, 'requeridas');
|
|
100
|
+
nomesUnicos(matriz.capacidades.map(c => c.id), 'capacidades');
|
|
101
|
+
const capacidades = matriz.capacidades.map(c => {
|
|
102
|
+
const provas = {};
|
|
103
|
+
const motivos = [];
|
|
104
|
+
let reprovada = false;
|
|
105
|
+
let unavailable = false;
|
|
106
|
+
for (const nivel of niveis) {
|
|
107
|
+
const id = c.evidencias[nivel];
|
|
108
|
+
if (id === undefined)
|
|
109
|
+
continue;
|
|
110
|
+
const fonte = resolverRecibo?.(id);
|
|
111
|
+
if (fonte === undefined || fonte === null) {
|
|
112
|
+
motivos.push(`${nivel}: fonte externa ausente`);
|
|
113
|
+
unavailable = true;
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
const recibo = validarReciboCapacidade(fonte);
|
|
117
|
+
exigir(recibo.id === id && recibo.capacidade === c.id && recibo.nivel === nivel, nivel, 'recibo de outra capacidade ou nivel');
|
|
118
|
+
exigir(recibo.fingerprint === fingerprint, nivel, 'recibo de outro fingerprint');
|
|
119
|
+
const r = recibo.resultado;
|
|
120
|
+
const casosCompletos = r.esperados.length > 0 && r.esperados.length === r.executados.length &&
|
|
121
|
+
r.executados.length === r.aprovados.length && r.skipped === 0 && r.todo === 0;
|
|
122
|
+
provas[nivel] = r.estado === 'aprovado' && r.exitCode === 0 && casosCompletos;
|
|
123
|
+
if (!provas[nivel]) {
|
|
124
|
+
motivos.push(`${nivel}: ${r.estado}, casos completos=${casosCompletos}, exitCode=${r.exitCode}`);
|
|
125
|
+
if (r.estado === 'unavailable')
|
|
126
|
+
unavailable = true;
|
|
127
|
+
else
|
|
128
|
+
reprovada = true;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
const homologada = provas.transporte === true && provas.teste === true && provas.homologacao === true &&
|
|
132
|
+
!reprovada && !unavailable;
|
|
133
|
+
return {
|
|
134
|
+
id: c.id, observadaNoHelp: provas.help === true, disponivelNoTransporte: provas.transporte === true,
|
|
135
|
+
testada: provas.teste === true, homologada,
|
|
136
|
+
estado: reprovada ? 'reprovada' : unavailable ? 'unavailable' :
|
|
137
|
+
homologada ? 'homologada' : 'pendente',
|
|
138
|
+
motivos,
|
|
139
|
+
};
|
|
140
|
+
});
|
|
141
|
+
const requeridasSemDemonstracao = requeridas.filter(id => !capacidades.some(c => c.id === id && c.homologada));
|
|
142
|
+
return { schemaVersion: playbook_contracts_1.PLAYBOOK_VERSION, fingerprint, capacidades,
|
|
143
|
+
requeridasAtendidas: requeridas.length > 0 && requeridasSemDemonstracao.length === 0,
|
|
144
|
+
requeridasSemDemonstracao };
|
|
145
|
+
}
|
|
146
|
+
function exigirCapacidadesRequeridas(...parametros) {
|
|
147
|
+
const matriz = avaliarCapacidades(...parametros);
|
|
148
|
+
exigir(matriz.requeridasAtendidas, 'requeridas', `sem demonstracao: ${matriz.requeridasSemDemonstracao.join(', ') || 'coorte vazia'}`);
|
|
149
|
+
return matriz;
|
|
150
|
+
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.textoPlaybook = exports.ContratoPlaybookInvalido = exports.MAX_RESULT_BYTES = exports.JSON_SCHEMA_DIALECT = exports.PLAYBOOK_VERSION = void 0;
|
|
4
|
+
exports.validarSchemaPlaybook = validarSchemaPlaybook;
|
|
5
|
+
exports.validarContratoPlaybook = validarContratoPlaybook;
|
|
6
|
+
exports.hashJsonPlaybook = hashJsonPlaybook;
|
|
7
|
+
exports.objetoFechado = objetoFechado;
|
|
8
|
+
exports.listaPlaybook = listaPlaybook;
|
|
9
|
+
exports.validarResultadoFasePlaybook = validarResultadoFasePlaybook;
|
|
10
|
+
/** Contratos puros I09. Validar formato nao concede autoridade nem executa verify. */
|
|
11
|
+
const node_crypto_1 = require("node:crypto");
|
|
12
|
+
exports.PLAYBOOK_VERSION = 1;
|
|
13
|
+
exports.JSON_SCHEMA_DIALECT = 'https://json-schema.org/draft/2020-12/schema';
|
|
14
|
+
exports.MAX_RESULT_BYTES = 256 * 1024;
|
|
15
|
+
class ContratoPlaybookInvalido extends Error {
|
|
16
|
+
campo;
|
|
17
|
+
constructor(campo, detalhe) {
|
|
18
|
+
super(`${campo}: ${detalhe}`);
|
|
19
|
+
this.campo = campo;
|
|
20
|
+
this.name = 'ContratoPlaybookInvalido';
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
exports.ContratoPlaybookInvalido = ContratoPlaybookInvalido;
|
|
24
|
+
function exigir(condicao, campo, detalhe) {
|
|
25
|
+
if (!condicao)
|
|
26
|
+
throw new ContratoPlaybookInvalido(campo, detalhe);
|
|
27
|
+
}
|
|
28
|
+
function objeto(valor) {
|
|
29
|
+
return valor !== null && typeof valor === 'object' && !Array.isArray(valor) &&
|
|
30
|
+
[Object.prototype, null].includes(Object.getPrototypeOf(valor));
|
|
31
|
+
}
|
|
32
|
+
/** Somente dados JSON, sem getters, prototipos customizados, ciclos ou arrays esparsos. */
|
|
33
|
+
function exigirJson(valor) {
|
|
34
|
+
const ativos = new Set();
|
|
35
|
+
let nos = 0;
|
|
36
|
+
function visitar(v, profundidade) {
|
|
37
|
+
exigir(++nos <= exports.MAX_RESULT_BYTES && profundidade <= 32, '$', 'JSON excede limite estrutural');
|
|
38
|
+
if (v === null || typeof v === 'string' || typeof v === 'boolean')
|
|
39
|
+
return;
|
|
40
|
+
if (typeof v === 'number') {
|
|
41
|
+
exigir(Number.isFinite(v), '$', 'numero JSON invalido');
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
exigir(Array.isArray(v) || objeto(v), '$', 'esperado dado JSON simples');
|
|
45
|
+
exigir(!ativos.has(v), '$', 'ciclo JSON');
|
|
46
|
+
ativos.add(v);
|
|
47
|
+
const chaves = Reflect.ownKeys(v);
|
|
48
|
+
if (Array.isArray(v)) {
|
|
49
|
+
exigir(chaves.length === v.length + 1, '$', 'array esparso ou com campos extras');
|
|
50
|
+
}
|
|
51
|
+
for (const chave of chaves) {
|
|
52
|
+
if (Array.isArray(v) && chave === 'length')
|
|
53
|
+
continue;
|
|
54
|
+
exigir(typeof chave === 'string', '$', 'chave nao JSON');
|
|
55
|
+
const d = Object.getOwnPropertyDescriptor(v, chave);
|
|
56
|
+
exigir(d.enumerable && 'value' in d, '$', 'campo nao JSON');
|
|
57
|
+
if (Array.isArray(v))
|
|
58
|
+
exigir(/^(0|[1-9][0-9]*)$/.test(chave) && Number(chave) < v.length, '$', 'indice invalido');
|
|
59
|
+
visitar(d.value, profundidade + 1);
|
|
60
|
+
}
|
|
61
|
+
ativos.delete(v);
|
|
62
|
+
}
|
|
63
|
+
visitar(valor, 0);
|
|
64
|
+
exigir(Buffer.byteLength(JSON.stringify(valor), 'utf8') <= exports.MAX_RESULT_BYTES, '$', 'JSON excede 256 KiB');
|
|
65
|
+
}
|
|
66
|
+
function primitivo(v) {
|
|
67
|
+
return v === null || typeof v === 'string' || typeof v === 'boolean' ||
|
|
68
|
+
(typeof v === 'number' && Number.isSafeInteger(v));
|
|
69
|
+
}
|
|
70
|
+
/** Subconjunto fechado. $ref, combinadores, pattern e keywords desconhecidas falham. */
|
|
71
|
+
function validarSchemaPlaybook(schema) {
|
|
72
|
+
exigirJson(schema);
|
|
73
|
+
function visitar(s, campo, nivel) {
|
|
74
|
+
exigir(objeto(s), campo, 'schema deve ser objeto');
|
|
75
|
+
exigir(nivel <= 24, campo, 'schema profundo demais');
|
|
76
|
+
const porTipo = {
|
|
77
|
+
object: ['properties', 'required', 'additionalProperties'],
|
|
78
|
+
array: ['items', 'minItems', 'maxItems'],
|
|
79
|
+
string: ['minLength', 'maxLength'],
|
|
80
|
+
integer: [], boolean: [], null: [],
|
|
81
|
+
};
|
|
82
|
+
exigir(typeof s.type === 'string' && Object.hasOwn(porTipo, s.type), campo, 'tipo nao suportado');
|
|
83
|
+
const permitidas = ['type', 'const', 'enum', ...porTipo[s.type]];
|
|
84
|
+
if (nivel === 0)
|
|
85
|
+
permitidas.push('$schema');
|
|
86
|
+
for (const chave of Object.keys(s))
|
|
87
|
+
exigir(permitidas.includes(chave), `${campo}.${chave}`, 'keyword nao suportada');
|
|
88
|
+
if (Object.hasOwn(s, '$schema'))
|
|
89
|
+
exigir(s.$schema === exports.JSON_SCHEMA_DIALECT, campo, 'dialeto nao suportado');
|
|
90
|
+
if (Object.hasOwn(s, 'const'))
|
|
91
|
+
exigir(primitivo(s.const), campo, 'const deve ser primitivo');
|
|
92
|
+
if (Object.hasOwn(s, 'enum')) {
|
|
93
|
+
exigir(Array.isArray(s.enum) && s.enum.length > 0 && s.enum.every(primitivo), campo, 'enum invalido');
|
|
94
|
+
exigir(new Set(s.enum).size === s.enum.length, campo, 'enum repetido');
|
|
95
|
+
}
|
|
96
|
+
for (const [min, max] of [['minLength', 'maxLength'], ['minItems', 'maxItems']]) {
|
|
97
|
+
for (const k of [min, max])
|
|
98
|
+
if (Object.hasOwn(s, k)) {
|
|
99
|
+
exigir(Number.isSafeInteger(s[k]) && s[k] >= 0, campo, `${k} invalido`);
|
|
100
|
+
}
|
|
101
|
+
if (Object.hasOwn(s, min) && Object.hasOwn(s, max))
|
|
102
|
+
exigir(s[min] <= s[max], campo, 'limites invertidos');
|
|
103
|
+
}
|
|
104
|
+
if (s.type === 'object') {
|
|
105
|
+
exigir(objeto(s.properties) && s.additionalProperties === false && Array.isArray(s.required), campo, 'objeto deve ser fechado');
|
|
106
|
+
exigir(s.required.every(k => typeof k === 'string' && Object.hasOwn(s.properties, k)), campo, 'required desconhecido');
|
|
107
|
+
exigir(new Set(s.required).size === s.required.length, campo, 'required repetido');
|
|
108
|
+
for (const [k, filho] of Object.entries(s.properties))
|
|
109
|
+
visitar(filho, `${campo}.properties.${k}`, nivel + 1);
|
|
110
|
+
}
|
|
111
|
+
if (s.type === 'array')
|
|
112
|
+
visitar(s.items, `${campo}.items`, nivel + 1);
|
|
113
|
+
// Nao aceitar restricoes impossiveis como const de outro tipo ou fora do enum.
|
|
114
|
+
const valores = Object.hasOwn(s, 'const') ? [s.const] : s.enum ?? [];
|
|
115
|
+
for (const v of valores)
|
|
116
|
+
validarValor(v, s, campo);
|
|
117
|
+
}
|
|
118
|
+
visitar(schema, '$schema', 0);
|
|
119
|
+
}
|
|
120
|
+
function validarValor(v, s, campo) {
|
|
121
|
+
const tipoOk = s.type === 'object' ? objeto(v) : s.type === 'array' ? Array.isArray(v) :
|
|
122
|
+
s.type === 'null' ? v === null : s.type === 'integer' ? Number.isSafeInteger(v) : typeof v === s.type;
|
|
123
|
+
exigir(tipoOk, campo, `tipo esperado: ${s.type}`);
|
|
124
|
+
if (Object.hasOwn(s, 'const'))
|
|
125
|
+
exigir(v === s.const, campo, 'const divergente');
|
|
126
|
+
if (Array.isArray(s.enum))
|
|
127
|
+
exigir(s.enum.includes(v), campo, 'valor fora do enum');
|
|
128
|
+
if (typeof v === 'string' || Array.isArray(v)) {
|
|
129
|
+
const tamanho = typeof v === 'string' ? [...v].length : v.length;
|
|
130
|
+
const min = s[typeof v === 'string' ? 'minLength' : 'minItems'];
|
|
131
|
+
const max = s[typeof v === 'string' ? 'maxLength' : 'maxItems'];
|
|
132
|
+
exigir(min === undefined || tamanho >= min, campo, 'tamanho abaixo do minimo');
|
|
133
|
+
exigir(max === undefined || tamanho <= max, campo, 'tamanho acima do maximo');
|
|
134
|
+
}
|
|
135
|
+
if (objeto(v)) {
|
|
136
|
+
const props = s.properties;
|
|
137
|
+
for (const k of s.required)
|
|
138
|
+
exigir(Object.hasOwn(v, k), `${campo}.${k}`, 'campo obrigatorio ausente');
|
|
139
|
+
for (const k of Object.keys(v)) {
|
|
140
|
+
exigir(Object.hasOwn(props, k), `${campo}.${k}`, 'campo desconhecido');
|
|
141
|
+
validarValor(v[k], props[k], `${campo}.${k}`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
if (Array.isArray(v))
|
|
145
|
+
v.forEach((item, i) => validarValor(item, s.items, `${campo}[${i}]`));
|
|
146
|
+
}
|
|
147
|
+
function validarContratoPlaybook(valor, schema) {
|
|
148
|
+
validarSchemaPlaybook(schema);
|
|
149
|
+
exigirJson(valor);
|
|
150
|
+
validarValor(valor, schema, '$');
|
|
151
|
+
}
|
|
152
|
+
/** Canonizacao independente da ordem de propriedades; arrays preservam sua ordem. */
|
|
153
|
+
function hashJsonPlaybook(valor) {
|
|
154
|
+
exigirJson(valor);
|
|
155
|
+
function canonico(v) {
|
|
156
|
+
if (Array.isArray(v))
|
|
157
|
+
return `[${v.map(canonico).join(',')}]`;
|
|
158
|
+
if (objeto(v))
|
|
159
|
+
return `{${Object.keys(v).sort().map(k => `${JSON.stringify(k)}:${canonico(v[k])}`).join(',')}}`;
|
|
160
|
+
return JSON.stringify(v);
|
|
161
|
+
}
|
|
162
|
+
return (0, node_crypto_1.createHash)('sha256').update(canonico(valor)).digest('hex');
|
|
163
|
+
}
|
|
164
|
+
exports.textoPlaybook = { type: 'string', minLength: 1, maxLength: 4096 };
|
|
165
|
+
function objetoFechado(properties, required = Object.keys(properties)) {
|
|
166
|
+
return { type: 'object', properties, required, additionalProperties: false };
|
|
167
|
+
}
|
|
168
|
+
function listaPlaybook(items, minItems = 0) {
|
|
169
|
+
return { type: 'array', items, minItems, maxItems: 256 };
|
|
170
|
+
}
|
|
171
|
+
const schemaFase = objetoFechado({
|
|
172
|
+
schemaVersion: { type: 'integer', const: exports.PLAYBOOK_VERSION },
|
|
173
|
+
fase: { type: 'string', enum: ['GOAL', 'PLAN', 'GO', 'CHECK', 'SHIP', 'MASTER'] },
|
|
174
|
+
slug: exports.textoPlaybook, sessionId: exports.textoPlaybook, dispatchId: exports.textoPlaybook,
|
|
175
|
+
estado: { type: 'string', enum: ['concluida', 'falhou', 'interrompida'] },
|
|
176
|
+
reciboId: exports.textoPlaybook,
|
|
177
|
+
resultado: objetoFechado({
|
|
178
|
+
produtoHomologado: { type: 'boolean' }, GOIntegralConcluido: { type: 'boolean' }, checkpoint: exports.textoPlaybook,
|
|
179
|
+
}),
|
|
180
|
+
});
|
|
181
|
+
/** Apenas contrato e correlacao. T06 consumira a prova terminal externa de I04. */
|
|
182
|
+
function validarResultadoFasePlaybook(valor, esperado) {
|
|
183
|
+
validarContratoPlaybook(valor, schemaFase);
|
|
184
|
+
const r = valor;
|
|
185
|
+
for (const k of ['fase', 'slug', 'sessionId', 'dispatchId'])
|
|
186
|
+
exigir(r[k] === esperado[k], k, 'despacho divergente');
|
|
187
|
+
return r;
|
|
188
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.inspectPortfolio = inspectPortfolio;
|
|
4
|
+
/** Inspeção por identidade: o estado do catálogo não é inferido de ciclos. */
|
|
5
|
+
const portfolio_1 = require("./portfolio");
|
|
6
|
+
const objective_1 = require("./objective");
|
|
7
|
+
const statusLabels = {
|
|
8
|
+
awaiting_approval: 'Aguardando aprovação', running: 'Em andamento', paused: 'Pausado', validated: 'Validado no ciclo', stopped: 'Interrompido',
|
|
9
|
+
};
|
|
10
|
+
function inspectPortfolio(root, id, readCycles = objective_1.listObjectives) {
|
|
11
|
+
if (!/^(prod|proj|init)-[a-z0-9][a-z0-9-]{2,47}$/.test(id))
|
|
12
|
+
throw new Error('portfolio.id: identidade inválida');
|
|
13
|
+
const entity = (0, portfolio_1.findEntity)(root, id);
|
|
14
|
+
if (!entity)
|
|
15
|
+
throw new Error('portfolio.unavailable: entidade indisponível');
|
|
16
|
+
const portfolio = (0, portfolio_1.readPortfolio)(root);
|
|
17
|
+
const parentId = entity.kind === 'project' ? entity.productId : entity.kind === 'initiative' ? entity.projectId : null;
|
|
18
|
+
const parent = parentId ? (0, portfolio_1.findEntity)(root, parentId) : null;
|
|
19
|
+
const result = {
|
|
20
|
+
schema: 'ork.portfolio-context/v1', entity, parent,
|
|
21
|
+
origin: { authority: 'ork.portfolio', schema: portfolio.schema, reference: `portfolio:${entity.id}`, catalogUpdatedAt: portfolio.updatedAt, observedAt: new Date().toISOString(), creationOperationId: entity.creationOperationId ?? null },
|
|
22
|
+
cycles: [], cyclesState: 'available', cyclesMessage: 'Nenhum ciclo vinculado',
|
|
23
|
+
gaps: ['delivery.receipt_not_loaded'], conflicts: [], references: parentId ? [{ kind: 'entity', id: parentId }] : [],
|
|
24
|
+
};
|
|
25
|
+
if (!entity.creationOperationId)
|
|
26
|
+
result.gaps.push('origin.legacy');
|
|
27
|
+
if (parentId && !parent)
|
|
28
|
+
result.gaps.push('parent.unavailable');
|
|
29
|
+
try {
|
|
30
|
+
for (const cycle of readCycles(root)) {
|
|
31
|
+
const scope = cycle.envelope.portfolio;
|
|
32
|
+
if (!scope)
|
|
33
|
+
continue;
|
|
34
|
+
const matches = entity.kind === 'product' ? scope.productId === entity.id
|
|
35
|
+
: entity.kind === 'project' ? scope.projectId === entity.id
|
|
36
|
+
: scope.projectId === entity.projectId && (scope.delivery === 'project' || scope.initiativeIds.includes(entity.id));
|
|
37
|
+
if (!matches)
|
|
38
|
+
continue;
|
|
39
|
+
const relation = entity.kind === 'product' || (entity.kind === 'initiative' && scope.delivery === 'project') ? 'project_context'
|
|
40
|
+
: scope.delivery === 'project' ? 'project_delivery' : 'initiative_delivery';
|
|
41
|
+
result.cycles.push({ id: cycle.id, title: cycle.title, status: cycle.status, statusLabel: statusLabels[cycle.status] ?? 'Estado não confirmado', relation,
|
|
42
|
+
projectId: scope.projectId, initiativeIds: [...scope.initiativeIds], updatedAt: cycle.updatedAt, envelopeHash: cycle.envelope.hash, entityDeliveryProven: false });
|
|
43
|
+
result.references.push({ kind: 'objective', id: cycle.id });
|
|
44
|
+
if (entity.status === 'delivered' && ['running', 'paused', 'awaiting_approval'].includes(cycle.status)) {
|
|
45
|
+
result.conflicts.push({ code: 'entity.delivered_cycle_open', cycleId: cycle.id });
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
result.cycles.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt) || a.id.localeCompare(b.id));
|
|
49
|
+
if (result.cycles.length)
|
|
50
|
+
result.cyclesMessage = 'Ciclos vinculados; escopo e estado independentes do catálogo';
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
result.cycles = [];
|
|
54
|
+
result.conflicts = [];
|
|
55
|
+
result.references = result.references.filter((reference) => reference.kind === 'entity');
|
|
56
|
+
result.cyclesState = 'unavailable';
|
|
57
|
+
result.cyclesMessage = 'Consulta de ciclos indisponível';
|
|
58
|
+
result.gaps.push('cycles.unavailable');
|
|
59
|
+
}
|
|
60
|
+
return result;
|
|
61
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
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.readPortfolio = readPortfolio;
|
|
37
|
+
exports.findEntity = findEntity;
|
|
38
|
+
exports.createProduct = createProduct;
|
|
39
|
+
exports.createProject = createProject;
|
|
40
|
+
exports.createInitiative = createInitiative;
|
|
41
|
+
exports.createOperationEntity = createOperationEntity;
|
|
42
|
+
exports.listEntities = listEntities;
|
|
43
|
+
exports.assertCycleScope = assertCycleScope;
|
|
44
|
+
/** Catálogo determinístico de produto -> projeto -> iniciativa. */
|
|
45
|
+
const fs = __importStar(require("node:fs"));
|
|
46
|
+
const project_state_1 = require("./project-state");
|
|
47
|
+
const company_brain_journal_1 = require("./company-brain-journal");
|
|
48
|
+
const util_1 = require("./util");
|
|
49
|
+
const creation_operation_store_1 = require("./creation-operation-store");
|
|
50
|
+
const ID = /^(prod|proj|init)-[a-z0-9][a-z0-9-]{2,47}$/;
|
|
51
|
+
const PREFIX = { product: 'prod-', project: 'proj-', initiative: 'init-' };
|
|
52
|
+
const statuses = ['idea', 'planned', 'ready', 'in_progress', 'validating', 'delivered', 'blocked', 'cancelled'];
|
|
53
|
+
const unique = (values = []) => [...new Set(values.map((v) => v.trim()).filter(Boolean))];
|
|
54
|
+
function file(root) { return (0, project_state_1.stateFile)(root, 'portfolio.json'); }
|
|
55
|
+
function empty() { return { schema: 'ork.portfolio/v1', updatedAt: (0, util_1.agora)(), products: [], projects: [], initiatives: [] }; }
|
|
56
|
+
function readPortfolio(root) {
|
|
57
|
+
const target = file(root);
|
|
58
|
+
if (!fs.existsSync(target))
|
|
59
|
+
return empty();
|
|
60
|
+
const value = (0, util_1.lerJson)(target);
|
|
61
|
+
if (value.schema !== 'ork.portfolio/v1' || !Array.isArray(value.products) || !Array.isArray(value.projects) || !Array.isArray(value.initiatives)) {
|
|
62
|
+
throw new Error('portfolio inválido');
|
|
63
|
+
}
|
|
64
|
+
return value;
|
|
65
|
+
}
|
|
66
|
+
function persist(root, value) { value.updatedAt = (0, util_1.agora)(); (0, company_brain_journal_1.replaceWithJournal)(file(root), value); return value; }
|
|
67
|
+
function mutatePortfolio(root, run) {
|
|
68
|
+
return (0, creation_operation_store_1.withCreationLock)(root, 'portfolio', () => (0, company_brain_journal_1.portfolioMutation)(file(root), run));
|
|
69
|
+
}
|
|
70
|
+
function validateBase(kind, input) {
|
|
71
|
+
if (!ID.test(input.id) || !input.id.startsWith(PREFIX[kind]))
|
|
72
|
+
throw new Error(`${kind} exige id ${PREFIX[kind]}...`);
|
|
73
|
+
if (!input.title.trim() || input.title.length > 160)
|
|
74
|
+
throw new Error('título deve ter entre 1 e 160 caracteres');
|
|
75
|
+
if (input.status && !statuses.includes(input.status))
|
|
76
|
+
throw new Error('status de portfólio inválido');
|
|
77
|
+
}
|
|
78
|
+
function findEntity(root, id) {
|
|
79
|
+
const p = readPortfolio(root);
|
|
80
|
+
return [...p.products, ...p.projects, ...p.initiatives].find((item) => item.id === id) ?? null;
|
|
81
|
+
}
|
|
82
|
+
function createProduct(root, input) {
|
|
83
|
+
return mutatePortfolio(root, () => {
|
|
84
|
+
validateBase('product', input);
|
|
85
|
+
if (findEntity(root, input.id))
|
|
86
|
+
throw new Error(`entidade ${input.id} já existe`);
|
|
87
|
+
const product = { kind: 'product', id: input.id, title: input.title.trim(), description: input.description?.trim() ?? '', ownerId: input.ownerId?.trim() || null, status: input.status ?? 'idea', acceptanceCriteria: unique(input.acceptanceCriteria), version: 1 };
|
|
88
|
+
const p = readPortfolio(root);
|
|
89
|
+
p.products.push(product);
|
|
90
|
+
persist(root, p);
|
|
91
|
+
return product;
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
function createProject(root, input) {
|
|
95
|
+
return mutatePortfolio(root, () => createProjectLocked(root, input));
|
|
96
|
+
}
|
|
97
|
+
function createProjectLocked(root, input, operationId) {
|
|
98
|
+
validateBase('project', input);
|
|
99
|
+
if (findEntity(root, input.id))
|
|
100
|
+
throw new Error(`entidade ${input.id} já existe`);
|
|
101
|
+
const parent = findEntity(root, input.productId);
|
|
102
|
+
if (!parent || parent.kind !== 'product')
|
|
103
|
+
throw new Error(`produto ${input.productId} não encontrado`);
|
|
104
|
+
const project = { kind: 'project', id: input.id, productId: input.productId, title: input.title.trim(), description: input.description?.trim() ?? '', ownerId: input.ownerId?.trim() || null, status: input.status ?? 'idea', workspaceIds: unique(input.workspaceIds), acceptanceCriteria: unique(input.acceptanceCriteria), version: 1 };
|
|
105
|
+
if (operationId)
|
|
106
|
+
Object.assign(project, { creationOperationId: operationId });
|
|
107
|
+
const p = readPortfolio(root);
|
|
108
|
+
p.projects.push(project);
|
|
109
|
+
persist(root, p);
|
|
110
|
+
return project;
|
|
111
|
+
}
|
|
112
|
+
function createInitiative(root, input) {
|
|
113
|
+
return mutatePortfolio(root, () => createInitiativeLocked(root, input));
|
|
114
|
+
}
|
|
115
|
+
function createInitiativeLocked(root, input, operationId) {
|
|
116
|
+
validateBase('initiative', input);
|
|
117
|
+
if (findEntity(root, input.id))
|
|
118
|
+
throw new Error(`entidade ${input.id} já existe`);
|
|
119
|
+
const parent = findEntity(root, input.projectId);
|
|
120
|
+
if (!parent || parent.kind !== 'project')
|
|
121
|
+
throw new Error(`projeto ${input.projectId} não encontrado`);
|
|
122
|
+
const dependencies = unique(input.dependsOn);
|
|
123
|
+
for (const id of dependencies) {
|
|
124
|
+
const dependency = findEntity(root, id);
|
|
125
|
+
if (!dependency || dependency.kind !== 'initiative')
|
|
126
|
+
throw new Error(`dependência ${id} não encontrada`);
|
|
127
|
+
if (dependency.projectId !== input.projectId)
|
|
128
|
+
throw new Error('dependências devem pertencer ao mesmo projeto');
|
|
129
|
+
}
|
|
130
|
+
const initiative = { kind: 'initiative', id: input.id, projectId: input.projectId, title: input.title.trim(), description: input.description?.trim() ?? '', ownerId: input.ownerId?.trim() || null, status: input.status ?? 'idea', dependsOn: dependencies, acceptanceCriteria: unique(input.acceptanceCriteria), version: 1 };
|
|
131
|
+
if (operationId)
|
|
132
|
+
Object.assign(initiative, { creationOperationId: operationId });
|
|
133
|
+
const p = readPortfolio(root);
|
|
134
|
+
p.initiatives.push(initiative);
|
|
135
|
+
persist(root, p);
|
|
136
|
+
return initiative;
|
|
137
|
+
}
|
|
138
|
+
/** CAS do pai e origem do efeito são persistidos sob o mesmo lock dos escritores legados. */
|
|
139
|
+
function createOperationEntity(root, operationId, principal) {
|
|
140
|
+
return mutatePortfolio(root, () => {
|
|
141
|
+
const op = (0, creation_operation_store_1.readCreationOperation)(root, operationId, principal);
|
|
142
|
+
const input = op.request.entity;
|
|
143
|
+
if (!input || ['compensating', 'compensated', 'compensation_failed'].includes(op.state))
|
|
144
|
+
throw new Error('creation.conflict: ação não cria entidade');
|
|
145
|
+
const parent = findEntity(root, input.parentId);
|
|
146
|
+
if (!parent || parent.version !== op.request.expectedParentVersion)
|
|
147
|
+
throw new Error('creation.conflict: versão do pai alterada');
|
|
148
|
+
const existing = findEntity(root, input.id);
|
|
149
|
+
if (existing) {
|
|
150
|
+
if (existing.creationOperationId !== operationId || existing.version !== 1)
|
|
151
|
+
throw new Error('creation.conflict: entidade alheia ou alterada');
|
|
152
|
+
return existing;
|
|
153
|
+
}
|
|
154
|
+
const common = { id: input.id, title: input.title, description: input.description, acceptanceCriteria: input.acceptanceCriteria };
|
|
155
|
+
if (input.kind === 'project')
|
|
156
|
+
return createProjectLocked(root, { ...common, productId: input.parentId, workspaceIds: op.request.workspaceIds }, operationId);
|
|
157
|
+
return createInitiativeLocked(root, { ...common, projectId: input.parentId }, operationId);
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
function listEntities(root, kind, parentId) {
|
|
161
|
+
const p = readPortfolio(root);
|
|
162
|
+
const values = kind === 'product' ? p.products : kind === 'project' ? p.projects : kind === 'initiative' ? p.initiatives : [...p.products, ...p.projects, ...p.initiatives];
|
|
163
|
+
return parentId ? values.filter((item) => item.kind === 'project' ? item.productId === parentId : item.kind === 'initiative' ? item.projectId === parentId : false) : values;
|
|
164
|
+
}
|
|
165
|
+
function assertCycleScope(root, scope) {
|
|
166
|
+
const project = findEntity(root, scope.projectId);
|
|
167
|
+
if (!project || project.kind !== 'project')
|
|
168
|
+
throw new Error(`projeto ${scope.projectId} não encontrado`);
|
|
169
|
+
const ids = unique(scope.initiativeIds);
|
|
170
|
+
if (scope.delivery === 'project' && ids.length)
|
|
171
|
+
throw new Error('escopo de projeto não aceita iniciativas');
|
|
172
|
+
if (scope.delivery === 'initiatives' && !ids.length)
|
|
173
|
+
throw new Error('escopo de iniciativas exige ao menos uma iniciativa');
|
|
174
|
+
for (const id of ids) {
|
|
175
|
+
const initiative = findEntity(root, id);
|
|
176
|
+
if (!initiative || initiative.kind !== 'initiative' || initiative.projectId !== scope.projectId)
|
|
177
|
+
throw new Error(`iniciativa ${id} não pertence ao projeto`);
|
|
178
|
+
}
|
|
179
|
+
return { ...scope, initiativeIds: ids };
|
|
180
|
+
}
|