@trac3er/oh-my-god 2.1.0 → 2.1.4

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 (371) hide show
  1. package/.agents/skills/omg/AGENTS.fragment.md +2 -2
  2. package/.agents/skills/omg/algorithms/SKILL.md +1 -1
  3. package/.agents/skills/omg/algorithms/openai.yaml +1 -1
  4. package/.agents/skills/omg/api-twin/SKILL.md +1 -1
  5. package/.agents/skills/omg/api-twin/openai.yaml +1 -1
  6. package/.agents/skills/omg/claim-judge/SKILL.md +1 -1
  7. package/.agents/skills/omg/claim-judge/openai.yaml +1 -1
  8. package/.agents/skills/omg/codex-rules.md +1 -1
  9. package/.agents/skills/omg/control-plane/SKILL.md +1 -1
  10. package/.agents/skills/omg/control-plane/openai.yaml +1 -1
  11. package/.agents/skills/omg/data-lineage/SKILL.md +1 -1
  12. package/.agents/skills/omg/data-lineage/openai.yaml +1 -1
  13. package/.agents/skills/omg/delta-classifier/SKILL.md +1 -1
  14. package/.agents/skills/omg/delta-classifier/openai.yaml +1 -1
  15. package/.agents/skills/omg/eval-gate/SKILL.md +1 -1
  16. package/.agents/skills/omg/eval-gate/openai.yaml +1 -1
  17. package/.agents/skills/omg/health/SKILL.md +1 -1
  18. package/.agents/skills/omg/health/openai.yaml +1 -1
  19. package/.agents/skills/omg/hook-governor/SKILL.md +1 -1
  20. package/.agents/skills/omg/hook-governor/openai.yaml +1 -1
  21. package/.agents/skills/omg/incident-replay/SKILL.md +1 -1
  22. package/.agents/skills/omg/incident-replay/openai.yaml +1 -1
  23. package/.agents/skills/omg/lsp-pack/SKILL.md +1 -1
  24. package/.agents/skills/omg/lsp-pack/openai.yaml +1 -1
  25. package/.agents/skills/omg/mcp-fabric/SKILL.md +1 -1
  26. package/.agents/skills/omg/mcp-fabric/openai.yaml +1 -1
  27. package/.agents/skills/omg/plan-council/SKILL.md +1 -1
  28. package/.agents/skills/omg/plan-council/openai.yaml +1 -1
  29. package/.agents/skills/omg/preflight/SKILL.md +1 -1
  30. package/.agents/skills/omg/preflight/openai.yaml +1 -1
  31. package/.agents/skills/omg/proof-gate/SKILL.md +1 -1
  32. package/.agents/skills/omg/proof-gate/openai.yaml +1 -1
  33. package/.agents/skills/omg/remote-supervisor/SKILL.md +1 -1
  34. package/.agents/skills/omg/remote-supervisor/openai.yaml +1 -1
  35. package/.agents/skills/omg/robotics/SKILL.md +1 -1
  36. package/.agents/skills/omg/robotics/openai.yaml +1 -1
  37. package/.agents/skills/omg/secure-worktree-pipeline/SKILL.md +1 -1
  38. package/.agents/skills/omg/secure-worktree-pipeline/openai.yaml +1 -1
  39. package/.agents/skills/omg/security-check/SKILL.md +1 -1
  40. package/.agents/skills/omg/security-check/openai.yaml +1 -1
  41. package/.agents/skills/omg/test-intent-lock/SKILL.md +1 -1
  42. package/.agents/skills/omg/test-intent-lock/openai.yaml +1 -1
  43. package/.agents/skills/omg/tracebank/SKILL.md +1 -1
  44. package/.agents/skills/omg/tracebank/openai.yaml +1 -1
  45. package/.agents/skills/omg/vision/SKILL.md +1 -1
  46. package/.agents/skills/omg/vision/openai.yaml +1 -1
  47. package/.claude-plugin/marketplace.json +3 -3
  48. package/.claude-plugin/plugin.json +1 -1
  49. package/CHANGELOG.md +16 -0
  50. package/CLI-ADAPTER-MAP.md +3 -3
  51. package/OMG-setup.sh +114 -1
  52. package/OMG_COMPAT_CONTRACT.md +1 -1
  53. package/README.md +2 -1
  54. package/artifacts/release/.claude-plugin/marketplace.json +3 -3
  55. package/artifacts/release/.claude-plugin/plugin.json +1 -1
  56. package/artifacts/release/OMG_COMPAT_CONTRACT.md +1 -1
  57. package/artifacts/release/dist/enterprise/bundle/.claude-plugin/marketplace.json +3 -3
  58. package/artifacts/release/dist/enterprise/bundle/.claude-plugin/plugin.json +1 -1
  59. package/artifacts/release/dist/enterprise/bundle/OMG_COMPAT_CONTRACT.md +1 -1
  60. package/artifacts/release/dist/enterprise/bundle/plugins/advanced/plugin.json +26 -9
  61. package/artifacts/release/dist/enterprise/bundle/registry/bundles/algorithms.yaml +1 -1
  62. package/artifacts/release/dist/enterprise/bundle/registry/bundles/api-twin.yaml +1 -1
  63. package/artifacts/release/dist/enterprise/bundle/registry/bundles/claim-judge.yaml +1 -1
  64. package/artifacts/release/dist/enterprise/bundle/registry/bundles/control-plane.yaml +1 -1
  65. package/artifacts/release/dist/enterprise/bundle/registry/bundles/data-lineage.yaml +1 -1
  66. package/artifacts/release/dist/enterprise/bundle/registry/bundles/delta-classifier.yaml +1 -1
  67. package/artifacts/release/dist/enterprise/bundle/registry/bundles/eval-gate.yaml +1 -1
  68. package/artifacts/release/dist/enterprise/bundle/registry/bundles/health.yaml +1 -1
  69. package/artifacts/release/dist/enterprise/bundle/registry/bundles/hook-governor.yaml +1 -1
  70. package/artifacts/release/dist/enterprise/bundle/registry/bundles/incident-replay.yaml +1 -1
  71. package/artifacts/release/dist/enterprise/bundle/registry/bundles/lsp-pack.yaml +1 -1
  72. package/artifacts/release/dist/enterprise/bundle/registry/bundles/mcp-fabric.yaml +1 -1
  73. package/artifacts/release/dist/enterprise/bundle/registry/bundles/plan-council.yaml +1 -1
  74. package/artifacts/release/dist/enterprise/bundle/registry/bundles/preflight.yaml +1 -1
  75. package/artifacts/release/dist/enterprise/bundle/registry/bundles/proof-gate.yaml +1 -1
  76. package/artifacts/release/dist/enterprise/bundle/registry/bundles/remote-supervisor.yaml +1 -1
  77. package/artifacts/release/dist/enterprise/bundle/registry/bundles/robotics.yaml +1 -1
  78. package/artifacts/release/dist/enterprise/bundle/registry/bundles/secure-worktree-pipeline.yaml +1 -1
  79. package/artifacts/release/dist/enterprise/bundle/registry/bundles/security-check.yaml +1 -1
  80. package/artifacts/release/dist/enterprise/bundle/registry/bundles/test-intent-lock.yaml +1 -1
  81. package/artifacts/release/dist/enterprise/bundle/registry/bundles/tracebank.yaml +1 -1
  82. package/artifacts/release/dist/enterprise/bundle/registry/bundles/vision.yaml +1 -1
  83. package/artifacts/release/dist/enterprise/bundle/registry/omg-capability.schema.json +1 -1
  84. package/artifacts/release/dist/enterprise/bundle/settings.json +3 -3
  85. package/artifacts/release/dist/enterprise/manifest.json +30 -40
  86. package/artifacts/release/dist/public/bundle/.claude-plugin/marketplace.json +3 -3
  87. package/artifacts/release/dist/public/bundle/.claude-plugin/plugin.json +1 -1
  88. package/artifacts/release/dist/public/bundle/OMG_COMPAT_CONTRACT.md +1 -1
  89. package/artifacts/release/dist/public/bundle/plugins/advanced/plugin.json +26 -9
  90. package/artifacts/release/dist/public/bundle/registry/bundles/algorithms.yaml +1 -1
  91. package/artifacts/release/dist/public/bundle/registry/bundles/api-twin.yaml +1 -1
  92. package/artifacts/release/dist/public/bundle/registry/bundles/claim-judge.yaml +1 -1
  93. package/artifacts/release/dist/public/bundle/registry/bundles/control-plane.yaml +1 -1
  94. package/artifacts/release/dist/public/bundle/registry/bundles/data-lineage.yaml +1 -1
  95. package/artifacts/release/dist/public/bundle/registry/bundles/delta-classifier.yaml +1 -1
  96. package/artifacts/release/dist/public/bundle/registry/bundles/eval-gate.yaml +1 -1
  97. package/artifacts/release/dist/public/bundle/registry/bundles/health.yaml +1 -1
  98. package/artifacts/release/dist/public/bundle/registry/bundles/hook-governor.yaml +1 -1
  99. package/artifacts/release/dist/public/bundle/registry/bundles/incident-replay.yaml +1 -1
  100. package/artifacts/release/dist/public/bundle/registry/bundles/lsp-pack.yaml +1 -1
  101. package/artifacts/release/dist/public/bundle/registry/bundles/mcp-fabric.yaml +1 -1
  102. package/artifacts/release/dist/public/bundle/registry/bundles/plan-council.yaml +1 -1
  103. package/artifacts/release/dist/public/bundle/registry/bundles/preflight.yaml +1 -1
  104. package/artifacts/release/dist/public/bundle/registry/bundles/proof-gate.yaml +1 -1
  105. package/artifacts/release/dist/public/bundle/registry/bundles/remote-supervisor.yaml +1 -1
  106. package/artifacts/release/dist/public/bundle/registry/bundles/robotics.yaml +1 -1
  107. package/artifacts/release/dist/public/bundle/registry/bundles/secure-worktree-pipeline.yaml +1 -1
  108. package/artifacts/release/dist/public/bundle/registry/bundles/security-check.yaml +1 -1
  109. package/artifacts/release/dist/public/bundle/registry/bundles/test-intent-lock.yaml +1 -1
  110. package/artifacts/release/dist/public/bundle/registry/bundles/tracebank.yaml +1 -1
  111. package/artifacts/release/dist/public/bundle/registry/bundles/vision.yaml +1 -1
  112. package/artifacts/release/dist/public/bundle/registry/omg-capability.schema.json +1 -1
  113. package/artifacts/release/dist/public/bundle/settings.json +3 -3
  114. package/artifacts/release/dist/public/manifest.json +30 -40
  115. package/artifacts/release/plugins/advanced/plugin.json +26 -9
  116. package/artifacts/release/registry/bundles/algorithms.yaml +1 -1
  117. package/artifacts/release/registry/bundles/api-twin.yaml +1 -1
  118. package/artifacts/release/registry/bundles/claim-judge.yaml +1 -1
  119. package/artifacts/release/registry/bundles/control-plane.yaml +1 -1
  120. package/artifacts/release/registry/bundles/data-lineage.yaml +1 -1
  121. package/artifacts/release/registry/bundles/delta-classifier.yaml +1 -1
  122. package/artifacts/release/registry/bundles/eval-gate.yaml +1 -1
  123. package/artifacts/release/registry/bundles/health.yaml +1 -1
  124. package/artifacts/release/registry/bundles/hook-governor.yaml +1 -1
  125. package/artifacts/release/registry/bundles/incident-replay.yaml +1 -1
  126. package/artifacts/release/registry/bundles/lsp-pack.yaml +1 -1
  127. package/artifacts/release/registry/bundles/mcp-fabric.yaml +1 -1
  128. package/artifacts/release/registry/bundles/plan-council.yaml +1 -1
  129. package/artifacts/release/registry/bundles/preflight.yaml +1 -1
  130. package/artifacts/release/registry/bundles/proof-gate.yaml +1 -1
  131. package/artifacts/release/registry/bundles/remote-supervisor.yaml +1 -1
  132. package/artifacts/release/registry/bundles/robotics.yaml +1 -1
  133. package/artifacts/release/registry/bundles/secure-worktree-pipeline.yaml +1 -1
  134. package/artifacts/release/registry/bundles/security-check.yaml +1 -1
  135. package/artifacts/release/registry/bundles/test-intent-lock.yaml +1 -1
  136. package/artifacts/release/registry/bundles/tracebank.yaml +1 -1
  137. package/artifacts/release/registry/bundles/vision.yaml +1 -1
  138. package/artifacts/release/registry/omg-capability.schema.json +1 -1
  139. package/artifacts/release/settings.json +3 -3
  140. package/build/lib/commands/OMG:profile-review.md +43 -0
  141. package/build/lib/commands/OMG:validate.md +59 -0
  142. package/build/lib/control_plane/service.py +40 -1
  143. package/build/lib/hooks/_agent_registry.py +2 -0
  144. package/build/lib/hooks/firewall.py +263 -4
  145. package/build/lib/hooks/intentgate-keyword-detector.py +15 -3
  146. package/build/lib/hooks/policy_engine.py +77 -0
  147. package/build/lib/hooks/prompt-enhancer.py +146 -0
  148. package/build/lib/hooks/session-end-capture.py +374 -6
  149. package/build/lib/hooks/session-start.py +13 -35
  150. package/build/lib/hooks/setup_wizard.py +333 -1
  151. package/build/lib/hooks/shadow_manager.py +111 -2
  152. package/build/lib/hooks/stop_dispatcher.py +108 -4
  153. package/build/lib/plugins/advanced/plugin.json +26 -9
  154. package/build/lib/plugins/core/plugin.json +44 -11
  155. package/build/lib/registry/bundles/algorithms.yaml +1 -1
  156. package/build/lib/registry/bundles/api-twin.yaml +1 -1
  157. package/build/lib/registry/bundles/claim-judge.yaml +1 -1
  158. package/build/lib/registry/bundles/control-plane.yaml +1 -1
  159. package/build/lib/registry/bundles/data-lineage.yaml +1 -1
  160. package/build/lib/registry/bundles/delta-classifier.yaml +1 -1
  161. package/build/lib/registry/bundles/eval-gate.yaml +1 -1
  162. package/build/lib/registry/bundles/health.yaml +1 -1
  163. package/build/lib/registry/bundles/hook-governor.yaml +1 -1
  164. package/build/lib/registry/bundles/incident-replay.yaml +1 -1
  165. package/build/lib/registry/bundles/lsp-pack.yaml +1 -1
  166. package/build/lib/registry/bundles/mcp-fabric.yaml +1 -1
  167. package/build/lib/registry/bundles/plan-council.yaml +1 -1
  168. package/build/lib/registry/bundles/preflight.yaml +1 -1
  169. package/build/lib/registry/bundles/proof-gate.yaml +1 -1
  170. package/build/lib/registry/bundles/remote-supervisor.yaml +1 -1
  171. package/build/lib/registry/bundles/robotics.yaml +1 -1
  172. package/build/lib/registry/bundles/secure-worktree-pipeline.yaml +1 -1
  173. package/build/lib/registry/bundles/security-check.yaml +1 -1
  174. package/build/lib/registry/bundles/test-intent-lock.yaml +1 -1
  175. package/build/lib/registry/bundles/tracebank.yaml +1 -1
  176. package/build/lib/registry/bundles/vision.yaml +1 -1
  177. package/build/lib/registry/omg-capability.schema.json +1 -1
  178. package/build/lib/runtime/adoption.py +1 -1
  179. package/build/lib/runtime/background_verification.py +62 -0
  180. package/build/lib/runtime/claim_judge.py +165 -5
  181. package/build/lib/runtime/compat.py +15 -1
  182. package/build/lib/runtime/context_engine.py +261 -4
  183. package/build/lib/runtime/contract_compiler.py +370 -93
  184. package/build/lib/runtime/defense_state.py +79 -8
  185. package/build/lib/runtime/delta_classifier.py +80 -2
  186. package/build/lib/runtime/evidence_query.py +56 -4
  187. package/build/lib/runtime/evidence_registry.py +16 -0
  188. package/build/lib/runtime/evidence_requirements.py +47 -0
  189. package/build/lib/runtime/forge_agents.py +179 -9
  190. package/build/lib/runtime/forge_contracts.py +208 -2
  191. package/build/lib/runtime/interaction_journal.py +318 -42
  192. package/build/lib/runtime/memory_store.py +134 -1
  193. package/build/lib/runtime/mutation_gate.py +57 -11
  194. package/build/lib/runtime/omg_compat_contract_snapshot.json +2 -2
  195. package/build/lib/runtime/omg_mcp_server.py +14 -4
  196. package/build/lib/runtime/preflight.py +14 -0
  197. package/build/lib/runtime/profile_io.py +294 -0
  198. package/build/lib/runtime/proof_gate.py +122 -7
  199. package/build/lib/runtime/release_run_coordinator.py +339 -0
  200. package/build/lib/runtime/release_surfaces.py +243 -0
  201. package/build/lib/runtime/rollback_manifest.py +136 -0
  202. package/build/lib/runtime/router_critics.py +50 -0
  203. package/build/lib/runtime/runtime_contracts.py +137 -0
  204. package/build/lib/runtime/security_check.py +16 -0
  205. package/build/lib/runtime/session_health.py +60 -16
  206. package/build/lib/runtime/team_router.py +156 -13
  207. package/build/lib/runtime/test_intent_lock.py +102 -0
  208. package/build/lib/runtime/tool_plan_gate.py +249 -24
  209. package/build/lib/runtime/validate.py +194 -0
  210. package/build/lib/runtime/verification_controller.py +52 -2
  211. package/commands/OMG:diagnose-plugins.md +33 -0
  212. package/commands/OMG:profile-review.md +43 -0
  213. package/commands/OMG:setup.md +4 -1
  214. package/commands/OMG:validate.md +59 -0
  215. package/control_plane/service.py +40 -1
  216. package/dist/enterprise/bundle/.claude-plugin/marketplace.json +3 -3
  217. package/dist/enterprise/bundle/.claude-plugin/plugin.json +1 -1
  218. package/dist/enterprise/bundle/OMG_COMPAT_CONTRACT.md +1 -1
  219. package/dist/enterprise/bundle/plugins/advanced/plugin.json +26 -9
  220. package/dist/enterprise/bundle/registry/bundles/algorithms.yaml +1 -1
  221. package/dist/enterprise/bundle/registry/bundles/api-twin.yaml +1 -1
  222. package/dist/enterprise/bundle/registry/bundles/claim-judge.yaml +1 -1
  223. package/dist/enterprise/bundle/registry/bundles/control-plane.yaml +1 -1
  224. package/dist/enterprise/bundle/registry/bundles/data-lineage.yaml +1 -1
  225. package/dist/enterprise/bundle/registry/bundles/delta-classifier.yaml +1 -1
  226. package/dist/enterprise/bundle/registry/bundles/eval-gate.yaml +1 -1
  227. package/dist/enterprise/bundle/registry/bundles/health.yaml +1 -1
  228. package/dist/enterprise/bundle/registry/bundles/hook-governor.yaml +1 -1
  229. package/dist/enterprise/bundle/registry/bundles/incident-replay.yaml +1 -1
  230. package/dist/enterprise/bundle/registry/bundles/lsp-pack.yaml +1 -1
  231. package/dist/enterprise/bundle/registry/bundles/mcp-fabric.yaml +1 -1
  232. package/dist/enterprise/bundle/registry/bundles/plan-council.yaml +1 -1
  233. package/dist/enterprise/bundle/registry/bundles/preflight.yaml +1 -1
  234. package/dist/enterprise/bundle/registry/bundles/proof-gate.yaml +1 -1
  235. package/dist/enterprise/bundle/registry/bundles/remote-supervisor.yaml +1 -1
  236. package/dist/enterprise/bundle/registry/bundles/robotics.yaml +1 -1
  237. package/dist/enterprise/bundle/registry/bundles/secure-worktree-pipeline.yaml +1 -1
  238. package/dist/enterprise/bundle/registry/bundles/security-check.yaml +1 -1
  239. package/dist/enterprise/bundle/registry/bundles/test-intent-lock.yaml +1 -1
  240. package/dist/enterprise/bundle/registry/bundles/tracebank.yaml +1 -1
  241. package/dist/enterprise/bundle/registry/bundles/vision.yaml +1 -1
  242. package/dist/enterprise/bundle/registry/omg-capability.schema.json +1 -1
  243. package/dist/enterprise/bundle/settings.json +3 -3
  244. package/dist/enterprise/manifest.json +30 -40
  245. package/dist/oh_my_god-2.1.4-py3-none-any.whl +0 -0
  246. package/dist/oh_my_god-2.1.4.tar.gz +0 -0
  247. package/dist/public/bundle/.claude-plugin/marketplace.json +3 -3
  248. package/dist/public/bundle/.claude-plugin/plugin.json +1 -1
  249. package/dist/public/bundle/OMG_COMPAT_CONTRACT.md +1 -1
  250. package/dist/public/bundle/plugins/advanced/plugin.json +26 -9
  251. package/dist/public/bundle/registry/bundles/algorithms.yaml +1 -1
  252. package/dist/public/bundle/registry/bundles/api-twin.yaml +1 -1
  253. package/dist/public/bundle/registry/bundles/claim-judge.yaml +1 -1
  254. package/dist/public/bundle/registry/bundles/control-plane.yaml +1 -1
  255. package/dist/public/bundle/registry/bundles/data-lineage.yaml +1 -1
  256. package/dist/public/bundle/registry/bundles/delta-classifier.yaml +1 -1
  257. package/dist/public/bundle/registry/bundles/eval-gate.yaml +1 -1
  258. package/dist/public/bundle/registry/bundles/health.yaml +1 -1
  259. package/dist/public/bundle/registry/bundles/hook-governor.yaml +1 -1
  260. package/dist/public/bundle/registry/bundles/incident-replay.yaml +1 -1
  261. package/dist/public/bundle/registry/bundles/lsp-pack.yaml +1 -1
  262. package/dist/public/bundle/registry/bundles/mcp-fabric.yaml +1 -1
  263. package/dist/public/bundle/registry/bundles/plan-council.yaml +1 -1
  264. package/dist/public/bundle/registry/bundles/preflight.yaml +1 -1
  265. package/dist/public/bundle/registry/bundles/proof-gate.yaml +1 -1
  266. package/dist/public/bundle/registry/bundles/remote-supervisor.yaml +1 -1
  267. package/dist/public/bundle/registry/bundles/robotics.yaml +1 -1
  268. package/dist/public/bundle/registry/bundles/secure-worktree-pipeline.yaml +1 -1
  269. package/dist/public/bundle/registry/bundles/security-check.yaml +1 -1
  270. package/dist/public/bundle/registry/bundles/test-intent-lock.yaml +1 -1
  271. package/dist/public/bundle/registry/bundles/tracebank.yaml +1 -1
  272. package/dist/public/bundle/registry/bundles/vision.yaml +1 -1
  273. package/dist/public/bundle/registry/omg-capability.schema.json +1 -1
  274. package/dist/public/bundle/settings.json +3 -3
  275. package/dist/public/manifest.json +30 -40
  276. package/docs/install/opencode.md +30 -0
  277. package/docs/proof.md +11 -0
  278. package/docs/release-checklist.md +14 -1
  279. package/hooks/_agent_registry.py +2 -0
  280. package/hooks/firewall.py +263 -4
  281. package/hooks/intentgate-keyword-detector.py +15 -3
  282. package/hooks/policy_engine.py +77 -0
  283. package/hooks/prompt-enhancer.py +146 -0
  284. package/hooks/session-end-capture.py +374 -6
  285. package/hooks/session-start.py +13 -35
  286. package/hooks/setup_wizard.py +342 -2
  287. package/hooks/shadow_manager.py +111 -2
  288. package/hooks/stop_dispatcher.py +108 -4
  289. package/hud/omg-hud.mjs +30 -6
  290. package/lab/pipeline.py +197 -26
  291. package/package.json +1 -1
  292. package/plugins/advanced/plugin.json +26 -9
  293. package/plugins/core/plugin.json +51 -12
  294. package/pyproject.toml +10 -2
  295. package/registry/bundles/algorithms.yaml +1 -1
  296. package/registry/bundles/api-twin.yaml +1 -1
  297. package/registry/bundles/claim-judge.yaml +1 -1
  298. package/registry/bundles/control-plane.yaml +1 -1
  299. package/registry/bundles/data-lineage.yaml +1 -1
  300. package/registry/bundles/delta-classifier.yaml +1 -1
  301. package/registry/bundles/eval-gate.yaml +1 -1
  302. package/registry/bundles/health.yaml +1 -1
  303. package/registry/bundles/hook-governor.yaml +1 -1
  304. package/registry/bundles/incident-replay.yaml +1 -1
  305. package/registry/bundles/lsp-pack.yaml +1 -1
  306. package/registry/bundles/mcp-fabric.yaml +1 -1
  307. package/registry/bundles/plan-council.yaml +1 -1
  308. package/registry/bundles/preflight.yaml +1 -1
  309. package/registry/bundles/proof-gate.yaml +1 -1
  310. package/registry/bundles/remote-supervisor.yaml +1 -1
  311. package/registry/bundles/robotics.yaml +1 -1
  312. package/registry/bundles/secure-worktree-pipeline.yaml +1 -1
  313. package/registry/bundles/security-check.yaml +1 -1
  314. package/registry/bundles/test-intent-lock.yaml +1 -1
  315. package/registry/bundles/tracebank.yaml +1 -1
  316. package/registry/bundles/vision.yaml +1 -1
  317. package/registry/omg-capability.schema.json +1 -1
  318. package/runtime/adoption.py +7 -5
  319. package/runtime/background_verification.py +62 -0
  320. package/runtime/claim_judge.py +165 -5
  321. package/runtime/compat.py +48 -1
  322. package/runtime/context_engine.py +261 -4
  323. package/runtime/contract_compiler.py +370 -93
  324. package/runtime/defense_state.py +79 -8
  325. package/runtime/delta_classifier.py +80 -2
  326. package/runtime/evidence_query.py +56 -4
  327. package/runtime/evidence_registry.py +16 -0
  328. package/runtime/evidence_requirements.py +47 -0
  329. package/runtime/forge_agents.py +179 -9
  330. package/runtime/forge_contracts.py +208 -2
  331. package/runtime/hook_governor.py +19 -1
  332. package/runtime/interaction_journal.py +318 -42
  333. package/runtime/memory_store.py +134 -1
  334. package/runtime/mutation_gate.py +57 -11
  335. package/runtime/omg_compat_contract_snapshot.json +2 -2
  336. package/runtime/omg_mcp_server.py +14 -4
  337. package/runtime/plugin_diagnostics.py +248 -0
  338. package/runtime/plugin_interop.py +1007 -0
  339. package/runtime/preflight.py +14 -0
  340. package/runtime/profile_io.py +294 -0
  341. package/runtime/proof_gate.py +122 -7
  342. package/runtime/providers/opencode_provider.py +94 -0
  343. package/runtime/release_run_coordinator.py +339 -0
  344. package/runtime/release_surfaces.py +243 -0
  345. package/runtime/rollback_manifest.py +136 -0
  346. package/runtime/router_critics.py +50 -0
  347. package/runtime/runtime_contracts.py +137 -0
  348. package/runtime/security_check.py +16 -0
  349. package/runtime/session_health.py +60 -16
  350. package/runtime/team_router.py +156 -13
  351. package/runtime/test_intent_lock.py +102 -0
  352. package/runtime/tool_plan_gate.py +218 -19
  353. package/runtime/validate.py +289 -0
  354. package/runtime/verification_controller.py +52 -2
  355. package/scripts/omg.py +306 -3
  356. package/scripts/prepare-release-proof-fixtures.py +191 -7
  357. package/scripts/print-canonical-version.py +80 -0
  358. package/scripts/sync-release-identity.py +476 -0
  359. package/scripts/validate-release-identity.py +246 -0
  360. package/scripts/verify-standalone.sh +3 -0
  361. package/settings.json +3 -3
  362. package/templates/plugins-allowlist.yaml +19 -0
  363. package/artifacts/release/dist/enterprise/bundle/.gemini/settings.json +0 -11
  364. package/artifacts/release/dist/enterprise/bundle/.kimi/mcp.json +0 -11
  365. package/artifacts/release/dist/public/bundle/.gemini/settings.json +0 -11
  366. package/artifacts/release/dist/public/bundle/.kimi/mcp.json +0 -11
  367. package/build/lib/yaml.py +0 -321
  368. package/dist/enterprise/bundle/.gemini/settings.json +0 -11
  369. package/dist/enterprise/bundle/.kimi/mcp.json +0 -11
  370. package/dist/public/bundle/.gemini/settings.json +0 -11
  371. package/dist/public/bundle/.kimi/mcp.json +0 -11
