@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/memoria.js CHANGED
@@ -1,22 +1,4 @@
1
1
  "use strict";
2
- /**
3
- * A camada de memoria do `ork` (bloco B6): o que o nucleo grava, com que tags, e o que
4
- * ele injeta de volta no prompt de cada fase.
5
- *
6
- * Divisao de trabalho deste bloco:
7
- * - `orkmind.ts` sabe FALAR com o OrkMind (transporte, degradacao, governanca);
8
- * - `memoria.ts` (aqui) sabe O QUE o Orkastery grava e le de volta (decisao, handoff,
9
- * rule, learning, roadmap), e como isso vira contexto de prompt;
10
- * - `recall.ts` sabe RESOLVER o que ficou fora do prompt, no momento indicado.
11
- *
12
- * Nenhuma regra de negocio nova nasce aqui. Decisao, claim, baseline, achado e score
13
- * continuam sendo do nucleo que ja existe: esta camada os PUBLICA e os RECUPERA.
14
- *
15
- * A propriedade que o B6 precisa provar e a da injecao deterministica: um prompt de fase
16
- * carrega 100 por cento das decisoes fechadas da thread, venham elas do `thread.json`
17
- * (regime `files`) ou da colecao `decision` (regime `orkmind`). Por isso a injecao NAO
18
- * depende do OrkMind estar ligado: ela troca de fonte, nao de garantia.
19
- */
20
2
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
21
3
  if (k2 === undefined) k2 = k;
22
4
  var desc = Object.getOwnPropertyDescriptor(m, k);
@@ -51,13 +33,18 @@ var __importStar = (this && this.__importStar) || (function () {
51
33
  };
52
34
  })();
53
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.sincronizarOnboarding = sincronizarOnboarding;
54
37
  exports.abrirMemoria = abrirMemoria;
55
38
  exports.tagsDoProjeto = tagsDoProjeto;
56
39
  exports.situacaoDaThread = situacaoDaThread;
57
40
  exports.situacaoDaFase = situacaoDaFase;
58
41
  exports.skillDaColecao = skillDaColecao;
42
+ exports.agenteDaThread = agenteDaThread;
59
43
  exports.tagsDaThread = tagsDaThread;
60
44
  exports.gravarDecisoes = gravarDecisoes;
45
+ exports.identidadeDoHandoff = identidadeDoHandoff;
46
+ exports.origemCanonicaDoHandoff = origemCanonicaDoHandoff;
47
+ exports.prepararHandoffGovernado = prepararHandoffGovernado;
61
48
  exports.gravarHandoff = gravarHandoff;
62
49
  exports.gravarPolicies = gravarPolicies;
63
50
  exports.gravarLearning = gravarLearning;
@@ -79,19 +66,132 @@ exports.publicarPropostas = publicarPropostas;
79
66
  exports.textoDoEstado = textoDoEstado;
80
67
  exports.textoDoSync = textoDoSync;
81
68
  exports.parsePrioridade = parsePrioridade;
69
+ const write_activation_1 = require("./write-activation");
70
+ const escopo_escrita_1 = require("./escopo-escrita");
71
+ const project_state_1 = require("./project-state");
72
+ /**
73
+ * A camada de memoria do `ork` (bloco B6): o que o nucleo grava, com que tags, e o que
74
+ * ele injeta de volta no prompt de cada fase.
75
+ *
76
+ * Divisao de trabalho deste bloco:
77
+ * - `orkmind.ts` sabe FALAR com o OrkMind (transporte, degradacao, governanca);
78
+ * - `memoria.ts` (aqui) sabe O QUE o Orkastery grava e le de volta (decisao, handoff,
79
+ * rule, learning, roadmap), e como isso vira contexto de prompt;
80
+ * - `recall.ts` sabe RESOLVER o que ficou fora do prompt, no momento indicado.
81
+ *
82
+ * Nenhuma regra de negocio nova nasce aqui. Decisao, claim, baseline, achado e score
83
+ * continuam sendo do nucleo que ja existe: esta camada os PUBLICA e os RECUPERA.
84
+ *
85
+ * A propriedade que o B6 precisa provar e a da injecao deterministica: um prompt de fase
86
+ * carrega 100 por cento das decisoes fechadas da thread, venham elas do `thread.json`
87
+ * (regime `files`) ou da colecao `decision` (regime `orkmind`). Por isso a injecao NAO
88
+ * depende do OrkMind estar ligado: ela troca de fonte, nao de garantia.
89
+ */
82
90
  const fs = __importStar(require("node:fs"));
83
91
  const path = __importStar(require("node:path"));
92
+ const manifest_1 = require("./manifest");
93
+ const onboarding_1 = require("./onboarding");
94
+ const node_crypto_1 = require("node:crypto");
95
+ const node_util_1 = require("node:util");
96
+ const memoria_humana_1 = require("./memoria-humana");
84
97
  const master_1 = require("./master");
85
98
  const modos_1 = require("./modos");
86
99
  const orkmind_1 = require("./orkmind");
87
100
  const ledger_1 = require("./ledger");
88
101
  const thread_1 = require("./thread");
89
102
  const util_1 = require("./util");
