@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
@@ -0,0 +1,357 @@
1
+ """Transporte de fabrica: OrkMind instalado, stdin JSON, sem provider ou embedder."""
2
+ from __future__ import annotations
3
+ import asyncio
4
+ import contextlib
5
+ import hashlib
6
+ import io
7
+ import json
8
+ import logging
9
+ import os
10
+ import sys
11
+
12
+
13
+ class LegacyProvenanceCollision(ValueError):
14
+ """Mesmo conteudo sem identidade comprovada: preservar e exigir tratamento."""
15
+
16
+
17
+ class ProspectiveMarkerInvalid(ValueError):
18
+ """Integridade do marcador recusada; nao e falha de transporte."""
19
+
20
+
21
+ class NativeSchemaMismatch(ValueError):
22
+ """Biblioteca instalada divergiu do contrato compartilhado; atualizacao exige revisao."""
23
+
24
+
25
+ def native_schema_fields():
26
+ from pathlib import Path
27
+ from orkmind.core.models import MemoryEntry
28
+ contract = json.loads(Path(__file__).with_name('orkmind-native-schema.json').read_text())
29
+ fields = contract.get('fields', [])
30
+ if (contract.get('schema') != 'ork.native-entry-fields/v1'
31
+ or len(fields) != len(set(fields)) or set(fields) != set(MemoryEntry.model_fields)):
32
+ raise NativeSchemaMismatch('memory.native.schema-mismatch')
33
+ return set(fields)
34
+
35
+
36
+ def prospective_agent_valid(value):
37
+ import re
38
+ return (isinstance(value, str) and re.fullmatch('[a-z0-9.-]+:[a-z0-9.-]+', value) is not None
39
+ and 'human' not in value.lower())
40
+
41
+
42
+ class QueryError(ValueError):
43
+ def __init__(self, code):
44
+ self.code = code
45
+ super().__init__(code)
46
+
47
+
48
+ def error_code(error):
49
+ return (error.code if isinstance(error, QueryError)
50
+ else 'memory.native.schema-mismatch' if isinstance(error, NativeSchemaMismatch)
51
+ else 'memory.prospective.marker-invalid' if isinstance(error, ProspectiveMarkerInvalid)
52
+ else 'memory.legacy.provenance-collision' if isinstance(error, LegacyProvenanceCollision)
53
+ else 'memory.schema.absent' if type(error).__name__ == 'UndefinedTable' else 'memory.bridge.failed')
54
+
55
+
56
+ def encoded(value):
57
+ return json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(',', ':'))
58
+
59
+
60
+ PHASES = {'GOAL', 'PLAN', 'GO', 'CHECK', 'SHIP', 'MASTER'}
61
+ FACTORY_KEYS = {'orkastery_identity', 'orkastery_identity_version'}
62
+
63
+
64
+ def prospective_compatible(old, entry):
65
+ """Recibo in-band integral preservado por duplicate sem update; nao autentica o passado."""
66
+ import re
67
+ fields = native_schema_fields()
68
+ try:
69
+ p = old.metadata['orkastery_prospective']
70
+ if (p['schema'] != 'ork.prospective-origin/v1' or p['historicalOrigin'] != 'unknown'
71
+ or p['source'] != 'agent' or p['entryId'] != old.id
72
+ or not re.fullmatch('[a-f0-9]{64}', p['receiptSha256'])
73
+ or not re.fullmatch('[a-f0-9]{40}', p['revision'])
74
+ or not prospective_agent_valid(p['agent'])
75
+ or hashlib.sha256(p['originalJson'].encode()).hexdigest() != p['originalSha256']):
76
+ return False
77
+ original = json.loads(p['originalJson'])
78
+ if set(original) != fields:
79
+ return False
80
+ tags = {k: sorted(set(original['tags'].get(k, [])) | set(entry.tags.get(k, [])))
81
+ for k in set(original['tags']) | set(entry.tags)}
82
+ return (old.collection == entry.collection == 'rule' and old.source == entry.source == 'agent'
83
+ and old.priority == entry.priority == 'high' and old.mandatory is False and old.protected is False
84
+ and old.visibility == original['visibility'] == 'private' and original['id'] == old.id
85
+ and original['content'] == old.content == entry.content and original['source'] == 'agent'
86
+ and original['priority'] == 'high' and original['mandatory'] is False
87
+ and original['protected'] is False and old.author_id == original['author_id']
88
+ and old.scope == original['scope'] == entry.scope and old.tags == tags
89
+ and old.metadata == {**entry.metadata, 'orkastery_prospective': p})
90
+ except (KeyError, TypeError, ValueError):
91
+ return False
92
+
93
+
94
+ def fixed_tags(tags):
95
+ """Fase e runtime sao observacoes; tenant e demais classificacoes sao identidade."""
96
+ fixed = {}
97
+ for key, values in tags.items():
98
+ if key == 'agent':
99
+ continue
100
+ keep = [v for v in values if not (key == 'skill' and v in PHASES)
101
+ and not (key == 'situation' and v.startswith('fase:') and v[5:] in PHASES)]
102
+ if keep:
103
+ fixed[key] = sorted(set(keep))
104
+ return fixed
105
+
106
+
107
+ def legacy_factory_identity(entry):
108
+ """Reconhece somente fingerprints comprovaveis da ponte T5, sem adotar legado."""
109
+ if 'orkastery_identity_version' in entry.metadata:
110
+ return False
111
+ original = {'collection': entry.collection, 'content': entry.content, 'tags': entry.tags,
112
+ 'priority': entry.priority,
113
+ 'metadata': {k: v for k, v in entry.metadata.items() if k not in FACTORY_KEYS}}
114
+ for source in (False, True):
115
+ for mandatory in (False, True):
116
+ candidate = dict(original)
117
+ if source:
118
+ candidate['source'] = entry.source
119
+ if mandatory:
120
+ candidate['mandatory'] = False
121
+ if hashlib.sha256(encoded(candidate).encode()).hexdigest() == entry.metadata.get('orkastery_identity'):
122
+ return True
123
+ return False
124
+
125
+
126
+ def governance(entry):
127
+ if 'scope' in entry and entry['scope'] != 'project':
128
+ raise ValueError('scope outside project contract')
129
+ if entry.get('mandatory') or entry.get('source', 'agent') not in ('agent', 'human'):
130
+ raise ValueError('governance')
131
+ if entry['collection'] not in ('decision', 'handoff', 'rule', 'learning', 'roadmap'):
132
+ raise ValueError('collection')
133
+ if entry.get('priority') == 'critical' and entry['collection'] in ('rule', 'instruction'):
134
+ raise ValueError('critical')
135
+ if entry.get('source') == 'human':
136
+ p = entry.get('metadata', {}).get('proveniencia', {})
137
+ if (entry['collection'] != 'decision' or entry.get('priority') == 'critical'
138
+ or p.get('tipo') != 'human_gate' or p.get('source') != 'human'
139
+ or p.get('confirmado') is not True
140
+ or not all(isinstance(p.get(k), str) and p[k] for k in ('autor', 'evento', 'evidencia', 'sha256'))):
141
+ raise ValueError('human provenance')
142
+
143
+
144
+ async def readback(store, p, handoff):
145
+ package = await store.retrieve(handoff.metadata['package_entry_id'])
146
+ session = await store.retrieve(handoff.parent_id)
147
+ if not package or not session:
148
+ raise ValueError('incomplete chain')
149
+ if (session.collection != 'session' or package.collection != 'semantic_log'
150
+ or package.parent_id != session.id or handoff.parent_id != session.id
151
+ or session.metadata['session_id'] != p['sessionId']
152
+ or handoff.metadata['package_id'] != package.metadata['package_id']
153
+ or handoff.metadata['origin'] != p['origem']
154
+ or handoff.metadata['destination'] != p['destino']
155
+ or json.loads(package.content) != p['payload']):
156
+ raise ValueError('readback mismatch')
157
+ if not all(set(values) <= set(session.tags.get(k, [])) for k, values in p['tags'].items()):
158
+ raise ValueError('session tags mismatch')
159
+ for entry in (handoff, package):
160
+ if entry.metadata['orkastery_identity'] != p['identidade']:
161
+ raise ValueError('identity mismatch')
162
+ if not all(set(values) <= set(entry.tags.get(k, [])) for k, values in p['tags'].items()):
163
+ raise ValueError('tags mismatch')
164
+ if not all(entry.metadata.get(k) == v for k, v in p['metadata'].items()):
165
+ raise ValueError('metadata mismatch')
166
+ return {'ok': True, 'id': handoff.id, 'collection': 'handoff', 'readback': True,
167
+ 'session_entry_id': session.id, 'package_entry_id': package.id,
168
+ 'package_id': package.metadata['package_id'], 'detalhe': 'G3 e readback confirmados'}
169
+
170
+
171
+ def query_contract(request):
172
+ """Somente fronteiras de leitura. Nao cria requester, ACL ou filtro extra."""
173
+ import re
174
+ def text(value, maximum):
175
+ return (isinstance(value, str) and 0 < len(value) <= maximum
176
+ and re.search(r'[\x00-\x1f\x7f]', value) is None)
177
+ valid = isinstance(request, dict) and set(request) == {'op', 'collection', 'tags', 'limit'}
178
+ tags = request.get('tags') if valid else None
179
+ limit = request.get('limit') if valid else None
180
+ if (not valid or request['op'] != 'query'
181
+ or request['collection'] not in ('decision', 'handoff', 'rule', 'learning', 'roadmap')
182
+ or type(limit) is not int or not 1 <= limit <= 1000
183
+ or not isinstance(tags, dict) or set(tags) != {'project', 'situation'}
184
+ or any(not isinstance(v, list) or len(v) != 1 for v in tags.values())
185
+ or not text(tags['project'][0], 128) or not text(tags['situation'][0], 135)
186
+ or re.fullmatch(r'thread:[a-z0-9][a-z0-9_-]{0,127}', tags['situation'][0]) is None):
187
+ raise QueryError('memory.query.invalid')
188
+ return {k: list(v) for k, v in tags.items()}, request['collection'], limit
189
+
190
+
191
+ async def execute(request, store):
192
+ from orkmind.core.models import MemoryEntry
193
+ from orkmind.core.semantic_layer import SemanticLayer
194
+ from orkmind.guardrails.handoff import submeter_handoff
195
+ layer = SemanticLayer(store, embedder=None, semantic_enabled=False)
196
+ op = request['op']
197
+ if op == 'stats':
198
+ return {c: await store.count(c) for c in await store.list_collections()}
199
+ if op == 'query':
200
+ tags, collection, limit = query_contract(request)
201
+ # GovernedStore conserva validade, anti-injection e visibilidade existentes.
202
+ # O transporte desta ponte usa exclusivamente pgvector; nenhum acesso inner.
203
+ entries = await store.search_by_tags(tags, collection=collection, limit=limit)
204
+ if len(entries) >= limit:
205
+ raise QueryError('memory.query.window-saturated')
206
+ if any(e.collection != collection or any(
207
+ not set(values) <= set(e.tags.get(k, [])) for k, values in tags.items())
208
+ for e in entries):
209
+ raise QueryError('memory.query.scope-violation')
210
+ return [e.model_dump(mode='json') for e in entries]
211
+ if op == 'export':
212
+ collection = request['collection']
213
+ count = await store.count(collection)
214
+ entries = await store.search_by_tags({}, collection=collection, limit=count + 1)
215
+ return [e.model_dump(mode='json') for e in entries]
216
+ if op == 'get':
217
+ entry = await store.retrieve(request['id'])
218
+ if entry and entry.collection != request['collection']:
219
+ raise ValueError('collection mismatch')
220
+ return entry.model_dump(mode='json') if entry else None
221
+ if op == 'add':
222
+ data = request['entrada']
223
+ governance(data)
224
+ if set(data['metadata']) & (FACTORY_KEYS | {'orkastery_prospective'}):
225
+ raise ValueError('reserved factory metadata')
226
+ evolving = data['collection'] == 'decision' and data.get('source', 'agent') == 'agent'
227
+ identity_data = data
228
+ if evolving:
229
+ if len(data['tags'].get('project', [])) != 1 or not data['tags']['project'][0]:
230
+ raise ValueError('decision tenant')
231
+ identity_data = {k: data[k] for k in ('collection', 'content', 'priority', 'metadata')}
232
+ identity_data.update(source='agent', tags=fixed_tags(data['tags']))
233
+ identity = hashlib.sha256(encoded(identity_data).encode()).hexdigest()
234
+ metadata = {**data['metadata'], 'orkastery_identity': identity}
235
+ if evolving:
236
+ metadata['orkastery_identity_version'] = 2
237
+ entry = MemoryEntry(content=data['content'], collection=data['collection'], tags=data['tags'],
238
+ priority=data['priority'], source=data.get('source', 'agent'),
239
+ scope='project', mandatory=False, visibility='public',
240
+ metadata=metadata)
241
+ # A native content collision is accepted only when provenance and source agree.
242
+ from orkmind.core.injection import compute_content_hash
243
+ old = await store.find_by_content_hash(compute_content_hash(entry.content), entry.collection)
244
+ if old:
245
+ if 'orkastery_prospective' in old.metadata:
246
+ if not prospective_compatible(old, entry):
247
+ raise ProspectiveMarkerInvalid('prospective receipt mismatch')
248
+ return {'ok': True, 'id': old.id, 'duplicada': True, 'collection': old.collection,
249
+ 'detalhe': 'duplicate; historical-origin-unknown'}
250
+ if not old.metadata.get('orkastery_identity'):
251
+ raise LegacyProvenanceCollision('unproven legacy identity')
252
+ if (old.content != entry.content or old.source != entry.source or old.priority != entry.priority
253
+ or old.mandatory or old.scope != entry.scope or old.visibility != entry.visibility):
254
+ raise ValueError('existing identity mismatch')
255
+ if evolving:
256
+ provenance = {k: v for k, v in old.metadata.items() if k not in FACTORY_KEYS}
257
+ recognized = ((old.metadata.get('orkastery_identity_version') == 2
258
+ and old.metadata.get('orkastery_identity') == identity)
259
+ or legacy_factory_identity(old))
260
+ if not recognized or provenance != data['metadata'] or fixed_tags(old.tags) != fixed_tags(entry.tags):
261
+ raise ValueError('existing decision provenance mismatch')
262
+ tags = {k: sorted(set(old.tags.get(k, [])) | set(entry.tags.get(k, [])))
263
+ for k in set(old.tags) | set(entry.tags)}
264
+ if old.tags != tags or old.metadata != metadata:
265
+ updated = old.model_copy(update={'tags': tags, 'metadata': metadata})
266
+ if not await store.update(old.id, updated):
267
+ raise ValueError('decision update rejected')
268
+ saved = await store.retrieve(old.id)
269
+ if (not saved or saved.tags != tags or saved.metadata != metadata or saved.content != old.content
270
+ or saved.source != old.source or saved.priority != old.priority or saved.mandatory):
271
+ raise ValueError('decision readback')
272
+ elif old.metadata != entry.metadata or old.tags != entry.tags:
273
+ raise ValueError('existing identity mismatch')
274
+ return {'ok': True, 'id': old.id, 'duplicada': True, 'collection': old.collection, 'detalhe': 'duplicate'}
275
+ entry_id, _ = await layer.add_memory(entry)
276
+ saved = await store.retrieve(entry_id)
277
+ if not saved or saved.metadata != entry.metadata or saved.source != entry.source:
278
+ raise ValueError('add readback')
279
+ return {'ok': True, 'id': entry_id, 'duplicada': False, 'collection': entry.collection, 'detalhe': 'created'}
280
+ if op != 'handoff':
281
+ raise ValueError('operation')
282
+ p = request['pedido']
283
+ if set(p['metadata']) & {'package_id', 'session_id', 'origin', 'destination', 'package_entry_id', 'handoff_entry_id', 'orkastery_identity'}:
284
+ raise ValueError('reserved G3 metadata')
285
+ if p['tags'].get('project') != [p['tenant']] or not p['identidade'] or not p['sessionId']:
286
+ raise ValueError('tenant/identity')
287
+ # Native lookup includes retained packages hidden from context search. The payload
288
+ # carries the stable factory identity, so two distinct origins cannot collide.
289
+ from orkmind.core.injection import compute_content_hash
290
+ if p['payload'].get('orkastery_identity') != p['identidade']:
291
+ raise ValueError('payload identity')
292
+ package_hash = compute_content_hash(json.dumps(p['payload'], ensure_ascii=False, indent=2, default=str))
293
+ existing_package = await store.find_by_content_hash(package_hash, 'semantic_log')
294
+ if existing_package:
295
+ existing = await store.retrieve(existing_package.metadata['handoff_entry_id'])
296
+ return {**await readback(store, p, existing), 'duplicada': True}
297
+ result = await submeter_handoff(layer, session_id=p['sessionId'], payload=p['payload'],
298
+ origin=p['origem'], destination=p['destino'], refacao=0)
299
+ if result['status'] != 'armazenado':
300
+ raise ValueError('G3 rejected')
301
+ # G3 owns validation and persistence. Only enrich entries it has just created.
302
+ session = await store.retrieve(result['session_entry_id'])
303
+ session.tags = {**session.tags, **{k: sorted(set(session.tags.get(k, [])) | set(v)) for k, v in p['tags'].items()}}
304
+ session.metadata = {**session.metadata, 'tenant': p['tenant']}
305
+ if not await store.update(session.id, session):
306
+ raise ValueError('session enrichment')
307
+ summary = await store.retrieve(result['handoff_id'])
308
+ package = await store.retrieve(result['package_entry_id'])
309
+ for entry in (summary, package):
310
+ entry.tags = {**entry.tags, **p['tags']}
311
+ entry.metadata = {**entry.metadata, **p['metadata'], 'orkastery_identity': p['identidade'],
312
+ 'package_entry_id': package.id, 'handoff_entry_id': summary.id}
313
+ entry.scope = 'project'
314
+ if not await store.update(entry.id, entry):
315
+ raise ValueError('G3 enrichment')
316
+ summary = await store.retrieve(summary.id)
317
+ return {**await readback(store, p, summary), 'duplicada': False}
318
+
319
+
320
+ async def main(request):
321
+ if isinstance(request, dict) and request.get('op') == 'brain':
322
+ if set(request) != {'op','request'}:
323
+ return {'schema':'orkmind.company-brain-api/v1','state':'conflict','error':'brain.api.invalid'}
324
+ from orkmind.cli.company_brain import service_request
325
+ return service_request(request['request'])
326
+ if isinstance(request, dict) and request.get('op') == 'query':
327
+ query_contract(request)
328
+ native_schema_fields()
329
+ from orkmind.core.config import OrkMindConfig
330
+ from orkmind.store.factory import create_store
331
+ dsn = os.environ.get('ORKMIND_DATABASE_URL', '')
332
+ if not dsn.strip():
333
+ raise ValueError('missing tenant environment')
334
+ store = create_store(OrkMindConfig(database_url=dsn, store_backend='pgvector', embedding_provider=''))
335
+ try:
336
+ if request['op'] in ('add', 'handoff'):
337
+ conn = await store.inner._get_conn()
338
+ async with conn.transaction():
339
+ await conn.execute('SELECT pg_advisory_xact_lock(1869769581)')
340
+ return await execute(request, store)
341
+ return await execute(request, store)
342
+ finally:
343
+ await store.close()
344
+
345
+
346
+ if __name__ == '__main__':
347
+ logging.disable(logging.CRITICAL)
348
+ try:
349
+ request = json.load(sys.stdin)
350
+ with contextlib.redirect_stdout(io.StringIO()), contextlib.redirect_stderr(io.StringIO()):
351
+ result = asyncio.run(main(request))
352
+ print(json.dumps(result, ensure_ascii=False))
353
+ except Exception as error:
354
+ # The factory requires an initialized tenant schema; health never provisions it.
355
+ code = error_code(error)
356
+ print(json.dumps({'error': code}))
357
+ sys.exit(1)
@@ -0,0 +1,67 @@
1
+ """Contrato de fixture externa. Apenas socket sintetico atestado; nunca DSN do ambiente."""
2
+ import json
3
+ import os
4
+ from pathlib import Path
5
+ import re
6
+ import stat
7
+ import time
8
+
9
+
10
+ def receipt_file(filename):
11
+ try:
12
+ path = Path(filename)
13
+ root = path.parent
14
+ if (not path.is_absolute() or not re.fullmatch(r'receipt(?:-[a-f0-9]+)?\.json', path.name)
15
+ or not re.fullmatch(r'/tmp/ork-prospective-fixture-[a-zA-Z0-9-]+', str(root))
16
+ or root.resolve() != root or root.is_symlink()):
17
+ raise ValueError()
18
+ st = root.stat()
19
+ if st.st_uid != os.getuid() or stat.S_IMODE(st.st_mode) != 0o700:
20
+ raise ValueError()
21
+ fd = os.open(path, os.O_RDONLY | os.O_NOFOLLOW)
22
+ try:
23
+ st = os.fstat(fd)
24
+ if not stat.S_ISREG(st.st_mode) or st.st_uid != os.getuid() or stat.S_IMODE(st.st_mode) != 0o600:
25
+ raise ValueError()
26
+ with os.fdopen(fd, closefd=False) as f:
27
+ receipt = json.load(f)
28
+ finally:
29
+ os.close(fd)
30
+ if (receipt['schema'] != 'ork.native-fixture/v1' or receipt['synthetic'] is not True
31
+ or receipt['ownerUid'] != os.getuid() or not receipt['preparedBy'].strip()
32
+ or receipt['socket'] != str(root) or receipt['database'] != 'ork_i06_txn_fixture'
33
+ or receipt['tenant'] != 'synthetic' or receipt['expiresAt'] <= time.time()
34
+ or not re.fullmatch('[a-f0-9]{64}', receipt['container'])
35
+ or not re.fullmatch('sha256:[a-f0-9]{64}', receipt['image'])
36
+ or not re.fullmatch('[a-f0-9]{64}', receipt['identity'])):
37
+ raise ValueError()
38
+ socket = root / '.s.PGSQL.5432'
39
+ st = socket.lstat()
40
+ if not stat.S_ISSOCK(st.st_mode) or st.st_uid != os.getuid():
41
+ raise ValueError()
42
+ return receipt
43
+ except (OSError, ValueError, KeyError, TypeError):
44
+ raise PermissionError('runtime.fixture.invalid') from None
45
+
46
+
47
+ def fixture_dsn(receipt):
48
+ # Values are validated, and credentials/operational connection strings are not inputs.
49
+ from psycopg.conninfo import make_conninfo
50
+ return make_conninfo(dbname=receipt['database'], user='postgres', host=receipt['socket'])
51
+
52
+
53
+ async def connect_fixture(filename):
54
+ import psycopg
55
+ receipt = receipt_file(filename)
56
+ try:
57
+ conn = await psycopg.AsyncConnection.connect(fixture_dsn(receipt), autocommit=True)
58
+ except psycopg.OperationalError:
59
+ raise RuntimeError('runtime.unavailable: external fixture connection') from None
60
+ try:
61
+ row = await (await conn.execute('SELECT identity, tenant FROM public.ork_fixture_identity')).fetchone()
62
+ if row != (receipt['identity'], receipt['tenant']):
63
+ raise PermissionError('runtime.fixture.identity-mismatch')
64
+ except Exception:
65
+ await conn.close()
66
+ raise PermissionError('runtime.fixture.identity-mismatch') from None
67
+ return conn, receipt
@@ -0,0 +1,138 @@
1
+ """Ensaio transacional de preparacao. Sem CLI/apply e restrito a PostgreSQL sintetico.
2
+
3
+ GovernedStore e responsavel por toda escrita; SQL abaixo serve somente aos locks.
4
+ O caminho operacional depende de autoridade nativa para orfas e decisao do condutor.
5
+ """
6
+ import hashlib
7
+ import json
8
+ import uuid
9
+ import re
10
+ from orkmind_fixture import receipt_file
11
+ from orkmind_bridge import prospective_agent_valid, native_schema_fields
12
+ from orkmind.core.models import MemoryEntry
13
+ from orkmind.store.governance import assert_nao_protegida, assert_pode_escrever
14
+
15
+
16
+ def digest(value):
17
+ return hashlib.sha256(json.dumps(value, sort_keys=True, ensure_ascii=False, separators=(',', ':')).encode()).hexdigest()
18
+
19
+
20
+ def dump(entry):
21
+ return entry.model_dump(mode='json')
22
+
23
+
24
+ def same_payload(a, b):
25
+ return {k: v for k, v in a.items() if k not in ('version', 'updated_at')} == {
26
+ k: v for k, v in b.items() if k not in ('version', 'updated_at')}
27
+
28
+
29
+ async def rehearse(native_store, plan, requester, *, fixture_receipt=None, rollback_receipt=None, fail_after=None, locked_probe=None):
30
+ """Lote e compensacao EXATOS, executaveis somente em fixture; requester sempre nomeado."""
31
+ if not isinstance(requester, str) or not requester.strip():
32
+ raise PermissionError('memory.prospective.requester-required')
33
+ if not isinstance(fixture_receipt, str):
34
+ raise PermissionError('runtime.fixture.receipt-required')
35
+ fixture = receipt_file(fixture_receipt)
36
+ native_schema_fields()
37
+ conn = await native_store.inner._get_conn()
38
+ if conn.info.dbname != fixture['database'] or conn.info.host != fixture['socket']:
39
+ raise PermissionError('memory.prospective.operational-unavailable')
40
+ identity = await (await conn.execute('SELECT identity, tenant FROM public.ork_fixture_identity')).fetchone()
41
+ if identity != {'identity': fixture['identity'], 'tenant': fixture['tenant']}:
42
+ raise PermissionError('runtime.fixture.identity-mismatch')
43
+ items = plan['entradas']
44
+ ids = sorted(item['id'] for item in items)
45
+ if len(ids) != 3 or len(set(ids)) != 3:
46
+ raise ValueError('memory.prospective.scope')
47
+ items = sorted(items, key=lambda e: e['id'])
48
+ phase = 'rollback' if rollback_receipt else 'prepare-rehearsal'
49
+ operation_hash = digest({'plan': plan, 'requester': requester, 'rollback': rollback_receipt})
50
+ receipt_id = str(uuid.uuid5(uuid.NAMESPACE_URL, operation_hash + ':' + phase))
51
+ async with conn.transaction():
52
+ # ORDER BY precede o lock; todos os tres IDs sao bloqueados antes de retrieve/update.
53
+ rows = await (await conn.execute('SELECT id FROM memories WHERE id=ANY(%s) ORDER BY id FOR UPDATE', (ids,))).fetchall()
54
+ if [r['id'] for r in rows] != ids:
55
+ raise ValueError('memory.prospective.scope-changed')
56
+ if locked_probe:
57
+ await locked_probe(ids)
58
+ current = [await native_store.retrieve(i) for i in ids]
59
+ identities = await native_store._identidades(requester)
60
+ for entry in current:
61
+ assert_nao_protegida(entry, 'agent', 'update')
62
+ assert_pode_escrever(entry, requester, identities)
63
+ if (entry.collection != 'rule' or entry.source != 'agent' or entry.priority != 'high'
64
+ or entry.mandatory or entry.visibility != 'private'):
65
+ raise PermissionError('memory.prospective.governance')
66
+ expected = [item['antes'] for item in items]
67
+ desired = [item['proposta'] for item in items]
68
+ if rollback_receipt:
69
+ applied = await native_store.retrieve(rollback_receipt)
70
+ if not applied:
71
+ raise ValueError('memory.prospective.receipt-missing')
72
+ previous = json.loads(applied.content)
73
+ if (previous.get('schema') != 'ork.prospective-transaction/v1'
74
+ or previous.get('phase') != 'prepare-rehearsal' or previous.get('rollbackOf') is not None
75
+ or previous.get('planHash') != digest(plan) or previous.get('requester') != requester):
76
+ raise ValueError('memory.prospective.receipt-conflict')
77
+ expected, desired = previous['after'], previous['before']
78
+ receipt = await native_store.retrieve(receipt_id)
79
+ if receipt:
80
+ previous = json.loads(receipt.content)
81
+ if (previous['operationHash'] != operation_hash or previous['requester'] != requester
82
+ or previous['after'] != [dump(e) for e in current]):
83
+ raise ValueError('memory.prospective.replay-conflict')
84
+ return {'receiptId': receipt_id, 'replay': True, 'updated': 0}
85
+ if [dump(e) for e in current] != expected:
86
+ raise ValueError('memory.prospective.snapshot-conflict')
87
+ # Validacao de TODO o lote antes da primeira escrita; preservar todas as colunas.
88
+ targets = []
89
+ for old, target in zip(current, desired):
90
+ if set(target) != set(dump(old)):
91
+ raise ValueError('memory.prospective.snapshot-incomplete')
92
+ changed = {k for k in target if target[k] != dump(old)[k]}
93
+ permitted = {'tags', 'metadata'} | ({'version', 'updated_at'} if rollback_receipt else set())
94
+ if changed - permitted:
95
+ raise ValueError('memory.prospective.field-change')
96
+ if target['tags'].get('editors', []) != old.tags.get('editors', []):
97
+ raise PermissionError('memory.prospective.acl-change')
98
+ if not rollback_receipt:
99
+ try:
100
+ marker = target['metadata']['orkastery_prospective']
101
+ original = marker['originalJson']
102
+ if (set(marker) != {'schema', 'historicalOrigin', 'source', 'entryId', 'agent', 'revision',
103
+ 'receiptSha256', 'originalJson', 'originalSha256'}
104
+ or marker['schema'] != 'ork.prospective-origin/v1'
105
+ or marker['historicalOrigin'] != 'unknown' or marker['source'] != 'agent'
106
+ or not prospective_agent_valid(marker['agent'])
107
+ or not re.fullmatch('[a-f0-9]{40}', marker['revision'])
108
+ or not re.fullmatch('[a-f0-9]{64}', marker['receiptSha256'])
109
+ or marker['entryId'] != old.id or json.loads(original) != dump(old)
110
+ or hashlib.sha256(original.encode()).hexdigest() != marker['originalSha256']):
111
+ raise ValueError()
112
+ except (KeyError, ValueError, TypeError, AttributeError):
113
+ raise ValueError('memory.prospective.marker-invalid') from None
114
+ targets.append(MemoryEntry.model_validate({**target, 'version': old.version, 'updated_at': dump(old)['updated_at']}))
115
+ for n, (old, target) in enumerate(zip(current, targets), 1):
116
+ if await native_store.inner._get_conn() is not conn:
117
+ raise RuntimeError('memory.prospective.connection-changed')
118
+ if not await native_store.update(old.id, target, requester_id=requester):
119
+ raise ValueError('memory.prospective.update-refused')
120
+ saved = await native_store.retrieve(old.id)
121
+ if saved.version != old.version + 1 or not same_payload(dump(saved), dump(target)):
122
+ raise ValueError('memory.prospective.readback')
123
+ if fail_after == n:
124
+ raise RuntimeError('fixture.injected-failure')
125
+ after = [dump(await native_store.retrieve(i)) for i in ids]
126
+ body = {'schema': 'ork.prospective-transaction/v1', 'phase': phase, 'operationHash': operation_hash,
127
+ 'planHash': digest(plan), 'requester': requester, 'before': expected, 'after': after,
128
+ 'rollbackOf': rollback_receipt, 'historicalOrigin': 'unknown'}
129
+ # Sidecar nativo integral, privado e duravel na MESMA transacao; nao memory_versions parcial.
130
+ receipt = MemoryEntry(id=receipt_id, collection='semantic_log', content=json.dumps(body, ensure_ascii=False),
131
+ source='agent', priority='high', mandatory=False, visibility='private',
132
+ scope='project', author_id=requester, tags={'project': current[0].tags['project']},
133
+ metadata={'operationHash': operation_hash, 'historicalOrigin': 'unknown'})
134
+ if await native_store.store(receipt) != receipt_id:
135
+ raise ValueError('memory.prospective.receipt-write')
136
+ if json.loads((await native_store.retrieve(receipt_id)).content) != body:
137
+ raise ValueError('memory.prospective.receipt-readback')
138
+ return {'receiptId': receipt_id, 'replay': False, 'updated': 3}
@@ -0,0 +1,23 @@
1
+ {
2
+ "schema": "ork.reference-tariffs/v1",
3
+ "verifiedAt": "2026-09-08T00:00:00.000Z",
4
+ "currency": "USD",
5
+ "unitTokens": 1000000,
6
+ "nature": "estimated_reference",
7
+ "models": {
8
+ "gpt-6-astra": {
9
+ "product": "Codex Enterprise reference",
10
+ "input": 10,
11
+ "cachedInput": 1,
12
+ "output": 50,
13
+ "source": "https://openai.com/business/codex-pricing/"
14
+ },
15
+ "claude-opus-5": {
16
+ "product": "Claude API reference",
17
+ "input": 5,
18
+ "cachedInput": 0.5,
19
+ "output": 25,
20
+ "source": "https://docs.anthropic.com/en/docs/about-claude/pricing"
21
+ }
22
+ }
23
+ }