@@ -6,10 +6,22 @@ import json
6
6
  import os
7
7
  from pathlib import Path
8
8
  import shutil
9
+ import subprocess
9
10
  import uuid
10
11
  from typing import Any, cast
11
12
 
12
13
  from runtime.repro_pack import build_repro_pack
14
+ from runtime.rollback_manifest import (
15
+ classify_side_effect,
16
+ create_rollback_manifest,
17
+ record_compensating_action,
18
+ record_local_restore,
19
+ record_side_effect,
20
+ write_rollback_manifest,
21
+ )
22
+
23
+
24
+ _MUTATION_TOOLS = frozenset({"write", "edit", "multiedit"})
13
25
 
14
26
 
15
27
  class InteractionJournal:
@@ -19,33 +31,43 @@ class InteractionJournal:
19
31
  def __init__(self, project_dir: str):
20
32
  self.project_dir = Path(project_dir).resolve()
21
33
  self.journal_dir = self.project_dir / ".omg" / "state" / "interaction_journal"
34
+ self._pending_step_id = ""
22
35
 
23
36
  def record_step(self, tool: str, metadata: dict[str, object]) -> dict[str, object]:
24
37
  step_id = self._new_step_id()
38
+ self._pending_step_id = step_id
39
+ normalized_tool = tool.lower().strip()
40
+ run_id = str(metadata.get("run_id", "")).strip()
41
+
25
42
  rollback_mode = "unsupported"