103
+ const licoes_1 = require("./licoes");
104
+ /** Publica somente a entrevista deste projeto; jamais percorre threads. */
105
+ function sincronizarOnboarding(carregado, opcoes = {}) {
106
+ carregado = (0, project_state_1.memoryState)(carregado).loaded;
107
+ const estado = (0, onboarding_1.lerOnboarding)(carregado.raiz);
108
+ const etapas = onboarding_1.ETAPAS_ONBOARDING.filter(e => estado.etapas[e] !== null);
109
+ const resultado = { regime: 'files', motivo: null, respondidas: etapas.length,
110
+ gravadas: 0, duplicadas: 0, falhas: 0, detalhe: '' };
111
+ const degradar = (motivo) => {
112
+ resultado.regime = 'files';
113
+ resultado.motivo = motivo;
114
+ resultado.detalhe = 'Entrevista preservada em arquivos; publicação semântica não concluída. Consulte ork memory status.';
115
+ (0, ledger_1.registrar)((0, manifest_1.dirEstado)(carregado.raiz), 'projeto', ledger_1.TIPOS_DE_EVENTO.memoriaDegradada, { origem: 'onboarding', motivo, respondidas: etapas.length, gravadas: resultado.gravadas, falhas: resultado.falhas });
116
+ return resultado;
117
+ };
118
+ // Os detalhes devolvidos por um driver podem conter credenciais. Só expor códigos locais.
119
+ try {
120
+ const memoria = abrirMemoria(carregado, opcoes);
121
+ if (!memoria.ativo)
122
+ return degradar(memoria.estado.motivo ?? 'orkmind.indisponivel');
123
+ const tenant = memoria.estado.tenant;
124
+ for (const etapa of etapas) {
125
+ const resposta = estado.etapas[etapa];
126
+ const identidade = `onboarding:${encodeURIComponent(tenant)}:${etapa}`;
127
+ const r = memoria.gravar({ collection: 'decision', priority: 'medium',
128
+ content: `Onboarding ${onboarding_1.CONTRATO_ONBOARDING} tenant=${JSON.stringify(tenant)} etapa=${etapa}\n${(0, onboarding_1.jsonCanonico)(resposta.conteudo)}`,
129
+ tags: { ...tagsDoProjeto(carregado.manifesto), skill: ['orkastery-onboarding'],
130
+ situation: ['onboarding', identidade] },
131
+ metadata: { identidade, etapa, tenant, por: resposta.por, respondidaEm: resposta.respondidaEm },
132
+ });
133
+ if (!r.ok)
134
+ resultado.falhas++;
135
+ else if (r.duplicada)
136
+ resultado.duplicadas++;
137
+ else
138
+ resultado.gravadas++;
139
+ }
140
+ if (resultado.falhas)
141
+ return degradar('orkmind.indisponivel');
142
+ resultado.regime = 'orkmind';
143
+ resultado.detalhe = `${resultado.gravadas} nova(s), ${resultado.duplicadas} já existente(s); ${etapas.length} etapa(s) publicada(s).`;
144
+ return resultado;
145
+ }
146
+ catch {
147
+ resultado.falhas++;
148
+ return degradar('orkmind.indisponivel');
149
+ }
150
+ }
151
+ function resolverLeituraRestrita(manifesto, contexto) {
152
+ if (contexto === undefined)
153
+ return undefined;
154
+ if (!contexto || typeof contexto !== 'object' || Array.isArray(contexto) ||
155
+ Object.keys(contexto).some(k => !['thread', 'limite'].includes(k)))
156
+ throw Error('memory.query.invalid');
157
+ const q = (0, orkmind_1.validarConsultaDelimitada)({ collection: 'handoff',
158
+ escopo: (0, orkmind_1.criarEscopoDeLeitura)(manifesto, contexto.thread), tags: {},
159
+ limite: contexto.limite === undefined ? orkmind_1.LIMITE_CONSULTA_PADRAO : contexto.limite });
160
+ return Object.freeze({ ...q.escopo, limite: q.limite });
161
+ }
90
162
  /** Abre a memoria do projeto, ja com o regime efetivo resolvido. */
