@orkastery/cli 0.2.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (257) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +86 -65
  3. package/adapters/README.md +30 -15
  4. package/adapters/claude-code/.claude-plugin/plugin.json +11 -5
  5. package/adapters/claude-code/README.md +110 -8
  6. package/adapters/claude-code/commands/check.md +4 -3
  7. package/adapters/claude-code/commands/goal.md +3 -2
  8. package/adapters/claude-code/commands/master.md +3 -2
  9. package/adapters/claude-code/commands/onboarding.md +21 -0
  10. package/adapters/claude-code/commands/ork.md +99 -27
  11. package/adapters/claude-code/hooks/hooks.json +6 -1
  12. package/adapters/claude-code/hooks/ork-guard.js +1 -1
  13. package/adapters/claude-code/hooks/ork-sensor.js +66 -0
  14. package/adapters/codex/skills/ork/SKILL.md +57 -0
  15. package/adapters/hermes/README.md +161 -0
  16. package/adapters/hermes/bin/ork-abrir-thread.sh +2 -0
  17. package/adapters/hermes/bin/ork-brain.sh +4 -0
  18. package/adapters/hermes/bin/ork-hitl-answer.py +82 -0
  19. package/adapters/hermes/bin/ork-maestro.sh +6 -0
  20. package/adapters/hermes/bin/ork-master-enviar.py +84 -0
  21. package/adapters/hermes/bin/ork-pulse-enviar.py +47 -0
  22. package/adapters/hermes/hermes.plugin.json +24 -4
  23. package/adapters/hermes/hitl-ingress/__init__.py +356 -0
  24. package/adapters/hermes/hitl-ingress/plugin.yaml +4 -0
  25. package/adapters/hermes/skills/orkastery-devmaster/SKILL.md +75 -22
  26. package/adapters/openclaw/README.md +136 -27
  27. package/adapters/openclaw/bin/ork-brain.sh +4 -0
  28. package/adapters/openclaw/construir.sh +19 -0
  29. package/adapters/openclaw/dist/hitl-ingress.js +199 -0
  30. package/adapters/openclaw/dist/index.js +437 -0
  31. package/adapters/openclaw/openclaw.plugin.json +39 -134
  32. package/adapters/openclaw/package.json +26 -0
  33. package/adapters/openclaw/src/hitl-ingress.ts +180 -0
  34. package/adapters/openclaw/src/index.ts +460 -0
  35. package/adapters/openclaw/src/tipos-openclaw.d.ts +58 -0
  36. package/adapters/openclaw/tsconfig.json +15 -0
  37. package/assets/docs/markdownlint-cli2.jsonc +22 -0
  38. package/assets/docs/padroes/documentacao-de-produto.md +133 -0
  39. package/assets/docs/padroes/roadmap-de-produto.md +98 -0
  40. package/assets/docs/produto/README.md +14 -0
  41. package/assets/docs/produto/_modelo-feature.md +59 -0
  42. package/assets/docs/roadmap/README.md +14 -0
  43. package/assets/docs/roadmap/_modelo-item.md +73 -0
  44. package/assets/orkmind-native-schema.json +32 -0
  45. package/assets/orkmind_bridge.py +357 -0
  46. package/assets/orkmind_fixture.py +67 -0
  47. package/assets/orkmind_prospective.py +138 -0
  48. package/assets/reference-tariffs-i07.json +23 -0
  49. package/dist/adapters/claude-bg.js +488 -39
  50. package/dist/adapters/codex-controller-sensor.js +449 -0
  51. package/dist/adapters/codex-controller-worker.js +361 -0
  52. package/dist/adapters/codex-controller.js +252 -0
  53. package/dist/adapters/codex-events.js +205 -0
  54. package/dist/adapters/codex-question.js +26 -0
  55. package/dist/adapters/codex-runner.js +133 -0
  56. package/dist/adapters/codex.js +394 -0
  57. package/dist/agents-md.js +84 -0
  58. package/dist/auditoria.js +12 -9
  59. package/dist/auditrun.js +5 -4
  60. package/dist/board.js +174 -23
  61. package/dist/branch-de-estado.js +136 -0
  62. package/dist/canarios-hitl.js +177 -0
  63. package/dist/canarios-i43.js +543 -0
  64. package/dist/canarios-pulse.js +147 -0
  65. package/dist/canarios-sensores.js +129 -0
  66. package/dist/canarios.js +111 -2
  67. package/dist/catalogo.js +9 -0
  68. package/dist/ci.js +223 -0
  69. package/dist/ciclos.js +2 -1
  70. package/dist/claim-lint.js +64 -0
  71. package/dist/claims.js +60 -0
  72. package/dist/company-brain-capture.js +195 -0
  73. package/dist/company-brain-cli.js +123 -0
  74. package/dist/company-brain-client.js +61 -0
  75. package/dist/company-brain-contract.js +166 -0
  76. package/dist/company-brain-journal.js +192 -0
  77. package/dist/company-brain-mcp.js +33 -0
  78. package/dist/company-brain-migration.js +60 -0
  79. package/dist/company-brain-source.js +177 -0
  80. package/dist/company-brain-worker.js +18 -0
  81. package/dist/conducao-texto.js +61 -0
  82. package/dist/conducao.js +876 -0
  83. package/dist/contrato-publico.js +39 -0
  84. package/dist/creation-operation-store.js +251 -0
  85. package/dist/creation-operation.js +148 -0
  86. package/dist/decisao-autonoma.js +183 -0
  87. package/dist/delegation.js +79 -0
  88. package/dist/demo.js +120 -0
  89. package/dist/docs.js +828 -0
  90. package/dist/doctor.js +184 -25
  91. package/dist/entrega-pr.js +117 -0
  92. package/dist/escopo-escrita.js +71 -0
  93. package/dist/estado-thread.js +222 -0
  94. package/dist/evalrunner.js +12 -0
  95. package/dist/fabrica-estado.js +329 -0
  96. package/dist/fabrica-publicar.js +76 -0
  97. package/dist/fix.js +22 -1
  98. package/dist/gates.js +59 -11
  99. package/dist/handoff.js +39 -23
  100. package/dist/hitl-canais.js +333 -0
  101. package/dist/hitl-classificacao.js +131 -0
  102. package/dist/hitl-contract.js +465 -0
  103. package/dist/hitl-estado.js +136 -0
  104. package/dist/hitl-gates.js +554 -0
  105. package/dist/hitl-ingress-receipt.js +382 -0
  106. package/dist/hitl-local-atestado.js +57 -0
  107. package/dist/hitl-local-receipt.js +328 -0
  108. package/dist/hitl-local.js +143 -0
  109. package/dist/hitl-lock.js +139 -0
  110. package/dist/hitl-lote.js +223 -0
  111. package/dist/hitl-native-offer.js +97 -0
  112. package/dist/hitl-native.js +65 -0
  113. package/dist/hitl-presentation.js +209 -0
  114. package/dist/hitl-public-receipt.js +176 -0
  115. package/dist/hitl-resumo.js +209 -0
  116. package/dist/hitl-sessions.js +475 -0
  117. package/dist/hitl.js +711 -0
  118. package/dist/horario.js +269 -0
  119. package/dist/hosts.js +198 -22
  120. package/dist/index.js +1741 -94
  121. package/dist/indice.js +99 -0
  122. package/dist/init.js +28 -3
  123. package/dist/integracoes-locais.js +17 -0
  124. package/dist/leases.js +65 -21
  125. package/dist/ledger-stats.js +272 -0
  126. package/dist/ledger.js +109 -2
  127. package/dist/licoes.js +221 -0
  128. package/dist/liveness.js +218 -0
  129. package/dist/maestro-actions.js +51 -0
  130. package/dist/maestro-authority.js +152 -0
  131. package/dist/maestro-cli.js +97 -0
  132. package/dist/maestro-contract.js +55 -0
  133. package/dist/maestro-discovery.js +132 -0
  134. package/dist/maestro-runtime.js +268 -0
  135. package/dist/maestro-snapshot.js +86 -0
  136. package/dist/maestro-sources.js +224 -0
  137. package/dist/manifest.js +165 -6
  138. package/dist/maquina.js +102 -0
  139. package/dist/master-audit.js +101 -0
  140. package/dist/master-batch.js +43 -0
  141. package/dist/master-digest.js +149 -0
  142. package/dist/master-migracao.js +155 -0
  143. package/dist/master.js +361 -77
  144. package/dist/mcp-artifacts.js +234 -0
  145. package/dist/mcp-git.js +433 -0
  146. package/dist/mcp-install.js +310 -0
  147. package/dist/mcp-maestro.js +25 -0
  148. package/dist/mcp-server.js +469 -0
  149. package/dist/mcp-ship.js +436 -0
  150. package/dist/mcp-verify.js +173 -0
  151. package/dist/memoria-humana.js +207 -0
  152. package/dist/memoria.js +314 -78
  153. package/dist/memory-migration.js +279 -0
  154. package/dist/memory-prospective.js +148 -0
  155. package/dist/modos-migracao.js +191 -0
  156. package/dist/modos.js +151 -15
  157. package/dist/monitor-lock.js +110 -0
  158. package/dist/objective.js +449 -0
  159. package/dist/ocupacao.js +267 -0
  160. package/dist/onboarding.js +303 -0
  161. package/dist/orkmind.js +389 -90
  162. package/dist/orquestracao.js +96 -69
  163. package/dist/phase.js +612 -151
  164. package/dist/playbook-capabilities.js +150 -0
  165. package/dist/playbook-contracts.js +188 -0
  166. package/dist/policies.js +72 -0
  167. package/dist/portfolio-context.js +61 -0
  168. package/dist/portfolio.js +180 -0
  169. package/dist/preflight.js +207 -0
  170. package/dist/process-audit.js +112 -0
  171. package/dist/project-state.js +93 -0
  172. package/dist/prompts.js +25 -5
  173. package/dist/prova-minima.js +112 -0
  174. package/dist/pulse-cadencia.js +163 -0
  175. package/dist/pulse-consentimento.js +260 -0
  176. package/dist/pulse-delivery.js +323 -0
  177. package/dist/pulse-resposta.js +542 -0
  178. package/dist/pulse.js +262 -0
  179. package/dist/ratelimit.js +35 -2
  180. package/dist/recall.js +80 -3
  181. package/dist/redacao-saida.js +45 -0
  182. package/dist/redacao-url.js +66 -0
  183. package/dist/retry.js +640 -118
  184. package/dist/roadmap-reservas.js +243 -0
  185. package/dist/runtime-ambiente.js +38 -0
  186. package/dist/runtime-context.js +215 -0
  187. package/dist/runtime-profiles.js +869 -0
  188. package/dist/runtimes.js +103 -0
  189. package/dist/sandbox.js +9 -0
  190. package/dist/session-events.js +203 -0
  191. package/dist/session-watcher-claude.js +637 -0
  192. package/dist/session-watcher.js +700 -0
  193. package/dist/sessoes-adopt.js +154 -0
  194. package/dist/sessoes-inventario.js +159 -0
  195. package/dist/sessoes.js +16 -35
  196. package/dist/setup.js +691 -0
  197. package/dist/ship.js +168 -19
  198. package/dist/slug.js +1 -1
  199. package/dist/thread-close.js +115 -0
  200. package/dist/thread.js +142 -28
  201. package/dist/tokens.js +1 -1
  202. package/dist/util.js +4 -2
  203. package/dist/verify-sandbox.js +265 -0
  204. package/dist/verify.js +270 -25
  205. package/dist/versao.js +60 -0
  206. package/dist/worktree.js +31 -2
  207. package/dist/write-activation.js +291 -0
  208. package/dist/yaml.js +5 -1
  209. package/eval/casos/master-metrics.json +1 -1
  210. package/eval/casos/onboarding.json +102 -0
  211. package/eval/casos/orkastery-bootstrap.json +37 -3
  212. package/eval/casos/scope-check-capability-map.json +45 -7
  213. package/eval/casos/ship-release.json +3 -3
  214. package/eval/fixtures/b0-slug-e-modos/caso.json +35 -22
  215. package/eval/fixtures/b2-master-log/caso.json +1 -1
  216. package/eval/fixtures/fx-adapter-editado-detectado/caso.json +19 -0
  217. package/eval/fixtures/fx-auto-quiet/caso.json +11 -0
  218. package/eval/fixtures/fx-blanket-approve/caso.json +10 -0
  219. package/eval/fixtures/fx-check-runtime-cruzado/caso.json +20 -0
  220. package/eval/fixtures/fx-codex-dry/caso.json +18 -0
  221. package/eval/fixtures/fx-donewhen-executavel/caso.json +15 -0
  222. package/eval/fixtures/fx-estado-dividido/caso.json +15 -0
  223. package/eval/fixtures/fx-fase-orfa/caso.json +116 -0
  224. package/eval/fixtures/fx-happy/caso.json +6 -4
  225. package/eval/fixtures/fx-hitl-latency/caso.json +20 -0
  226. package/eval/fixtures/fx-indice-reversao/caso.json +25 -0
  227. package/eval/fixtures/fx-listagem-abertas/caso.json +16 -0
  228. package/eval/fixtures/fx-maestro-bootstrap/caso.json +21 -0
  229. package/eval/fixtures/fx-modo-aposentado-escritor/caso.json +21 -0
  230. package/eval/fixtures/fx-modo-aposentado-leitor/caso.json +16 -0
  231. package/eval/fixtures/fx-objective-oscillation/caso.json +14 -0
  232. package/eval/fixtures/fx-omnicanal/caso.json +17 -0
  233. package/eval/fixtures/fx-sensores-runtime/caso.json +17 -0
  234. package/monitor/company-brain.cjs +17 -0
  235. package/monitor/pulse-scope.cjs +45 -0
  236. package/monitor/pulse.cron +18 -0
  237. package/monitor/varredura-pulse.sh +27 -0
  238. package/package.json +28 -7
  239. package/references/definition-of-done.md +2 -2
  240. package/schemas/claims.schema.json +25 -0
  241. package/schemas/company-brain.schema.json +1158 -0
  242. package/schemas/creation-operation.schema.json +365 -0
  243. package/schemas/maestro-snapshot.schema.json +2716 -0
  244. package/skills/README.md +3 -3
  245. package/skills/core/onboarding/SKILL.md +50 -0
  246. package/skills/core/orkastery-bootstrap/SKILL.md +95 -65
  247. package/skills/core/thread-state/SKILL.md +3 -1
  248. package/skills/governance/decision-triage/SKILL.md +4 -4
  249. package/skills/governance/narrative-guardian/SKILL.md +1 -1
  250. package/skills/governance/roadmap-keeper/SKILL.md +1 -1
  251. package/skills/governance/scope-check-capability-map/SKILL.md +17 -7
  252. package/skills/observability/thread-tracing/SKILL.md +1 -1
  253. package/skills/phases/check-quality/SKILL.md +2 -2
  254. package/skills/phases/goal-definition/SKILL.md +1 -1
  255. package/skills/phases/master-metrics/SKILL.md +9 -7
  256. package/skills/phases/plan-specification/SKILL.md +1 -1
  257. package/skills/phases/ship-release/SKILL.md +1 -1