26
- if tool in {"write", "edit"}:
43
+ if normalized_tool in _MUTATION_TOOLS:
27
44
  rollback_mode = self._rollback_mode()
28
45
  checkpoint_ref = self._checkpoint_reference(step_id)
29
46
 
30
47
  shadow_manifest_path = ""
31
- if tool in {"write", "edit"}:
48
+ if normalized_tool in _MUTATION_TOOLS:
32
49
  shadow_manifest_path = self._capture_shadow_metadata(metadata)
33
50
 
34
- repro_pointer = self._build_repro_pointer(step_id)
51
+ repro_pointer = self._build_repro_pointer(run_id)
52
+ side_effect_scope = str(metadata.get("side_effect_scope", "")).strip()
35
53
 
36
54
  payload: dict[str, object] = {
37
55
  "schema": "InteractionJournalStep",
38
56
  "schema_version": "1.0.0",
39
57
  "step_id": step_id,
40
58
  "recorded_at": self._utc_now(),
41
- "tool": tool,
59
+ "tool": normalized_tool,
60
+ "run_id": run_id,
42
61
  "metadata": dict(metadata),
43
62
  "rollback_mode": rollback_mode,
44
63
  "checkpoint_ref": checkpoint_ref,
45
64
  "shadow_manifest_path": shadow_manifest_path,
46
65
  "repro_pointer": repro_pointer,
47
66
  }