91
163
  function abrirMemoria(carregado, opcoes = {}) {
164
+ const context = (0, project_state_1.memoryState)(carregado);
165
+ carregado = context.loaded;
166
+ // Pedido invalido nao abre driver nem executa sua verificacao de saude.
167
+ const leituraRestrita = resolverLeituraRestrita(carregado.manifesto, opcoes.leituraRestrita);
92
168
  const { estado, driver } = (0, orkmind_1.resolverRegime)(carregado.manifesto, opcoes.driver ?? null);
93
169
  const ativo = estado.efetivo === 'orkmind' && driver !== null;
170
+ const consultarRestrito = (collection, tags) => {
171
+ if (!leituraRestrita)
172
+ throw Error('memory.query.invalid');
173
+ const q = (0, orkmind_1.validarConsultaDelimitada)({ collection, tags,
174
+ escopo: { tenant: leituraRestrita.tenant, thread: leituraRestrita.thread }, limite: leituraRestrita.limite });
175
+ if (!ativo || !driver)
176
+ return [];
177
+ if (typeof driver.consultar !== 'function')
178
+ throw Error('memory.query.unsupported');
179
+ return (0, orkmind_1.concluirConsultaDelimitada)(driver.consultar(q), q);
180
+ };
94
181
  return {
182
+ configSource: context.source,
183
+ configDivergent: context.divergent,
184
+ get leituraRestrita() { return leituraRestrita; },
185
+ raiz: carregado.raiz,
186
+ submeterHandoff(pedido) {
187
+ if (!ativo || !driver?.submeterHandoff)
188
+ return { ok: false, id: null, duplicada: false,
189
+ collection: 'handoff', detalhe: 'memory.g3.unavailable' };
190
+ if (pedido.tenant !== estado.tenant)
191
+ return { ok: false, id: null, duplicada: false,
192
+ collection: 'handoff', detalhe: 'memory.tenant.mismatch' };
193
+ return driver.submeterHandoff(pedido);
194
+ },
95
195
  estado,
96
196
  ativo,
97
197
  regime: estado.efetivo,
@@ -118,6 +218,14 @@ function abrirMemoria(carregado, opcoes = {}) {
118
218
  return driver.adicionar(entrada);
119
219
  },
120
220
  buscar(consulta) {
221
+ if (leituraRestrita) {
222
+ if (consulta.limite !== undefined && (!Number.isInteger(consulta.limite) ||
223
+ consulta.limite < 1 || consulta.limite > orkmind_1.LIMITE_CONSULTA_MAXIMO))
224
+ throw Error('memory.query.invalid');
225
+ const entradas = consultarRestrito(consulta.collection, consulta.tags);
226
+ // Corte de apresentacao somente depois da janela completa e dos filtros locais.
227
+ return consulta.limite === undefined ? entradas : entradas.slice(0, consulta.limite);
228
+ }
121
229
  if (!ativo || !driver)
122
230
  return [];
123
231
  const colecoes = consulta.collection ? [consulta.collection] : [...orkmind_1.COLECOES_DO_ORK];
@@ -125,6 +233,11 @@ function abrirMemoria(carregado, opcoes = {}) {
125
233
  return (0, orkmind_1.filtrarPorTags)(tudo, consulta);
126
234
  },
127
235
  porId(colecao, id) {
236
+ if (leituraRestrita) {
237
+ if (typeof id !== 'string' || !id || id.length > 512 || /[\x00-\x1f\x7f]/.test(id))
238
+ throw Error('memory.query.invalid');
239
+ return consultarRestrito(colecao, {}).find(e => e.id === id) ?? null;
240
+ }
128
241
  if (!ativo || !driver)
129
242
  return null;
130
243
  return driver.exportar(colecao).find((e) => e.id === id) ?? null;
@@ -141,7 +254,7 @@ function abrirMemoria(carregado, opcoes = {}) {
141
254
  function tagsDoProjeto(manifesto) {
142
255
  return {
143
256
  project: [manifesto.memory.tenant || manifesto.project.name],
144
- agent: ['ork'],
257
+ agent: ['desconhecido:desconhecido', 'ork'],
145
258
  domain: ['orkastery'],
146
259
  };
147
260
  }
@@ -157,14 +270,27 @@ function situacaoDaFase(fase) {
157
270
  function skillDaColecao(colecao) {
158
271
  return `orkastery-${colecao}`;
159
272
  }
273
+ /** Autoria observada; manifesto do executor nao prova autoria historica. */
274
+ function agenteDaThread(thread, slug) {
275
+ const sessao = slug ? thread.sessoes.find(s => s.slug === slug) : thread.sessoes.at(-1);
276
+ if (!sessao)
277
+ return 'desconhecido:desconhecido';
278
+ let modelo = 'desconhecido';
279
+ if (thread.worktree) {
280
+ const evento = (0, ledger_1.lerLedger)((0, thread_1.dirThread)(thread.worktree, thread.id)).find(e => e.tipo === 'phase_dispatch' && e.sessionId === sessao.sessionId);
281
+ if (typeof evento?.model === 'string' && evento.model)
282
+ modelo = evento.model;
283
+ }
284
+ return `${sessao.runtime || 'desconhecido'}:${modelo}`;
285
+ }
160
286
  /** As tags completas de uma entrada de thread. */
161
287
  function tagsDaThread(manifesto, colecao, thread, fase) {
162
- const situacao = [situacaoDaThread(thread.id), `modo:${thread.modo}`];
163
- if (fase)
164
- situacao.push(situacaoDaFase(fase));
288
+ const aplicavel = fase ?? thread.faseAtual;
289
+ const situacao = [colecao, situacaoDaThread(thread.id), `modo:${thread.modo}`, situacaoDaFase(aplicavel)];
165
290
  return {
166
291
  ...tagsDoProjeto(manifesto),
167
- skill: [skillDaColecao(colecao)],
292
+ agent: [agenteDaThread(thread), 'ork'],
293
+ skill: [aplicavel, skillDaColecao(colecao)],
168
294
  situation: situacao,
169
295
  };
170
296
  }
@@ -202,14 +328,15 @@ function gravarDecisoes(memoria, manifesto, thread) {
202
328
  // metadata, entao a identidade da entrada precisa viajar por onde a busca por
203
329
  // tag ja e exata. Sem ela, a mesma decisao voltaria duas vezes ao prompt (uma
204
330
  // do thread.json, outra da memoria), porque nao haveria como deduplicar.
205
- tags: {
331
+ tags: Object.fromEntries(Object.entries({
206
332
  ...tagsDaThread(manifesto, 'decision', thread),
207
333
  situation: [
334
+ 'decision',
208
335
  situacaoDaThread(thread.id),
209
336
  `modo:${thread.modo}`,
210
337
  `decisao:${d.id}`,
211
338
  ],
212
- },
339
+ }).map(([k, v]) => [k, [...new Set(v)].sort()])),
213
340
  priority: 'high',
214
341
  metadata: {
215
342
  thread: thread.id,
@@ -223,43 +350,81 @@ function gravarDecisoes(memoria, manifesto, thread) {
223
350
  }
224
351
  return juntar(resultados, memoria.regime);
225
352
  }
226
- /**
227
- * Grava o pacote de handoff da fase na colecao `handoff`.
228
- *
229
- * O corpo gravado e o RESUMO tipado (contagens, momento, itens criticos), nao o JSON
230
- * inteiro: o handoff completo continua commitado em `.orkastery/threads/<id>/`, e o
231
- * ponteiro do proprio handoff diz onde ele esta. Memoria semantica nao e backup.
232
- */
353
+ /** Identidade independente da worktree: tenant, caminho canonico e bytes da origem. */
354
+ function identidadeDoHandoff(tenant, arquivo, sha256) {
355
+ const digest = (0, node_crypto_1.createHash)('sha256').update([tenant, arquivo, sha256].join('\0')).digest('hex');
356
+ return digest.match(/.{8}/g).join(':');
357
+ }
358
+ function origemCanonicaDoHandoff(arquivo) {
359
+ const normalizado = arquivo.split(path.sep).join('/');
360
+ const inicio = normalizado.lastIndexOf('.orkastery/');
361
+ return inicio >= 0 ? normalizado.slice(inicio) : normalizado;
362
+ }
363
+ /** Adaptacao do v1 para o G3. Ausencias sao declaradas; o original fica integral. */
364
+ function prepararHandoffGovernado(manifesto, thread, handoff, arquivo, textoOriginal, raiz) {
365
+ const tenant = manifesto.memory.tenant || manifesto.project.name;
366
+ if (thread.projeto.name !== manifesto.project.name || handoff.thread !== thread.id) {
367
+ throw new Error('memory.tenant.mismatch');
368
+ }
369
+ if (!(0, node_util_1.isDeepStrictEqual)(JSON.parse(textoOriginal), handoff))
370
+ throw new Error('memory.handoff.source-mismatch');
371
+ if (!handoff.de?.slug || !handoff.para?.slug || !Array.isArray(handoff.inline) ||
372
+ !Array.isArray(handoff.pointers) || !Array.isArray(handoff.summaries))
373
+ throw new Error('memory.handoff.invalid');
374
+ arquivo = origemCanonicaDoHandoff(arquivo);
375
+ const sha256 = (0, node_crypto_1.createHash)('sha256').update(textoOriginal).digest('hex');
376
+ const identidade = identidadeDoHandoff(tenant, arquivo, sha256);
377
+ const anteriores = thread.sessoes.filter(s => s.origem !== 'adocao' && s.fase === handoff.de.fase &&
378
+ Date.parse(s.despachadaEm) <= Date.parse(handoff.geradoEm));
379
+ const exata = anteriores.find(s => s.slug === handoff.de.slug);
380
+ // O v1 exportava thread.slug no campo de.slug. A data/fase identifica a sessao
381
+ // efetivamente em curso quando o arquivo foi produzido, sem atribuir o migrador.
382
+ const sessao = exata ?? (handoff.de.slug === thread.slug ? anteriores.at(-1) : undefined);
383
+ const evento = sessao ? (0, ledger_1.lerLedger)((0, thread_1.dirThread)(raiz, thread.id)).find(e => e.tipo === 'phase_dispatch' && e.sessionId === sessao.sessionId) : undefined;
384
+ const agente = `${sessao?.runtime || 'desconhecido'}:${typeof evento?.model === 'string' ? evento.model : 'desconhecido'}`;
385
+ const sessionId = sessao?.sessionId || `origem-declarada:${tenant}:${thread.id}:${handoff.de.slug}`;
386
+ const autoria = { sessionId, slug: sessao?.slug ?? handoff.de.slug, agente,
387
+ criterio: exata ? 'slug, fase e data do despacho' : sessao ? 'ultima sessao da fase anterior ao arquivo; slug v1 da thread' :
388
+ 'somente slug declarado no arquivo; runtime e modelo desconhecidos',
389
+ evidencia: sessao ? `.orkastery/threads/${thread.id}/thread.json#json:sessoes` : `${arquivo}#json:de` };
390
+ const decisoes = handoff.inline.filter(i => /decis[aã]o/i.test(i.titulo) || i.proveniencia.location.includes('decisoes'));
391
+ const tags = { ...tagsDaThread(manifesto, 'handoff', thread, handoff.para.fase ?? undefined), agent: [agente, 'ork'] };
392
+ const progresso = `Handoff da thread ${thread.id} de ${handoff.de.slug} (${handoff.de.fase}) para ` +
393
+ `${handoff.para.slug} (${handoff.para.fase}), regime ${handoff.memory}.\n` +
394
+ `Triagem: ${handoff.inline.length} inline, ${handoff.pointers.length} ponteiros e ${handoff.summaries.length} resumos.\n` +
395
+ `Arquivo commitado: ${arquivo}\nResumos declarados na origem: ${JSON.stringify(handoff.summaries)}`;
396
+ return { tenant, identidade, sessionId, origem: handoff.de.slug, destino: handoff.para.slug, tags,
397
+ metadata: { tenant, thread: thread.id, arquivo, sha256, bytes: Buffer.byteLength(textoOriginal),
398
+ de: handoff.de, para: handoff.para, autoria },
399
+ payload: {
400
+ progresso,
401
+ decisoes: decisoes.length ? `Decisoes declaradas no pacote original, preservadas a seguir:\n${JSON.stringify(decisoes)}` :
402
+ 'O pacote original nao declara decisoes inline identificaveis. Esta ausencia foi preservada, sem inferir uma decisao.',
403
+ referencias_criticas: `Caminhos e ancoras declarados pelo produtor; hashes integrais permanecem no pacote original:\n${JSON.stringify({
404
+ inline: handoff.inline.map(i => i.proveniencia.location),
405
+ pointers: handoff.pointers.map(p => ({ location: p.location, retrieve_when: p.retrieve_when }))
406
+ })}`,
407
+ proximos_passos: `Destino declarado: ${handoff.para.slug}, fase ${handoff.para.fase ?? 'nao declarada'}. ` +
408
+ `Momentos de recuperacao dos ponteiros: ${JSON.stringify(handoff.pointers.map(p => ({ id: p.id, retrieve_when: p.retrieve_when })))}. ` +
409
+ 'O formato v1 nao declara outra lista de tarefas seguintes; nenhuma foi inferida nesta adaptacao.',
410
+ original: handoff, original_texto: textoOriginal, orkastery_identity: identidade,
411
+ } };
412
+ }
413
+ /** Publica exclusivamente por G3, com readback; arquivo ausente nunca vira pacote vazio. */
233
414
  function gravarHandoff(memoria, manifesto, thread, handoff, caminhoRelativo) {
234
- const criticos = handoff.inline.map((i) => `- ${i.titulo} (${i.proveniencia.location})`).join('\n');
235
- return memoria.gravar({
236
- collection: 'handoff',
237
- content: `Handoff da thread ${thread.id} de ${handoff.de.slug} (${handoff.de.fase}) para ` +
238
- `${handoff.para.slug} (${handoff.para.fase}), regime ${handoff.memory}.\n` +
239
- `Triagem: ${handoff.inline.length} inline (CRITICO), ${handoff.pointers.length} ponteiro(s) ` +
240
- `(IMPORTANTE), ${handoff.summaries.length} resumo(s) (RESUMIVEL).\n` +
241
- `Arquivo commitado: ${caminhoRelativo}\n` +
242
- `Itens criticos:\n${criticos || '- (nenhum)'}`,
243
- tags: {
244
- ...tagsDaThread(manifesto, 'handoff', thread),
245
- situation: [
246
- situacaoDaThread(thread.id),
247
- `modo:${thread.modo}`,
248
- ...(handoff.para.fase ? [situacaoDaFase(handoff.para.fase)] : []),
249
- `slug:${handoff.para.slug}`,
250
- ],
251
- },
252
- priority: 'high',
253
- metadata: {
254
- thread: thread.id,
255
- de: handoff.de,
256
- para: handoff.para,
257
- arquivo: caminhoRelativo,
258
- inline: handoff.inline.length,
259
- pointers: handoff.pointers.length,
260
- summaries: handoff.summaries.length,
261
- },
262
- });
415
+ try {
416
+ const arquivo = path.resolve(memoria.raiz, caminhoRelativo);
417
+ const textoOriginal = fs.readFileSync(arquivo, 'utf8');
418
+ const pedido = prepararHandoffGovernado(manifesto, thread, handoff, caminhoRelativo, textoOriginal, memoria.raiz);
419
+ const resultado = memoria.submeterHandoff(pedido);
420
+ if (memoria.ativo && !resultado.ok)
421
+ throw new Error(resultado.detalhe);
422
+ return resultado;
423
+ }
424
+ catch {
425
+ // Falha de publicacao precisa reprovar export/sync, preservando o arquivo de origem.
426
+ throw new Error('memory.handoff.failed: fonte, G3 ou readback nao confirmado');
427
+ }
263
428
  }
264
429
  /**
265
430
  * Grava as policies do manifesto na colecao `rule`.
@@ -279,8 +444,8 @@ function gravarPolicies(memoria, manifesto) {
279
444
  'e regra mandatoria do ecossistema.',
280
445
  tags: {
281
446
  ...tagsDoProjeto(manifesto),
282
- skill: [skillDaColecao('rule')],
283
- situation: [`policy:${nome}`, `postura:${postura}`],
447
+ skill: ['GOAL', 'PLAN', 'GO', 'CHECK', 'SHIP', 'MASTER', skillDaColecao('rule')],
448
+ situation: ['policy', `policy:${nome}`, `postura:${postura}`],
284
449
  },
285
450
  priority: 'high',
286
451
  metadata: { policy: nome, postura, origem: 'orkastery.yaml' },
@@ -310,8 +475,9 @@ function gravarLearning(memoria, manifesto, thread, postmortem, master) {
310
475
  `Postmortem: .orkastery/threads/${thread.id}/POSTMORTEM.json`,
311
476
  tags: {
312
477
  ...tagsDoProjeto(manifesto),
313
- skill: [skillDaColecao('learning')],
478
+ skill: ['GOAL', skillDaColecao('learning')],
314
479
  situation: [
480
+ 'learning',
315
481
  situacaoDaThread(thread.id),
316
482
  `score:${master.score}`,
317
483
  ...postmortem.classesDeFalha.map((c) => `classe:${c}`),
@@ -340,7 +506,7 @@ function gravarOrigemDaThread(memoria, manifesto, thread, origem) {
340
506
  `Modo ${(0, modos_1.tagDoModo)(thread.modo)}, base ${thread.base.branch} @ ${thread.base.commit}.`,
341
507
  tags: {
342
508
  ...tagsDaThread(manifesto, 'roadmap', thread),
343
- situation: [situacaoDaThread(thread.id), `origem:${origem.tipo}`],
509
+ situation: ['roadmap', situacaoDaThread(thread.id), `origem:${origem.tipo}`],
344
510
  },
345
511
  priority: 'medium',
346
512
  metadata: {
@@ -370,8 +536,9 @@ function gravarPropostaDeAuditoria(memoria, manifesto, achado) {
370
536
  `Claim do auditor: ${achado.claim.alegacao} (verificar com: ${achado.claim.verificar.join(' && ') || 'sem comando'})`,
371
537
  tags: {
372
538
  ...tagsDoProjeto(manifesto),
373
- skill: [skillDaColecao('roadmap')],
539
+ skill: ['GOAL', skillDaColecao('roadmap')],
374
540
  situation: [
541
+ 'proposta_auditoria',
375
542
  `achado:${achado.id}`,
376
543
  `rodada:${achado.rodada}`,
377
544
  `pack:${achado.pack}`,
@@ -453,6 +620,9 @@ function decisoesDaThreadNaMemoria(memoria, manifesto, thread) {
453
620
  * de ter fechado, e repetir a propria licao no GOAL so gasta janela.
454
621
  */
455
622
  function licoesDoProduto(memoria, manifesto, threadCorrente, limite = 5) {
623
+ if (memoria.ativo && memoria.leituraRestrita) {
624
+ throw Error('memory.query.broad-operation: licoes do produto exigem contexto de manutencao ou regime files');
625
+ }
456
626
  const todas = memoria.buscar({
457
627
  collection: 'learning',
458
628
  tags: {
@@ -487,6 +657,7 @@ function primeiraLinha(texto) {
487
657
  * - Todo item declara a proveniencia: `path#ancora` ou `orkmind://<colecao>/<id>`.
488
658
  */
489
659
  function injecaoDaFase(carregado, memoria, thread, fase) {
660
+ carregado = (0, project_state_1.memoryState)(carregado).loaded;
490
661
  const { manifesto } = carregado;
491
662
  const itens = [];
492
663
  const caminhoThread = `.orkastery/threads/${thread.id}/thread.json`;
@@ -545,6 +716,23 @@ function injecaoDaFase(carregado, memoria, thread, fase) {
545
716
  });
546
717
  }
547
718
  }
719
+ // I-55 (RM-008): a licao dos POSTMORTEMs em disco volta no GOAL e no PLAN, em qualquer regime.
720
+ // E um item so, agregado: o que mais bloqueou, o que mais fechou thread e as notas baixas. No
721
+ // GOAL ela so entra quando o OrkMind nao trouxe licao, para o prompt nao repetir a mesma thread.
722
+ if (fase === 'PLAN' || (fase === 'GOAL' && licoes === 0)) {
723
+ const texto = (0, licoes_1.textoDasLicoes)((0, licoes_1.resumoDasLicoes)(carregado.raiz, thread.id));
724
+ if (texto) {
725
+ licoes += 1;
726
+ add({
727
+ colecao: 'learning',
728
+ titulo: 'Licoes das threads fechadas deste produto (POSTMORTEM e MASTER)',
729
+ conteudo: texto,
730
+ origem: 'postmortem',
731
+ location: '.orkastery/threads/*/POSTMORTEM.json',
732
+ mandatory: false,
733
+ });
734
+ }
735
+ }
548
736
  // 3. Regras `mandatory` do ecossistema: elas sempre voltam quando as tags casam, em
549
737
  // qualquer fase. O `ork` nunca as cria; ele apenas as respeita.
550
738
  let regras = 0;
@@ -628,22 +816,35 @@ function caminhoRelativoDoHandoff(raiz, threadId) {
628
816
  * vezes nao duplica decisao nem licao. Em regime `files` ele nao falha: ele relata que
629
817
  * nada foi para a memoria semantica, e por que.
630
818
  */
631
- function sincronizarMemoria(carregado, memoria, threadId, origem) {
819
+ function sincronizarMemoria(carregado, memoria, threadId, origem, perfil = 'integral') {
820
+ carregado = (0, project_state_1.memoryState)(carregado).loaded;
632
821
  const { raiz, manifesto } = carregado;
822
+ if (!['fabrica', 'integral'].includes(perfil) || (perfil === 'fabrica' && !threadId))
823
+ throw Error('memory.publication.profile-invalid');
633
824
  const vazio = juntar([], memoria.regime);
634
825
  let decisoes = vazio;
635
826
  let handoff = null;
636
827
  let learning = null;
637
828
  let roadmap = null;
638
- const policies = gravarPolicies(memoria, manifesto);
829
+ if (threadId) {
830
+ (0, escopo_escrita_1.validarDiretorioDeThread)(raiz, threadId);
831
+ const autorizada = (0, thread_1.lerThread)(raiz, threadId);
832
+ if (autorizada.projeto.name !== manifesto.project.name)
833
+ throw new Error('memory.tenant.mismatch');
834
+ }
835
+ const policies = perfil === 'integral' ? gravarPolicies(memoria, manifesto)
836
+ : { ...vazio, detalhe: 'fora do perfil fabrica: rules exigem sync integral; nenhuma tentativa feita' };
837
+ const humanos = threadId ? (0, memoria_humana_1.publicarGatesHumanos)(carregado, memoria, threadId)
838
+ : { inventario: [], resultados: [], elegiveis: 0, excluidos: 0, falhas: 0 };
639
839
  if (threadId) {
640
840
  const thread = (0, thread_1.lerThread)(raiz, threadId);
641
841
  decisoes = gravarDecisoes(memoria, manifesto, thread);
642
- roadmap = gravarOrigemDaThread(memoria, manifesto, thread, origem ?? {
643
- tipo: 'pedido',
644
- referencia: '',
645
- detalhe: `Thread aberta no modo ${(0, modos_1.tagDoModo)(thread.modo)} para "${thread.nome}".`,
646
- });
842
+ if (perfil === 'integral')
843
+ roadmap = gravarOrigemDaThread(memoria, manifesto, thread, origem ?? {
844
+ tipo: 'pedido',
845
+ referencia: '',
846
+ detalhe: `Thread aberta no modo ${(0, modos_1.tagDoModo)(thread.modo)} para "${thread.nome}".`,
847
+ });
647
848
  const caminhoDoHandoff = path.join((0, thread_1.dirThread)(raiz, threadId), 'handoff.json');
648
849
  if (fs.existsSync(caminhoDoHandoff)) {
649
850
  const pacote = (0, util_1.lerJson)(caminhoDoHandoff);
@@ -652,19 +853,22 @@ function sincronizarMemoria(carregado, memoria, threadId, origem) {
652
853
  // A licao so existe depois do MASTER: sem score humano nao ha o que ensinar a
653
854
  // proxima thread, e inventar licao de thread aberta e self-report.
654
855
  const caminhoDoPostmortem = (0, master_1.caminhoPostmortem)(raiz, threadId);
655
- const master = (0, master_1.lerMasterLog)(raiz, threadId);
856
+ const master = perfil === 'integral' ? (0, master_1.lerMasterLog)(raiz, threadId) : null;
656
857
  if (master && fs.existsSync(caminhoDoPostmortem)) {
657
858
  const postmortem = (0, util_1.lerJson)(caminhoDoPostmortem);
658
859
  learning = gravarLearning(memoria, manifesto, thread, postmortem, master);
659
860
  }
660
861
  }
661
862
  const unitarios = [handoff, learning, roadmap].filter((g) => g !== null);
662
- const total = decisoes.gravadas.length + policies.gravadas.length + unitarios.length;
663
- const falhas = decisoes.falhas + policies.falhas + unitarios.filter((g) => !g.ok).length;
863
+ const total = decisoes.gravadas.length + policies.gravadas.length + unitarios.length + humanos.resultados.length;
864
+ const falhas = decisoes.falhas + policies.falhas + unitarios.filter((g) => !g.ok).length + humanos.falhas;
664
865
  return {
665
866
  regime: memoria.regime,
666
867
  estado: memoria.estado,
667
868
  thread: threadId,
869
+ perfil,
870
+ colecoesForaDoPerfil: perfil === 'fabrica' ? ['rule', 'learning', 'roadmap'] : [],
871
+ humanos,
668
872
  decisoes,
669
873
  policies,
670
874
  handoff,
@@ -682,8 +886,29 @@ function sincronizarMemoria(carregado, memoria, threadId, origem) {
682
886
  * resultado dizendo que nada foi gravado, e o comando segue igual.
683
887
  */
684
888
  function publicar(carregado, threadId, origem, memoriaAberta) {
889
+ carregado = (0, project_state_1.memoryState)(carregado).loaded;
890
+ if (threadId)
891
+ (0, escopo_escrita_1.validarDiretorioDeThread)(carregado.raiz, threadId);
892
+ let perfil = 'integral', motivo = null;
893
+ if (!memoriaAberta && carregado.manifesto.memory.mode === 'orkmind') {
894
+ try {
895
+ perfil = (0, write_activation_1.exigirAtivacao)(carregado, threadId ?? '', 'memory').perfil;
896
+ }
897
+ catch (e) {
898
+ motivo = e instanceof Error && /^(write\.activation|scope\.|memory\.tenant)/.test(e.message) ? e.message : 'write.activation.invalid';
899
+ }
900
+ }
685
901
  const memoria = memoriaAberta ?? abrirMemoria(carregado);
686
- const r = sincronizarMemoria(carregado, memoria, threadId, origem);
902
+ if (motivo && memoria.ativo) {
903
+ const vazio = { ...juntar([], memoria.regime), detalhe: motivo };
904
+ return { regime: memoria.regime, estado: memoria.estado, thread: threadId, perfil,
905
+ publicacao: { estado: 'pendente', motivo }, humanos: { inventario: [], resultados: [], elegiveis: 0, excluidos: 0, falhas: 0 },
906
+ decisoes: vazio, policies: vazio, handoff: null, learning: null, roadmap: null, total: 0, falhas: 1 };
907
+ }
908
+ const r = sincronizarMemoria(carregado, memoria, threadId, origem, perfil);
909
+ // O driver files apenas relata tentativas recusadas; conserva a degradacao publicada.
910
+ if (motivo)
911
+ r.publicacao = { estado: 'pendente', motivo };
687
912
  if (threadId && memoria.ativo) {
688
913
  (0, ledger_1.registrar)((0, thread_1.dirThread)(carregado.raiz, threadId), threadId, ledger_1.TIPOS_DE_EVENTO.memoriaGravada, {
689
914
  origem: origem?.tipo ?? 'sync',
@@ -691,6 +916,7 @@ function publicar(carregado, threadId, origem, memoriaAberta) {
691
916
  tenant: memoria.estado.tenant,
692
917
  decisoes: r.decisoes.gravadas.length,
693
918
  policies: r.policies.gravadas.length,
919
+ humanGates: r.humanos.elegiveis,
694
920
  handoff: r.handoff?.id ?? null,
695
921
  learning: r.learning?.id ?? null,
696
922
  roadmap: r.roadmap?.id ?? null,
@@ -706,7 +932,13 @@ function publicar(carregado, threadId, origem, memoriaAberta) {
706
932
  * board em arquivo e chega ao roadmap do produto pela memoria, quando ela esta ligada.
707
933
  */
708
934
  function publicarPropostas(carregado, achados, memoriaAberta) {
935
+ carregado = (0, project_state_1.memoryState)(carregado).loaded;
709
936
  const memoria = memoriaAberta ?? abrirMemoria(carregado);
937
+ // Propostas globais nao herdam uma autorizacao de thread por conveniencia.
938
+ if (!memoriaAberta && carregado.manifesto.memory.mode === 'orkmind')
939
+ return juntar(achados.map(() => ({
940
+ ok: false, id: null, duplicada: false, collection: 'roadmap', detalhe: 'write.activation.operator-required'
941
+ })), memoria.regime);
710
942
  const resultados = achados.map((a) => gravarPropostaDeAuditoria(memoria, carregado.manifesto, a));
711
943
  return juntar(resultados, memoria.regime);
712
944
  }
@@ -734,7 +966,7 @@ function textoDoEstado(estado) {
734
966
  linhas.push(` ${estado.detalhe}`);
735
967
  linhas.push('');
736
968
  linhas.push(` colecoes gravadas: ${orkmind_1.COLECOES_DO_ORK.join(', ')}`);
737
- linhas.push(' governanca: source=agent, mandatory=false, rule/instruction nunca critical.');
969
+ linhas.push(' governanca: source=agent; decision/human exige human_gate comprovado; mandatory=false e regra nunca critical.');
738
970
  }
739
971
  return linhas.join('\n');
740
972
  }
@@ -742,12 +974,16 @@ function textoDoEstado(estado) {
742
974
  function textoDoSync(r) {
743
975
  const linhas = [];
744
976
  linhas.push(`Sincronizacao de memoria (regime ${r.regime})${r.thread ? ` da thread ${r.thread}` : ''}`);
977
+ linhas.push(`Perfil: ${r.perfil ?? 'integral'}; fora do perfil: ${(r.colecoesForaDoPerfil ?? []).join(', ') || '(nenhuma)'}`);
978
+ if (r.publicacao?.estado === 'pendente')
979
+ linhas.push(`Publicacao pendente: ${r.publicacao.motivo}`);
745
980
  linhas.push('');
746
981
  const linha = (nome, s) => {
747
982
  linhas.push(` ${nome.padEnd(10)} ${s.gravadas.length} entrada(s): ${s.detalhe}`);
748
983
  };
749
984
  linha('decision', r.decisoes);
750
985
  linha('rule', r.policies);
986
+ linhas.push(` human_gate ${r.humanos.elegiveis} elegiveis, ${r.humanos.excluidos} excluidos, ${r.humanos.falhas} falhas`);
751
987
  const um = (nome, g) => {
752
988
  linhas.push(` ${nome.padEnd(10)} ${g ? (g.ok ? `${g.duplicada ? 'ja existente' : 'gravado'} (${g.id})` : `nao gravado: ${g.detalhe}`) : '(nada a gravar)'}`);
753
989
  };