package/dist/ci.js ADDED
@@ -0,0 +1,223 @@
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
+ // O CI roda o mesmo preparo do `ork verify` local (I-54), uma vez e antes das claims: a claim
213
+ // que passa na maquina de quem entrega passa aqui pelo mesmo caminho. Como no verify, o
214
+ // preparo nao e condicao de claim; ele so deixa a compilacao pronta e sai no resultado.
215
+ const comandoDoPreparo = carregado.manifesto.verify.preparo;
216
+ const preparo = comandoDoPreparo
217
+ ? (0, verify_2.executar)('preparo', comandoDoPreparo, carregado.raiz, (0, verify_2.prazoDoComando)(carregado.manifesto, 'preparo'))
218
+ : null;
219
+ const claims = bundle.claims.map((claim) => (0, verify_2.verificarClaim)(claim, carregado.raiz));
220
+ const commands = bundle.commands.map((item) => (0, verify_2.executar)(item.name, item.command, carregado.raiz));
221
+ const ok = claims.every((item) => item.verificado || item.motivo === 'claims.unverifiable') && commands.every((item) => item.ok);
222
+ return { schema: 'ork.ci-run/v1', ok, thread: bundle.thread, commit: (0, verify_2.commitReal)(carregado.raiz), preparo, claims, commands };
223
+ }
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 `#Look` e `#Ork` a fusao economiza exatamente uma pausa.
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
+ }