67
+ if side_effect_scope:
68
+ payload["side_effect_scope"] = side_effect_scope
48
69
  self._write_step(step_id, payload)
70
+ self._pending_step_id = ""
49
71
 
50
72
  return {
51
73
  "step_id": step_id,
@@ -54,7 +76,7 @@ class InteractionJournal:
54
76
  "repro_pointer": repro_pointer,
55
77
  }
56
78
 
57
- def undo(self, step_id: str) -> dict[str, str]:
79
+ def undo(self, step_id: str, run_id: str | None = None) -> dict[str, object]:
58
80
  target_step_id = step_id.strip() or self._latest_step_id()
59
81
  if not target_step_id:
60
82
  return {"status": "noop", "reason": "no journal entries"}
@@ -66,20 +88,106 @@ class InteractionJournal:
66
88
  tool = str(step.get("tool", "")).strip()
67
89
  metadata = step.get("metadata")
68
90
  metadata_obj = cast(dict[str, object], metadata) if isinstance(metadata, dict) else {}
69
-
70
- if tool not in {"write", "edit"}:
71
- if self._is_external_side_effect(tool, metadata_obj):
72
- return {"status": "unsupported", "reason": "external side effect scope"}
73
- return {"status": "unsupported", "reason": "external side effect scope"}
74
-
75
- shadow_manifest_path = str(step.get("shadow_manifest_path", "")).strip()
76
- if not shadow_manifest_path:
77
- return {"status": "noop", "reason": "missing shadow manifest"}
78
-
79
- restore_result = self._restore_shadow_entry(shadow_manifest_path)
80
- if restore_result:
81
- return {"status": "ok", "reason": "shadow restore applied"}
82
- return {"status": "noop", "reason": "shadow restore unavailable"}
91
+ manifest_run_id = (
92
+ (run_id or "").strip()
93
+ or str(step.get("run_id", "")).strip()
94
+ or self._derive_run_id(step)
95
+ )
96
+ rollback_manifest = create_rollback_manifest(manifest_run_id, target_step_id)
97
+ side_effect = classify_side_effect(tool, metadata_obj)
98
+ record_side_effect(rollback_manifest, side_effect)
99
+ compensating_actions = self._collect_compensating_actions(tool, metadata_obj, side_effect)
100
+
101
+ local_restore: dict[str, object] = {"restored": [], "failed": [], "reason": "shadow restore unavailable"}
102
+ if tool in _MUTATION_TOOLS:
103
+ shadow_manifest_path = str(step.get("shadow_manifest_path", "")).strip()
104
+ if shadow_manifest_path:
105
+ local_restore = self._restore_shadow_entry(target_step_id, shadow_manifest_path)
106
+ restored_value = local_restore.get("restored")
107
+ restored_items = cast(list[object], restored_value) if isinstance(restored_value, list) else []
108
+ for file_path in restored_items:
109
+ record_local_restore(rollback_manifest, str(file_path), "restored")
110
+ failed_value = local_restore.get("failed")
111
+ failed_items = cast(list[object], failed_value) if isinstance(failed_value, list) else []
112
+ for failure in failed_items:
113
+ if not isinstance(failure, dict):
114
+ continue
115
+ record_local_restore(
116
+ rollback_manifest,
117
+ str(failure.get("file", "")),
118
+ "failed",
119
+ str(failure.get("reason", "")),
120
+ )
121
+ else:
122
+ local_restore = {"restored": [], "failed": [], "reason": "missing shadow manifest"}
123
+ elif not compensating_actions:
124
+ return self._finalize_undo(
125
+ rollback_manifest,
126
+ {
127
+ "status": "unsupported",
128
+ "reason": "external side effect scope",
129
+ },
130
+ )
131
+
132
+ snapshot_restore = self._restore_checkpoint(step.get("checkpoint_ref"))
133
+
134
+ action_results = self._execute_compensating_actions(compensating_actions)
135
+ for action_result in action_results:
136
+ record_compensating_action(
137
+ rollback_manifest,
138
+ str(action_result.get("effect_type", "external")),
139
+ str(action_result.get("action", "")),
140
+ str(action_result.get("command", "")),
141
+ status=str(action_result.get("status", "failed")),
142
+ )
143
+
144
+ failed_actions = [row for row in action_results if str(row.get("status", "")) == "failed"]
145
+ restored_files = cast(list[object], local_restore.get("restored", []))
146
+ failed_restores = cast(list[object], local_restore.get("failed", []))
147
+ restored_snapshot = snapshot_restore.get("status") == "restored"
148
+ if failed_actions:
149
+ return self._finalize_undo(
150
+ rollback_manifest,
151
+ {
152
+ "status": "rollback_failed",
153
+ "reason": "compensating actions failed",
154
+ "failed_actions": failed_actions,
155
+ "local_restore": local_restore,
156
+ "snapshot_restore": snapshot_restore,
157
+ },
158
+ )
159
+
160
+ if failed_restores:
161
+ return self._finalize_undo(
162
+ rollback_manifest,
163
+ {
164
+ "status": "partial",
165
+ "reason": str(local_restore.get("reason", "shadow restore partial")),
166
+ "local_restore": local_restore,
167
+ "snapshot_restore": snapshot_restore,
168
+ },
169
+ )
170
+
171
+ if restored_files or restored_snapshot or action_results:
172
+ return self._finalize_undo(
173
+ rollback_manifest,
174
+ {
175
+ "status": "ok",
176
+ "reason": "rollback complete",
177
+ "local_restore": local_restore,
178
+ "snapshot_restore": snapshot_restore,
179
+ "compensating_actions": action_results,
180
+ },
181
+ )
182
+ return self._finalize_undo(
183
+ rollback_manifest,
184
+ {
185
+ "status": "noop",
186
+ "reason": str(local_restore.get("reason", "shadow restore unavailable")),
187
+ "local_restore": local_restore,
188
+ "snapshot_restore": snapshot_restore,
189
+ },
190
+ )
83
191
 
