@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
package/dist/ci.js
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
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.motivoDiferimentoCi = motivoDiferimentoCi;
|
|
37
|
+
exports.repositorioGitHub = repositorioGitHub;
|
|
38
|
+
exports.consultarCi = consultarCi;
|
|
39
|
+
exports.executarCi = executarCi;
|
|
40
|
+
exports.lintDoBundle = lintDoBundle;
|
|
41
|
+
exports.prepararBundleCi = prepararBundleCi;
|
|
42
|
+
exports.executarBundleCi = executarBundleCi;
|
|
43
|
+
const util_1 = require("./util");
|
|
44
|
+
const verify_1 = require("./verify");
|
|
45
|
+
const verify_2 = require("./verify");
|
|
46
|
+
const claims_1 = require("./claims");
|
|
47
|
+
const fs = __importStar(require("node:fs"));
|
|
48
|
+
const path = __importStar(require("node:path"));
|
|
49
|
+
const claim_lint_1 = require("./claim-lint");
|
|
50
|
+
const integracoes_locais_1 = require("./integracoes-locais");
|
|
51
|
+
const ledger_1 = require("./ledger");
|
|
52
|
+
const thread_1 = require("./thread");
|
|
53
|
+
const MARCADORES_DE_HOST = [
|
|
54
|
+
/(^|[\s;&|])\/home\//,
|
|
55
|
+
/(^|[\s;&|])localhost(?=[:/\s;&|]|$)/,
|
|
56
|
+
/verify-company-brain-live\.cjs/,
|
|
57
|
+
];
|
|
58
|
+
// Alguns verificadores misturam casos hermeticos com casos que, por contrato, leem
|
|
59
|
+
// repositorios irmaos ou o estado local da thread. Classificar por executavel inteiro
|
|
60
|
+
// dispensaria tambem os casos hermeticos; a unidade correta e o caso selecionado.
|
|
61
|
+
const CASOS_DE_VERIFICADOR_LOCAIS = [
|
|
62
|
+
/verify-goal-c2-b3\.cjs\s+(?:P2|P3|P4|P5)(?:\s|$)/,
|
|
63
|
+
/verify-check-c2-b3\.cjs\s+(?:docs-metrics|d12-ratification)(?:\s|$)/,
|
|
64
|
+
];
|
|
65
|
+
const CASOS_DE_VERIFICADOR_COM_INTEGRACAO_LOCAL = [
|
|
66
|
+
/verify-check-c2-b3\.cjs\s+channel-offer(?:\s|$)/,
|
|
67
|
+
];
|
|
68
|
+
function exigeIntegracaoLocal(command) {
|
|
69
|
+
return [...command.matchAll(/(?:^|[\s/])([a-z0-9-]+\.test\.js)(?=$|[\s;&|])/g)]
|
|
70
|
+
.some(match => integracoes_locais_1.TESTES_DE_INTEGRACAO_LOCAL.has(match[1]));
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* CI hosted cannot impersonate an installed host. Those claims are not dropped:
|
|
74
|
+
* the bundle names them as deferred and `ork ship` still runs the complete local
|
|
75
|
+
* verification before consulting GitHub. Only checkout-hermetic claims execute in
|
|
76
|
+
* the independent runner.
|
|
77
|
+
*/
|
|
78
|
+
function motivoDiferimentoCi(claim, raiz) {
|
|
79
|
+
// `git ls-files` aceita um pathspec absoluto que esteja dentro desta worktree,
|
|
80
|
+
// mas o mesmo texto aponta para uma worktree inexistente no runner hospedado.
|
|
81
|
+
if (path.isAbsolute(claim.arquivo))
|
|
82
|
+
return 'artifact-not-in-checkout';
|
|
83
|
+
const tracked = (0, util_1.exec)('git', ['ls-files', '--error-unmatch', '--', claim.arquivo], raiz);
|
|
84
|
+
if (!tracked.ok)
|
|
85
|
+
return 'artifact-not-in-checkout';
|
|
86
|
+
if (claim.verificar.some((command) => MARCADORES_DE_HOST.some((marker) => marker.test(command)))) {
|
|
87
|
+
return 'host-runtime-required';
|
|
88
|
+
}
|
|
89
|
+
if (claim.verificar.some((command) => CASOS_DE_VERIFICADOR_LOCAIS.some((caso) => caso.test(command)))) {
|
|
90
|
+
return 'host-runtime-required';
|
|
91
|
+
}
|
|
92
|
+
if (claim.verificar.some((command) => exigeIntegracaoLocal(command)
|
|
93
|
+
|| CASOS_DE_VERIFICADOR_COM_INTEGRACAO_LOCAL.some((caso) => caso.test(command)))) {
|
|
94
|
+
return 'local-integration-required';
|
|
95
|
+
}
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
function repositorioGitHub(url) {
|
|
99
|
+
const match = url.trim().replace(/\.git$/, '').match(/(?:github\.com[/:])([^/]+\/[^/]+)$/i);
|
|
100
|
+
return match?.[1] ?? null;
|
|
101
|
+
}
|
|
102
|
+
function executorPadrao(input) {
|
|
103
|
+
return (0, util_1.exec)('gh', [
|
|
104
|
+
'api', `repos/${input.repository}/commits/${input.sha}/check-runs`,
|
|
105
|
+
'-H', 'Accept: application/vnd.github+json',
|
|
106
|
+
], process.cwd(), 120000);
|
|
107
|
+
}
|
|
108
|
+
function consultarCi(carregado, sha, remoto = 'origin', executor = executorPadrao) {
|
|
109
|
+
const { ci } = carregado.manifesto;
|
|
110
|
+
const context = ci.context || 'ork-verify';
|
|
111
|
+
if (!ci.required_for_ship)
|
|
112
|
+
return { schema: 'ork.ci-status/v1', required: false, ok: true, provider: 'github', repository: null, sha, context, state: 'disabled', url: null, detail: 'gate de CI não exigido pelo manifesto' };
|
|
113
|
+
const remote = (0, util_1.exec)('git', ['remote', 'get-url', remoto], carregado.raiz);
|
|
114
|
+
const repository = remote.ok ? repositorioGitHub(remote.stdout) : null;
|
|
115
|
+
if (!repository)
|
|
116
|
+
return { schema: 'ork.ci-status/v1', required: true, ok: false, provider: 'github', repository: null, sha, context, state: 'unavailable', url: null, detail: `remoto ${remoto} não é um repositório GitHub reconhecível` };
|
|
117
|
+
const result = executor({ repository, sha, context });
|
|
118
|
+
if (!result.ok)
|
|
119
|
+
return { schema: 'ork.ci-status/v1', required: true, ok: false, provider: 'github', repository, sha, context, state: 'unavailable', url: null, detail: (result.stderr || result.stdout || 'consulta ao GitHub falhou').trim().slice(0, 400) };
|
|
120
|
+
let payload;
|
|
121
|
+
try {
|
|
122
|
+
payload = JSON.parse(result.stdout);
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
return { schema: 'ork.ci-status/v1', required: true, ok: false, provider: 'github', repository, sha, context, state: 'unavailable', url: null, detail: 'GitHub devolveu uma resposta de checks inválida' };
|
|
126
|
+
}
|
|
127
|
+
const checks = (payload.check_runs ?? []).filter((check) => check.name === context);
|
|
128
|
+
if (!checks.length)
|
|
129
|
+
return { schema: 'ork.ci-status/v1', required: true, ok: false, provider: 'github', repository, sha, context, state: 'missing', url: null, detail: `check ${context} não encontrado no commit` };
|
|
130
|
+
const check = checks.at(-1);
|
|
131
|
+
const status = check.status ?? 'unknown';
|
|
132
|
+
const conclusion = check.conclusion ?? '';
|
|
133
|
+
const url = check.html_url ?? '';
|
|
134
|
+
const state = status !== 'completed' ? 'pending' : conclusion === 'success' ? 'success' : 'failure';
|
|
135
|
+
return { schema: 'ork.ci-status/v1', required: true, ok: state === 'success', provider: 'github', repository, sha, context, state, url: url || null, detail: state === 'success' ? 'CHECK independente verde no GitHub' : `check ${context}: ${status}/${conclusion || 'sem conclusão'}` };
|
|
136
|
+
}
|
|
137
|
+
function executarCi(carregado, threadId) {
|
|
138
|
+
const verify = (0, verify_1.verificar)(carregado, threadId);
|
|
139
|
+
const context = carregado.manifesto.ci.context || 'ork-verify';
|
|
140
|
+
const repository = process.env.GITHUB_REPOSITORY ?? null;
|
|
141
|
+
return {
|
|
142
|
+
status: {
|
|
143
|
+
schema: 'ork.ci-status/v1', required: true, ok: verify.ok, provider: 'github', repository,
|
|
144
|
+
sha: verify.commit, context, state: verify.ok ? 'success' : 'failure',
|
|
145
|
+
url: process.env.GITHUB_SERVER_URL && repository && process.env.GITHUB_RUN_ID ? `${process.env.GITHUB_SERVER_URL}/${repository}/actions/runs/${process.env.GITHUB_RUN_ID}` : null,
|
|
146
|
+
detail: verify.ok ? 'ork verify aprovado no runner independente' : `ork verify reprovou: ${verify.motivos.join(', ')}`,
|
|
147
|
+
},
|
|
148
|
+
verify,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* I-53 (RM-037, P6): o lint no `ci prepare`. Claim nascida sob a regra (com o campo `lint`) que
|
|
153
|
+
* roda a suite inteira e recusada: o bundle vai para o runner hospedado e reprovaria la, depois de
|
|
154
|
+
* gastar o tempo todo. O resto so avisa, e o aviso fica no ledger da thread.
|
|
155
|
+
*/
|
|
156
|
+
function lintDoBundle(claims) {
|
|
157
|
+
const recusas = [], avisos = [];
|
|
158
|
+
for (const claim of claims) {
|
|
159
|
+
for (const achado of (0, claim_lint_1.analisarComandos)(claim.verificar)) {
|
|
160
|
+
const linha = (0, claim_lint_1.linhaDoLintDeClaim)(claim.id, achado);
|
|
161
|
+
if (achado.regra === 'suite-inteira' && claim.lint !== undefined)
|
|
162
|
+
recusas.push(linha);
|
|
163
|
+
else
|
|
164
|
+
avisos.push(achado.regra === 'suite-inteira' ? `${linha} (claim anterior ao lint: so aviso)` : linha);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return { recusas, avisos };
|
|
168
|
+
}
|
|
169
|
+
function prepararBundleCi(carregado, threadId, opcoes = {}) {
|
|
170
|
+
const commands = carregado.manifesto.ci.command
|
|
171
|
+
? [{ name: 'ci', command: carregado.manifesto.ci.command }]
|
|
172
|
+
: (0, verify_2.comandosDoManifesto)(carregado.manifesto).map(({ nome, comando }) => ({ name: nome, command: comando }));
|
|
173
|
+
const activeClaims = (0, claims_1.lerClaims)(carregado.raiz, threadId).filter((claim) => claim.estado !== 'retirada');
|
|
174
|
+
const lint = lintDoBundle(activeClaims);
|
|
175
|
+
if (lint.recusas.length || lint.avisos.length) {
|
|
176
|
+
(0, ledger_1.registrar)((0, thread_1.dirThread)(carregado.raiz, threadId), threadId, ledger_1.TIPOS_DE_EVENTO.lintDeClaim, { recusas: lint.recusas, avisos: lint.avisos });
|
|
177
|
+
}
|
|
178
|
+
for (const aviso of lint.avisos)
|
|
179
|
+
opcoes.aoAvisar?.(aviso);
|
|
180
|
+
if (lint.recusas.length) {
|
|
181
|
+
throw new Error(`claims.lint: o bundle nao foi gerado; retire a claim e registre de novo com o comando focado:\n ` +
|
|
182
|
+
lint.recusas.join('\n '));
|
|
183
|
+
}
|
|
184
|
+
const classified = activeClaims.map((claim) => ({
|
|
185
|
+
claim,
|
|
186
|
+
reason: motivoDiferimentoCi(claim, carregado.raiz),
|
|
187
|
+
}));
|
|
188
|
+
const bundle = {
|
|
189
|
+
schema: 'ork.ci-bundle/v1',
|
|
190
|
+
thread: threadId,
|
|
191
|
+
base: carregado.manifesto.worktree.base_branch,
|
|
192
|
+
claims: classified.filter((item) => item.reason === null).map((item) => item.claim),
|
|
193
|
+
deferredClaims: classified
|
|
194
|
+
.filter((item) => item.reason !== null)
|
|
195
|
+
.map((item) => ({ id: item.claim.id, reason: item.reason })),
|
|
196
|
+
commands,
|
|
197
|
+
};
|
|
198
|
+
const dir = path.join(carregado.raiz, '.ork-ci');
|
|
199
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
200
|
+
const file = path.join(dir, 'bundle.json');
|
|
201
|
+
fs.writeFileSync(file, JSON.stringify(bundle, null, 2) + '\n', 'utf8');
|
|
202
|
+
return file;
|
|
203
|
+
}
|
|
204
|
+
function executarBundleCi(carregado, file = '.ork-ci/bundle.json') {
|
|
205
|
+
const absolute = path.resolve(carregado.raiz, file);
|
|
206
|
+
const stat = fs.statSync(absolute);
|
|
207
|
+
if (!stat.isFile() || stat.size > 1024 * 1024)
|
|
208
|
+
throw new Error('bundle de CI ausente ou acima de 1 MiB');
|
|
209
|
+
const bundle = JSON.parse(fs.readFileSync(absolute, 'utf8'));
|
|
210
|
+
if (bundle.schema !== 'ork.ci-bundle/v1' || !/^ork-[a-z0-9-]+$/.test(bundle.thread) || !Array.isArray(bundle.claims) || !Array.isArray(bundle.commands))
|
|
211
|
+
throw new Error('bundle de CI inválido');
|
|
212
|
+
const claims = bundle.claims.map((claim) => (0, verify_2.verificarClaim)(claim, carregado.raiz));
|
|
213
|
+
const commands = bundle.commands.map((item) => (0, verify_2.executar)(item.name, item.command, carregado.raiz));
|
|
214
|
+
const ok = claims.every((item) => item.verificado || item.motivo === 'claims.unverifiable') && commands.every((item) => item.ok);
|
|
215
|
+
return { schema: 'ork.ci-run/v1', ok, thread: bundle.thread, commit: (0, verify_2.commitReal)(carregado.raiz), claims, commands };
|
|
216
|
+
}
|
package/dist/ciclos.js
CHANGED
|
@@ -85,7 +85,8 @@ function refazerBloco(fases, pausa, pausaSobre) {
|
|
|
85
85
|
* Funde os blocos que conduzem GOAL e PLAN num bloco unico.
|
|
86
86
|
*
|
|
87
87
|
* Idempotente: em `#Default`, `#Maestro` e `#Auto` o GOAL e o PLAN ja vivem no mesmo
|
|
88
|
-
* bloco e nada muda. Em `#
|
|
88
|
+
* bloco e nada muda. Em `#Classic`, que abre GOAL e PLAN em blocos separados, a fusao
|
|
89
|
+
* economiza exatamente uma pausa.
|
|
89
90
|
*/
|
|
90
91
|
function fundirGoalPlan(blocos) {
|
|
91
92
|
const envolvidos = blocos.filter((b) => b.fases.includes('GOAL') || b.fases.includes('PLAN'));
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* I-53 (RM-037, P6): o lint do comando de claim.
|
|
4
|
+
*
|
|
5
|
+
* A claim C48 foi para o bundle rodando a suite inteira do npm e derrubou 33m57s de CI no runner
|
|
6
|
+
* hospedado. O conhecimento existia; faltava a regra no lugar certo. Ela mora aqui, pura e sem
|
|
7
|
+
* disco, e e lida em dois pontos com severidades diferentes (D14 do PLAN da I-37):
|
|
8
|
+
*
|
|
9
|
+
* regra `claims add` `ci prepare`
|
|
10
|
+
* (a) suite inteira do npm avisa RECUSA (so em claim nascida sob a regra)
|
|
11
|
+
* (b1) SHA intermediario avisa avisa
|
|
12
|
+
* (b2) contagem de commits avisa avisa
|
|
13
|
+
*
|
|
14
|
+
* Toda claim nova nasce com o campo `lint` (vazio quando nada casou). E ele que separa a claim
|
|
15
|
+
* nascida sob a regra da claim antiga: a antiga so recebe aviso, porque corrigir claim de thread
|
|
16
|
+
* alheia e mexer em estado de quem nao pediu (D16). Nada de data de corte no codigo.
|
|
17
|
+
*/
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.analisarComando = analisarComando;
|
|
20
|
+
exports.analisarComandos = analisarComandos;
|
|
21
|
+
exports.linhaDoLintDeClaim = linhaDoLintDeClaim;
|
|
22
|
+
const CORRECAO = {
|
|
23
|
+
'suite-inteira': 'use `npm --prefix core run test:ci` (a suite hermetica) ou `node --test` no arquivo do teste',
|
|
24
|
+
'sha-intermediario': 'ancore na base (`git diff "$(git merge-base origin/main HEAD)" HEAD`) ou prove ancestralidade com `git merge-base --is-ancestor <sha> HEAD`',
|
|
25
|
+
'contagem-de-commits': 'use `git merge-base --is-ancestor <commit> HEAD`: contagem de commits quebra com rebase e merge',
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* (a) `npm [--prefix <dir>] [run] test` sem subcomando (`test:ci` nao casa) e sem arquivos
|
|
29
|
+
* nomeados depois de `--` (o comando que nomeia `.test.ts`/`.js` depois de `--` e focado).
|
|
30
|
+
*/
|
|
31
|
+
const SUITE_INTEIRA = /\bnpm\s+(?:(?:--prefix|-C)\s+\S+\s+)?(?:run\s+|run-script\s+)?test(?![\w:.-])(?!\s+--\s+\S*\.test\.(?:ts|js|mjs|cjs|tsx)\b)/;
|
|
32
|
+
/** (b1) Hash de 7 a 40 hex com letra e digito, em comando que cita `git`. */
|
|
33
|
+
const HASH = /\b[0-9a-f]{7,40}\b/g;
|
|
34
|
+
const SO_ANCESTRALIDADE = /merge-base\s+--is-ancestor\s+\S+\s+\S+/g;
|
|
35
|
+
/** (b2) Contagem de commits comparada a um numero. */
|
|
36
|
+
const CONTAGEM = /rev-list\s+(?:[^|;&]*\s)?--count\b|\bgit\s+log\b[^|;&]*\|\s*wc\s+-l\b/;
|
|
37
|
+
const COMPARACAO = /-(?:eq|ne|gt|lt|ge|le)\s+['"]?\d|[^=!<>]==?\s*['"]?\d|grep\s+-(?:q?x|xq)\s+['"]?\d/;
|
|
38
|
+
function analisarComando(comando) {
|
|
39
|
+
const achados = [];
|
|
40
|
+
const suite = SUITE_INTEIRA.exec(comando);
|
|
41
|
+
if (suite)
|
|
42
|
+
achados.push({ regra: 'suite-inteira', trecho: suite[0].trim(), correcao: CORRECAO['suite-inteira'] });
|
|
43
|
+
if (/\bgit\b/.test(comando)) {
|
|
44
|
+
const semAncestralidade = comando.replace(SO_ANCESTRALIDADE, ' ');
|
|
45
|
+
const hash = [...semAncestralidade.matchAll(HASH)].map((m) => m[0]).find((t) => /\d/.test(t) && /[a-f]/.test(t));
|
|
46
|
+
if (hash)
|
|
47
|
+
achados.push({ regra: 'sha-intermediario', trecho: hash, correcao: CORRECAO['sha-intermediario'] });
|
|
48
|
+
}
|
|
49
|
+
const contagem = CONTAGEM.exec(comando);
|
|
50
|
+
if (contagem && COMPARACAO.test(comando)) {
|
|
51
|
+
achados.push({ regra: 'contagem-de-commits', trecho: contagem[0].trim(), correcao: CORRECAO['contagem-de-commits'] });
|
|
52
|
+
}
|
|
53
|
+
return achados;
|
|
54
|
+
}
|
|
55
|
+
/** O lint de todos os comandos de uma claim, na ordem. */
|
|
56
|
+
function analisarComandos(comandos) {
|
|
57
|
+
return comandos.flatMap(analisarComando);
|
|
58
|
+
}
|
|
59
|
+
/** Uma linha por achado, para o terminal e para o ledger. */
|
|
60
|
+
function linhaDoLintDeClaim(claimId, a) {
|
|
61
|
+
const nome = { 'suite-inteira': 'roda a suite inteira', 'sha-intermediario': 'fixa um SHA intermediario',
|
|
62
|
+
'contagem-de-commits': 'compara contagem de commits' }[a.regra];
|
|
63
|
+
return `${claimId} ${nome} (${a.trecho}); ${a.correcao}`;
|
|
64
|
+
}
|
package/dist/claims.js
CHANGED
|
@@ -44,6 +44,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
44
44
|
};
|
|
45
45
|
})();
|
|
46
46
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
47
|
+
exports.ARQUIVO_DONEWHEN = void 0;
|
|
47
48
|
exports.caminhoClaims = caminhoClaims;
|
|
48
49
|
exports.lerClaims = lerClaims;
|
|
49
50
|
exports.proximoIdDeClaim = proximoIdDeClaim;
|
|
@@ -51,6 +52,7 @@ exports.ehAlegacaoNegativa = ehAlegacaoNegativa;
|
|
|
51
52
|
exports.montarClaim = montarClaim;
|
|
52
53
|
exports.gravarClaim = gravarClaim;
|
|
53
54
|
exports.adicionarClaim = adicionarClaim;
|
|
55
|
+
exports.registrarCriteriosDePronto = registrarCriteriosDePronto;
|
|
54
56
|
exports.exigirClaim = exigirClaim;
|
|
55
57
|
exports.anexarComando = anexarComando;
|
|
56
58
|
exports.retirarClaim = retirarClaim;
|
|
@@ -58,8 +60,11 @@ exports.carimbarEstado = carimbarEstado;
|
|
|
58
60
|
exports.tabelaDeClaims = tabelaDeClaims;
|
|
59
61
|
const path = __importStar(require("node:path"));
|
|
60
62
|
const ledger_1 = require("./ledger");
|
|
63
|
+
const manifest_1 = require("./manifest");
|
|
64
|
+
const prova_minima_1 = require("./prova-minima");
|
|
61
65
|
const thread_1 = require("./thread");
|
|
62
66
|
const util_1 = require("./util");
|
|
67
|
+
const claim_lint_1 = require("./claim-lint");
|
|
63
68
|
/** Caminho do `claims.jsonl` da thread (append-only, como o ledger). */
|
|
64
69
|
function caminhoClaims(raiz, threadId) {
|
|
65
70
|
return path.join((0, thread_1.dirThread)(raiz, threadId), 'claims.jsonl');
|
|
@@ -117,15 +122,24 @@ function montarClaim(entrada) {
|
|
|
117
122
|
verificar,
|
|
118
123
|
criadoEm: (0, util_1.agora)(),
|
|
119
124
|
estado: verificar.length > 0 ? 'pendente' : 'nao-verificavel',
|
|
125
|
+
...(entrada.origem ? { origem: entrada.origem } : {}),
|
|
126
|
+
// I-53 (RM-037, P6): a mesma regra para CLI, MCP, canarios e auditor, porque todos passam aqui.
|
|
127
|
+
lint: (0, claim_lint_1.analisarComandos)(verificar),
|
|
120
128
|
};
|
|
121
129
|
}
|
|
122
130
|
/** Grava uma claim no `claims.jsonl` (append-only). */
|
|
123
131
|
function gravarClaim(raiz, claim) {
|
|
124
132
|
(0, util_1.anexarJsonl)(caminhoClaims(raiz, claim.thread), claim);
|
|
125
133
|
}
|
|
134
|
+
/** I-42 (D5): a suite inteira do manifesto, que um ciclo sem CHECK nao roda como prova. */
|
|
135
|
+
function comandoDeTesteDoManifesto(raiz) {
|
|
136
|
+
return (0, manifest_1.carregarManifesto)(raiz)?.manifesto.verify.test;
|
|
137
|
+
}
|
|
126
138
|
/** `ork claims add`: registra a alegacao e a lista no `thread.json` e no ledger. */
|
|
127
139
|
function adicionarClaim(raiz, threadId, opcoes) {
|
|
128
140
|
const thread = (0, thread_1.lerThread)(raiz, threadId);
|
|
141
|
+
if ((0, prova_minima_1.cicloSemCheck)(thread))
|
|
142
|
+
(0, prova_minima_1.validarProvaSemCheck)(opcoes.verificar ?? [], comandoDeTesteDoManifesto(raiz));
|
|
129
143
|
const existentes = lerClaims(raiz, threadId);
|
|
130
144
|
const claim = montarClaim({
|
|
131
145
|
id: proximoIdDeClaim(existentes),
|
|
@@ -134,6 +148,7 @@ function adicionarClaim(raiz, threadId, opcoes) {
|
|
|
134
148
|
arquivo: opcoes.arquivo,
|
|
135
149
|
alegacao: opcoes.alegacao,
|
|
136
150
|
verificar: opcoes.verificar,
|
|
151
|
+
origem: opcoes.origem,
|
|
137
152
|
});
|
|
138
153
|
const negativa = claim.negativa;
|
|
139
154
|
const verificar = claim.verificar;
|
|
@@ -148,9 +163,49 @@ function adicionarClaim(raiz, threadId, opcoes) {
|
|
|
148
163
|
negativa,
|
|
149
164
|
verificar,
|
|
150
165
|
estado: claim.estado,
|
|
166
|
+
...(claim.lint?.length ? { lint: claim.lint.map((a) => a.regra) } : {}),
|
|
151
167
|
});
|
|
152
168
|
return claim;
|
|
153
169
|
}
|
|
170
|
+
/** Prefixo do arquivo das claims de criterio, para o veredito dizer de onde veio. */
|
|
171
|
+
exports.ARQUIVO_DONEWHEN = 'thread.json#doneWhen';
|
|
172
|
+
/**
|
|
173
|
+
* Registra os criterios de pronto da thread como CLAIMS do nucleo (I-43, D4, viga b).
|
|
174
|
+
*
|
|
175
|
+
* E o que torna `doneWhen` executavel sem criar uma segunda maquina de rodar comando.
|
|
176
|
+
* A alternativa rejeitada em D4 era um executor proprio em `donewhen.ts`, que
|
|
177
|
+
* duplicaria timeout, captura de saida, codigo de retorno e registro de evidencia,
|
|
178
|
+
* tudo ja resolvido em claims e verify. Duas maquinas de executar comando divergem, e
|
|
179
|
+
* a divergencia aparece como criterio que passa num lugar e falha no outro.
|
|
180
|
+
*
|
|
181
|
+
* A claim carrega o TEXTO do criterio na alegacao, e e por isso que o veredito do
|
|
182
|
+
* `ork verify` o nomeia sem precisar de nenhum campo extra: quando ela reprova, o
|
|
183
|
+
* relatorio ja diz qual criterio falhou.
|
|
184
|
+
*
|
|
185
|
+
* IDEMPOTENTE: registrar de novo nao duplica claim. `ork verify` roda em toda fase, e
|
|
186
|
+
* uma claim por criterio POR RODADA transformaria a lista num acumulador.
|
|
187
|
+
*/
|
|
188
|
+
function registrarCriteriosDePronto(raiz, threadId) {
|
|
189
|
+
const thread = (0, thread_1.lerThread)(raiz, threadId);
|
|
190
|
+
const criterios = thread.doneWhen ?? [];
|
|
191
|
+
if (criterios.length === 0)
|
|
192
|
+
return [];
|
|
193
|
+
const existentes = lerClaims(raiz, threadId);
|
|
194
|
+
const novas = [];
|
|
195
|
+
for (const { criterio, comando } of criterios) {
|
|
196
|
+
const alegacao = `criterio de pronto: ${criterio.trim()}`;
|
|
197
|
+
const ja = existentes.find((c) => c.origem === 'nucleo.doneWhen' && c.alegacao === alegacao);
|
|
198
|
+
if (ja)
|
|
199
|
+
continue;
|
|
200
|
+
novas.push(adicionarClaim(raiz, threadId, {
|
|
201
|
+
arquivo: exports.ARQUIVO_DONEWHEN,
|
|
202
|
+
alegacao,
|
|
203
|
+
verificar: [comando],
|
|
204
|
+
origem: 'nucleo.doneWhen',
|
|
205
|
+
}));
|
|
206
|
+
}
|
|
207
|
+
return novas;
|
|
208
|
+
}
|
|
154
209
|
/** Acha a claim pelo id, com erro acionavel quando ela nao existe. */
|
|
155
210
|
function exigirClaim(raiz, threadId, claimId) {
|
|
156
211
|
const claim = lerClaims(raiz, threadId).find((c) => c.id === claimId);
|
|
@@ -167,10 +222,15 @@ function exigirClaim(raiz, threadId, claimId) {
|
|
|
167
222
|
*/
|
|
168
223
|
function anexarComando(raiz, threadId, claimId, comando) {
|
|
169
224
|
const claim = exigirClaim(raiz, threadId, claimId);
|
|
225
|
+
if ((0, prova_minima_1.cicloSemCheck)((0, thread_1.lerThread)(raiz, threadId))) {
|
|
226
|
+
(0, prova_minima_1.validarProvaSemCheck)([...claim.verificar, comando], comandoDeTesteDoManifesto(raiz));
|
|
227
|
+
}
|
|
170
228
|
const atualizada = {
|
|
171
229
|
...claim,
|
|
172
230
|
verificar: [...claim.verificar, comando],
|
|
173
231
|
estado: 'pendente',
|
|
232
|
+
// I-53: claim nascida sob a regra refaz o lint; a antiga continua sem o campo (so aviso).
|
|
233
|
+
...(claim.lint ? { lint: (0, claim_lint_1.analisarComandos)([...claim.verificar, comando]) } : {}),
|
|
174
234
|
};
|
|
175
235
|
gravarClaim(raiz, atualizada);
|
|
176
236
|
(0, ledger_1.registrar)((0, thread_1.dirThread)(raiz, threadId), threadId, ledger_1.TIPOS_DE_EVENTO.claimRegistrada, {
|
|
@@ -0,0 +1,195 @@
|
|
|
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.capture = capture;
|
|
37
|
+
const fs = __importStar(require("node:fs"));
|
|
38
|
+
const path = __importStar(require("node:path"));
|
|
39
|
+
const company_brain_contract_1 = require("./company-brain-contract");
|
|
40
|
+
const company_brain_client_1 = require("./company-brain-client");
|
|
41
|
+
const company_brain_journal_1 = require("./company-brain-journal");
|
|
42
|
+
const company_brain_source_1 = require("./company-brain-source");
|
|
43
|
+
function capture(scope, dir, catalog, ledger, transport, fault = () => { }, journal = []) {
|
|
44
|
+
const file = path.join(dir, 'cursor.json');
|
|
45
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
46
|
+
return (0, company_brain_journal_1.withProcessLock)(path.join(dir, 'capture.lock'), 'brain.capture.busy', () => {
|
|
47
|
+
let cursor = fs.existsSync(file) ? JSON.parse(fs.readFileSync(file, 'utf8')) : { schema: 'ork.brain-cursor/v2', scope,
|
|
48
|
+
offset: 0, prefixHash: (0, company_brain_journal_1.bytesHash)(''), versions: {}, pending: {}, confirmed: {} };
|
|
49
|
+
// Preserve the single outstanding v1 outbox item and its already-spent attempts.
|
|
50
|
+
const legacy = cursor;
|
|
51
|
+
if (legacy.schema === 'ork.brain-cursor/v1') {
|
|
52
|
+
const p = legacy.pending;
|
|
53
|
+
cursor = { ...legacy, schema: 'ork.brain-cursor/v2', pending: {}, confirmed: {} };
|
|
54
|
+
if (p)
|
|
55
|
+
cursor.pending[p.event.aggregate_id] = { event: p.event, attempts: p.attempts, error: p.error,
|
|
56
|
+
...(p.next.offset > cursor.offset ? { end: p.next.offset } : { version: p.next.versions[p.event.aggregate_id] }) };
|
|
57
|
+
}
|
|
58
|
+
if (cursor.schema !== 'ork.brain-cursor/v2' || JSON.stringify(cursor.scope) !== JSON.stringify(scope) ||
|
|
59
|
+
!Number.isSafeInteger(cursor.offset) || cursor.offset < 0 || cursor.offset > ledger.length ||
|
|
60
|
+
(0, company_brain_journal_1.bytesHash)(ledger.subarray(0, cursor.offset)) !== cursor.prefixHash)
|
|
61
|
+
throw Error('brain.cursor.source-conflict');
|
|
62
|
+
const source = (0, company_brain_source_1.readCycle)(ledger, scope, cursor.offset);
|
|
63
|
+
let received = 0;
|
|
64
|
+
const errors = {};
|
|
65
|
+
// A migration may have materialized the current snapshot before this local
|
|
66
|
+
// cursor existed. Read the authenticated aggregate head and continue from
|
|
67
|
+
// its monotonic sequence; never guess from a local migration plan.
|
|
68
|
+
for (const entity of (0, company_brain_source_1.portfolioEntities)(catalog, scope)) {
|
|
69
|
+
if (cursor.versions[entity.id] || cursor.pending[entity.id])
|
|
70
|
+
continue;
|
|
71
|
+
let response;
|
|
72
|
+
try {
|
|
73
|
+
response = transport({ schema: company_brain_client_1.BRAIN_API, operation: 'head', payload: { tenant_id: scope.tenant, id: entity.id } });
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
response = { state: 'unavailable' };
|
|
77
|
+
}
|
|
78
|
+
if (response.state === 'unknown')
|
|
79
|
+
continue;
|
|
80
|
+
if (response.state !== 'ok') {
|
|
81
|
+
if (['forbidden', 'withheld', 'conflict'].includes(response.state))
|
|
82
|
+
errors[entity.id] = 'brain.capture.head-unavailable';
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
const head = response;
|
|
86
|
+
if (!Number.isSafeInteger(head.sequence) || head.sequence < 1 || typeof head.active !== 'boolean' ||
|
|
87
|
+
(head.active && (!Number.isSafeInteger(head.source_version) || head.source_version < 1 ||
|
|
88
|
+
typeof head.source_hash !== 'string' || !/^[a-f0-9]{64}$/.test(head.source_hash)))) {
|
|
89
|
+
errors[entity.id] = 'brain.capture.head-invalid';
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if (!head.active) {
|
|
93
|
+
cursor.versions[entity.id] = { version: 0, hash: '', sequence: head.sequence };
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (head.source_version > entity.version) {
|
|
97
|
+
errors[entity.id] = 'brain.source.version-conflict';
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
cursor.versions[entity.id] = { version: head.source_version, hash: head.source_hash, sequence: head.sequence };
|
|
101
|
+
}
|
|
102
|
+
const flush = (aggregate) => {
|
|
103
|
+
const pending = cursor.pending[aggregate];
|
|
104
|
+
if (!pending)
|
|
105
|
+
return true;
|
|
106
|
+
const event = (0, company_brain_contract_1.validateContract)(pending.event);
|
|
107
|
+
while (pending.attempts < 3) {
|
|
108
|
+
pending.attempts++;
|
|
109
|
+
(0, company_brain_journal_1.atomicJson)(file, cursor);
|
|
110
|
+
fault('attempt');
|
|
111
|
+
let response;
|
|
112
|
+
try {
|
|
113
|
+
response = transport({ schema: company_brain_client_1.BRAIN_API, operation: 'ingest', payload: event });
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
response = { state: 'unavailable' };
|
|
117
|
+
}
|
|
118
|
+
try {
|
|
119
|
+
if (response.state !== 'ok')
|
|
120
|
+
throw Error('brain.capture.unconfirmed');
|
|
121
|
+
const receipt = (0, company_brain_contract_1.validateContract)(response.receipt);
|
|
122
|
+
const indexed = (0, company_brain_contract_1.validateContract)(response.indexed);
|
|
123
|
+
for (const [r, stage] of [[receipt, 'materialized'], [indexed, 'retrievable']]) {
|
|
124
|
+
if (r.schema !== 'orkmind.company-brain-receipt/v1' || r.tenant_id !== scope.tenant || r.event_id !== event.id || r.stage !== stage || r.result !== 'ok' ||
|
|
125
|
+
r.materialized_version !== (event.payload?.version ?? r.materialized_version))
|
|
126
|
+
throw Error('brain.receipt.invalid');
|
|
127
|
+
}
|
|
128
|
+
fault('remote-receipt');
|
|
129
|
+
(0, company_brain_journal_1.durableAppend)(path.join(dir, 'receipts.jsonl'), { event_id: event.id, receipt, indexed });
|
|
130
|
+
if (pending.version)
|
|
131
|
+
cursor.versions[aggregate] = pending.version;
|
|
132
|
+
if (pending.end !== undefined)
|
|
133
|
+
cursor.confirmed[String(pending.end)] = true;
|
|
134
|
+
delete cursor.pending[aggregate];
|
|
135
|
+
(0, company_brain_journal_1.atomicJson)(file, cursor);
|
|
136
|
+
fault('cursor');
|
|
137
|
+
received++;
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
if (error instanceof Error && error.message === 'injected-crash')
|
|
142
|
+
throw error;
|
|
143
|
+
pending.error = 'brain.capture.unconfirmed';
|
|
144
|
+
(0, company_brain_journal_1.atomicJson)(file, cursor);
|
|
145
|
+
if (['conflict', 'forbidden', 'withheld'].includes(response.state))
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
errors[aggregate] = 'brain.capture.dead-letter';
|
|
150
|
+
return false;
|
|
151
|
+
};
|
|
152
|
+
for (const aggregate of Object.keys(cursor.pending))
|
|
153
|
+
flush(aggregate);
|
|
154
|
+
const enqueue = (pending) => {
|
|
155
|
+
cursor.pending[pending.event.aggregate_id] = pending;
|
|
156
|
+
(0, company_brain_journal_1.atomicJson)(file, cursor);
|
|
157
|
+
fault('outbox');
|
|
158
|
+
return flush(pending.event.aggregate_id);
|
|
159
|
+
};
|
|
160
|
+
for (const entity of (0, company_brain_source_1.portfolioHistory)(catalog, scope, journal)) {
|
|
161
|
+
if (errors[entity.id] || cursor.pending[entity.id])
|
|
162
|
+
continue;
|
|
163
|
+
const old = cursor.versions[entity.id];
|
|
164
|
+
if (old && entity.version < old.version)
|
|
165
|
+
continue;
|
|
166
|
+
if (old?.version === entity.version) {
|
|
167
|
+
if (old.hash !== entity.source.source_hash)
|
|
168
|
+
errors[entity.id] = 'brain.source.version-conflict';
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
if (old && entity.version !== old.version + 1) {
|
|
172
|
+
errors[entity.id] = 'brain.source.version-gap';
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
const sequence = (old?.sequence ?? 0) + 1;
|
|
176
|
+
enqueue({ event: (0, company_brain_source_1.entityEvent)(entity, sequence), version: { version: entity.version, hash: entity.source.source_hash, sequence }, attempts: 0, error: null });
|
|
177
|
+
}
|
|
178
|
+
for (const record of source.records) {
|
|
179
|
+
if (!record.event || cursor.confirmed[String(record.end)] || cursor.pending[record.event.aggregate_id])
|
|
180
|
+
continue;
|
|
181
|
+
enqueue({ event: record.event, end: record.end, attempts: 0, error: null });
|
|
182
|
+
}
|
|
183
|
+
// Independent aggregates can confirm out of order; the ledger watermark cannot.
|
|
184
|
+
for (const record of source.records) {
|
|
185
|
+
if (record.event && !cursor.confirmed[String(record.end)])
|
|
186
|
+
break;
|
|
187
|
+
cursor.offset = record.end;
|
|
188
|
+
cursor.prefixHash = record.prefixHash;
|
|
189
|
+
delete cursor.confirmed[String(record.end)];
|
|
190
|
+
}
|
|
191
|
+
(0, company_brain_journal_1.atomicJson)(file, cursor);
|
|
192
|
+
return { state: Object.keys(errors).length ? 'unavailable' : 'ok', received, offset: cursor.offset, partial: source.partial,
|
|
193
|
+
...(Object.keys(errors).length ? { error: 'brain.capture.aggregate-blocked', errors } : {}) };
|
|
194
|
+
});
|
|
195
|
+
}
|