84
192
  def _write_step(self, step_id: str, payload: dict[str, object]) -> None:
85
193
  self.journal_dir.mkdir(parents=True, exist_ok=True)
@@ -119,38 +227,60 @@ class InteractionJournal:
119
227
  return ""
120
228
 
121
229
  run_id = cast(str, begin_shadow_run(str(self.project_dir), metadata={"source": "interaction_journal"}))
122
- _ = record_shadow_write(str(self.project_dir), run_id, str(file_path), source="interaction_journal")
230
+ _ = record_shadow_write(
231
+ str(self.project_dir),
232
+ run_id,
233
+ str(file_path),
234
+ source="interaction_journal",
235
+ step_id=self._pending_step_id,
236
+ )
123
237
  manifest_path = self.project_dir / ".omg" / "shadow" / run_id / "manifest.json"
124
238
  return str(manifest_path)
125
239
  except Exception:
126
240
  return ""
127
241
 
128
- def _restore_shadow_entry(self, shadow_manifest_path: str) -> bool:
242
+ def _restore_shadow_entry(self, step_id: str, shadow_manifest_path: str) -> dict[str, object]:
243
+ manifest_path = Path(shadow_manifest_path)
244
+ run_id = manifest_path.parent.name if manifest_path.name == "manifest.json" else ""
245
+ if not run_id:
246
+ return {"restored": [], "failed": [], "reason": "invalid shadow manifest path"}
247
+
129
248
  try:
130
249
  module = importlib.import_module("hooks.shadow_manager")
131
250
  except Exception:
132
- return False
251
+ return {"restored": [], "failed": [], "reason": "shadow module unavailable"}
133
252
 
134
253
  restore_fn = getattr(module, "restore_shadow_entry", None)
135
254
  if not callable(restore_fn):
136
- return False
137
-
138
- call_variants: list[tuple[tuple[Any, ...], dict[str, Any]]] = [
139
- ((), {"project_dir": str(self.project_dir), "manifest_path": shadow_manifest_path}),
140
- ((), {"project_dir": str(self.project_dir), "shadow_manifest_path": shadow_manifest_path}),
141
- ((), {"manifest_path": shadow_manifest_path}),
142
- ((str(self.project_dir), shadow_manifest_path), {}),
143
- ((shadow_manifest_path,), {}),
144
- ]
145
- for args, kwargs in call_variants:
146
- try:
147
- _ = restore_fn(*args, **kwargs)
148
- return True
149
- except TypeError:
150
- continue
151
- except Exception:
152
- return False
153
- return False
255
+ return {"restored": [], "failed": [], "reason": "restore helper unavailable"}
256
+
257
+ try:
258
+ result = restore_fn(str(self.project_dir), run_id, step_id)
259
+ except Exception:
260
+ return {"restored": [], "failed": [], "reason": "shadow restore unavailable"}
261
+
262
+ if not isinstance(result, dict):
263
+ return {"restored": [], "failed": [], "reason": "shadow restore unavailable"}
264
+
265
+ restored = result.get("restored") if isinstance(result.get("restored"), list) else []
266
+ failed = result.get("failed") if isinstance(result.get("failed"), list) else []
267
+ reason = str(result.get("reason", "shadow restore unavailable"))
268
+ return {"restored": restored, "failed": failed, "reason": reason}
269
+
270
+ def _derive_run_id(self, step: dict[str, object]) -> str:
271
+ manifest_path = str(step.get("shadow_manifest_path", "")).strip()
272
+ manifest = Path(manifest_path)
273
+ if manifest.name == "manifest.json" and manifest.parent.name:
274
+ return manifest.parent.name
275
+
276
+ checkpoint_ref = step.get("checkpoint_ref")
277
+ if isinstance(checkpoint_ref, dict):
278
+ snapshot = checkpoint_ref.get("snapshot")
279
+ if isinstance(snapshot, dict):
280
+ snapshot_id = snapshot.get("id")
281
+ if isinstance(snapshot_id, str) and snapshot_id.strip():
282
+ return snapshot_id.strip()
283
+ return "unknown"
154
284
 
155
285
  def _checkpoint_reference(self, step_id: str) -> dict[str, object] | None:
156
286
  try:
@@ -176,9 +306,12 @@ class InteractionJournal:
176
306
  except Exception:
177
307
  return None
178
308
 
179
- def _build_repro_pointer(self, step_id: str) -> str | None:
309
+ def _build_repro_pointer(self, run_id: str) -> str | None:
310
+ normalized_run_id = run_id.strip()
311
+ if not normalized_run_id:
312
+ return None
180
313
  try:
181
- result = build_repro_pack(str(self.project_dir), step_id)
314
+ result = build_repro_pack(str(self.project_dir), normalized_run_id)
182
315
  except Exception:
183
316
  return None
184
317
  if result.get("status") != "ok":
@@ -211,6 +344,149 @@ class InteractionJournal:
211
344
  return True
212
345
  return False
213
346
 
347
+ def _collect_compensating_actions(
348
+ self,
349
+ tool: str,
350
+ metadata: dict[str, object],
351
+ side_effect: dict[str, object],
352
+ ) -> list[dict[str, str]]:
353
+ action_rows: list[dict[str, str]] = []
354
+
355
+ declared = metadata.get("compensating_action")
356
+ if isinstance(declared, dict):
357
+ action = str(declared.get("action", "")).strip()
358
+ command = str(declared.get("command", "")).strip()
359
+ if action and command:
360
+ action_rows.append(
361
+ {
362
+ "effect_type": str(side_effect.get("category", "external")),
363
+ "action": action,
364
+ "command": command,
365
+ }
366
+ )
367
+
368
+ declared_list = metadata.get("compensating_actions")
369
+ if isinstance(declared_list, list):
370
+ for item in declared_list:
371
+ if not isinstance(item, dict):
372
+ continue
373
+ action = str(item.get("action", "")).strip()
374
+ command = str(item.get("command", "")).strip()
375
+ if not action or not command:
376
+ continue
377
+ action_rows.append(
378
+ {
379
+ "effect_type": str(item.get("effect_type", side_effect.get("category", "external"))),
380
+ "action": action,
381
+ "command": command,
382
+ }
383
+ )
384
+
385
+ if tool == "bash" and not action_rows:
386
+ default_command = str(side_effect.get("default_compensation", "")).strip()
387
+ if default_command:
388
+ action_rows.append(
389
+ {
390
+ "effect_type": str(side_effect.get("category", "external")),
391
+ "action": "default compensation",
392
+ "command": default_command,
393
+ }
394
+ )
395
+ return action_rows
396
+
397
+ def _restore_checkpoint(self, checkpoint_ref: object) -> dict[str, object]:
398
+ if not isinstance(checkpoint_ref, dict):
399
+ return {"status": "skipped", "reason": "checkpoint unavailable"}
400
+
401
+ snapshot = checkpoint_ref.get("snapshot")
402
+ if not isinstance(snapshot, dict):
403
+ return {"status": "skipped", "reason": "snapshot unavailable"}
404
+ snapshot_id = str(snapshot.get("id", "")).strip()
405
+ if not snapshot_id:
406
+ return {"status": "skipped", "reason": "snapshot unavailable"}
407
+
408
+ try:
409
+ module = importlib.import_module("tools.session_snapshot")
410
+ except Exception:
411
+ return {"status": "failed", "reason": "snapshot module unavailable", "snapshot_id": snapshot_id}
412
+
413
+ restore_snapshot = getattr(module, "restore_snapshot", None)
414
+ if not callable(restore_snapshot):
415
+ return {"status": "failed", "reason": "snapshot restore unavailable", "snapshot_id": snapshot_id}
416
+
417
+ try:
418
+ restored = bool(restore_snapshot(snapshot_id, state_dir=str(self.project_dir / ".omg" / "state")))
419
+ except Exception:
420
+ return {"status": "failed", "reason": "snapshot restore unavailable", "snapshot_id": snapshot_id}
421
+ if restored:
422
+ return {"status": "restored", "reason": "snapshot restore applied", "snapshot_id": snapshot_id}
423
+ return {"status": "failed", "reason": "snapshot not found", "snapshot_id": snapshot_id}
424
+
425
+ def _execute_compensating_actions(self, actions: list[dict[str, str]]) -> list[dict[str, object]]:
426
+ results: list[dict[str, object]] = []
427
+ for action in actions:
428
+ command = str(action.get("command", "")).strip()
429
+ if not command:
430
+ results.append(
431
+ {
432
+ "effect_type": str(action.get("effect_type", "external")),
433
+ "action": str(action.get("action", "")),
434
+ "command": command,
435
+ "status": "failed",
436
+ "reason": "missing command",
437
+ }
438
+ )
439
+ continue
440
+ try:
441
+ completed = subprocess.run(
442
+ command,
443
+ cwd=str(self.project_dir),
444
+ shell=True,
445
+ check=False,
446
+ capture_output=True,
447
+ text=True,
448
+ )
449
+ except Exception as exc:
450
+ results.append(
451
+ {
452
+ "effect_type": str(action.get("effect_type", "external")),
453
+ "action": str(action.get("action", "")),
454
+ "command": command,
455
+ "status": "failed",
456
+ "reason": f"execution error: {exc}",
457
+ }
458
+ )
459
+ continue
460
+
461
+ if completed.returncode == 0:
462
+ results.append(
463
+ {
464
+ "effect_type": str(action.get("effect_type", "external")),
465
+ "action": str(action.get("action", "")),
466
+ "command": command,
467
+ "status": "succeeded",
468
+ }
469
+ )
470
+ continue
471
+
472
+ results.append(
473
+ {
474
+ "effect_type": str(action.get("effect_type", "external")),
475
+ "action": str(action.get("action", "")),
476
+ "command": command,
477
+ "status": "failed",
478
+ "reason": (completed.stderr or completed.stdout or "command failed").strip() or "command failed",
479
+ "exit_code": completed.returncode,
480
+ }
481
+ )
482
+ return results
483
+
484
+ def _finalize_undo(self, rollback_manifest: dict[str, object], result: dict[str, object]) -> dict[str, object]:
485
+ manifest_path = write_rollback_manifest(str(self.project_dir), rollback_manifest)
486
+ merged = dict(result)
487
+ merged["manifest_path"] = str(Path(manifest_path).relative_to(self.project_dir)).replace("\\", "/")
488
+ return merged
489
+
214
490
  def _rollback_mode(self) -> str:
215
491
  if shutil.which("git"):
216
492
  return "branch+journal+repro"
@@ -9,12 +9,21 @@ from datetime import datetime, timezone
9
9
  from pathlib import Path
10
10
  from typing import Any
11
11
 
12
+ from runtime.profile_io import classify_preference_section, is_destructive_preference
13
+
12
14
 
13
15
  class MemoryStoreFullError(Exception):
14
16
  """Raised when adding to a store that has reached the 10,000 item limit."""
15
17
 
16
18
 
17
19
  _MAX_ITEMS = 10_000
20
+ _PREFERENCE_SIGNAL_LIMIT = 12
21
+ _PREFERENCE_VALUE_MAX = 160
22
+ _PREFERENCE_ALLOWED_FIELDS = (
23
+ "preferences.architecture_requests",
24
+ "preferences.constraints.",
25
+ "user_vector.tags",
26
+ )
18
27
 
19
28
  # Type alias for memory items — JSON-like dicts.
20
29
  _Item = dict[str, Any] # pyright: ignore[reportExplicitAny]
@@ -207,9 +216,133 @@ class MemoryStore:
207
216
  _ = os.replace(tmp_path, path)
208
217
 
209
218
 
219
+ def project_preference_signals(
220
+ project_dir: str,
221
+ *,
222
+ store_path: str | None = None,
223
+ max_signals: int = _PREFERENCE_SIGNAL_LIMIT,
224
+ ) -> list[dict[str, Any]]:
225
+ """Return bounded preference signals scoped to *project_dir*."""
226
+ canonical_project = os.path.realpath(project_dir)
227
+ if not canonical_project:
228
+ return []
229
+
230
+ limit = max(1, min(int(max_signals), _PREFERENCE_SIGNAL_LIMIT))
231
+ store = MemoryStore(store_path=store_path)
232
+ items = list(reversed(store.list_all()))
233
+ results: list[dict[str, Any]] = []
234
+ seen: set[tuple[str, str, str, str]] = set()
235
+
236
+ for item in items:
237
+ signal = _extract_project_signal(item, canonical_project)
238
+ if signal is None:
239
+ continue
240
+
241
+ signature = (
242
+ str(signal.get("field", "")),
243
+ str(signal.get("value", "")),
244
+ str(signal.get("source", "")),
245
+ str(signal.get("run_id", "")),
246
+ )
247
+ if signature in seen:
248
+ continue
249
+ seen.add(signature)
250
+ results.append(signal)
251
+ if len(results) >= limit:
252
+ break
253
+
254
+ return results
255
+
256
+
257
+ def _extract_project_signal(item: _Item, canonical_project: str) -> dict[str, Any] | None:
258
+ raw_content = item.get("content")
259
+ if not isinstance(raw_content, str):
260
+ return None
261
+
262
+ try:
263
+ payload = json.loads(raw_content)
264
+ except (TypeError, ValueError, json.JSONDecodeError):
265
+ return None
266
+ if not isinstance(payload, dict):
267
+ return None
268
+
269
+ scope = _extract_scope(payload, item)
270
+ if os.path.realpath(scope) != canonical_project:
271
+ return None
272
+
273
+ field = str(payload.get("field", "")).strip()
274
+ if not _is_allowed_field(field):
275
+ return None
276
+
277
+ value = _normalize_value(payload.get("value"))
278
+ if not value:
279
+ return None
280
+
281
+ source = str(payload.get("source", "inferred_observation")).strip().lower() or "inferred_observation"
282
+ confidence = _clamp_confidence(payload.get("confidence"))
283
+ contradicted = bool(payload.get("contradicted") is True)
284
+
285
+ section = classify_preference_section(field, value)
286
+ destructive = is_destructive_preference(field, value)
287
+
288
+ signal: dict[str, Any] = {
289
+ "field": field,
290
+ "value": value,
291
+ "source": source,
292
+ "confidence": confidence,
293
+ "project_scope": canonical_project,
294
+ "run_id": str(payload.get("run_id", "")).strip(),
295
+ "updated_at": str(payload.get("updated_at", item.get("updated_at", ""))).strip(),
296
+ "contradicted": contradicted,
297
+ "section": section,
298
+ "destructive": destructive,
299
+ }
300
+ return signal
301
+
302
+
303
+ def _extract_scope(payload: dict[str, Any], item: _Item) -> str:
304
+ scope = str(payload.get("project_scope", "")).strip()
305
+ if scope:
306
+ return scope
307
+ tags = item.get("tags", [])
308
+ if isinstance(tags, list):
309
+ for tag in tags:
310
+ text = str(tag)
311
+ if text.startswith("project_scope:"):
312
+ return text.split(":", 1)[1].strip()
313
+ return ""
314
+
315
+
316
+ def _is_allowed_field(field: str) -> bool:
317
+ if field == "preferences.architecture_requests":
318
+ return True
319
+ if field == "user_vector.tags":
320
+ return True
321
+ return field.startswith("preferences.constraints.")
322
+
323
+
324
+ def _normalize_value(raw: Any) -> str:
325
+ text = " ".join(str(raw).strip().split())
326
+ if not text:
327
+ return ""
328
+ return text[:_PREFERENCE_VALUE_MAX]
329
+
330
+
331
+ def _clamp_confidence(raw: Any) -> float:
332
+ try:
333
+ score = float(raw)
334
+ except (TypeError, ValueError):
335
+ score = 0.0
336
+ if score < 0.0:
337
+ return 0.0
338
+ if score > 1.0:
339
+ return 1.0
340
+ return round(score, 3)
341
+
342
+
210
343
  def _utc_now_iso() -> str:
211
344
  """Return current UTC time as ISO 8601 string."""
212
345
  return datetime.now(timezone.utc).isoformat()
213
346
 
214
347
 
215
- __all__ = ["MemoryStore", "MemoryStoreFullError"]
348
+ __all__ = ["MemoryStore", "MemoryStoreFullError", "project_preference_signals"]