@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
@@ -11,12 +11,16 @@ import time as _time
11
11
  import re as _re
12
12
 
13
13
  HOOKS_DIR = os.path.dirname(__file__)
14
+ PROJECT_ROOT = os.path.dirname(HOOKS_DIR)
15
+ if PROJECT_ROOT not in sys.path:
16
+ sys.path.insert(0, PROJECT_ROOT)
14
17
  if HOOKS_DIR not in sys.path:
15
18
  sys.path.insert(0, HOOKS_DIR)
16
19
 
17
- from _common import setup_crash_handler, json_input, get_feature_flag, _resolve_project_dir
18
- from state_migration import resolve_state_file, resolve_state_dir
19
- from _budget import BUDGET_SESSION_TOTAL, BUDGET_SESSION_IDLE
20
+ from hooks._common import setup_crash_handler, json_input, get_feature_flag, _resolve_project_dir
21
+ from hooks.state_migration import resolve_state_file, resolve_state_dir
22
+ from hooks._budget import BUDGET_SESSION_TOTAL, BUDGET_SESSION_IDLE
23
+ from runtime.context_engine import render_profile_digest_text
20
24
 
21
25
  setup_crash_handler("session-start", fail_closed=False)
22
26
 
@@ -43,37 +47,11 @@ project_path = resolve_state_file(project_dir, "state/project.md", "project.md")
43
47
 
44
48
  profile = _read_file(profile_path, 3000)
45
49
  if profile:
46
- lines = [l.strip() for l in profile.split("\n") if l.strip() and not l.strip().startswith("#")]
47
- kv = {}
48
- current_section = ""
49
- for l in lines:
50
- if ":" not in l:
51
- continue
52
- k, v = l.split(":", 1)
53
- k = k.strip().lower()
54
- v = v.strip().strip('"').strip("'")
55
- if k in ("conventions", "ai_behavior"):
56
- current_section = k
57
- continue
58
- if current_section:
59
- kv[f"{current_section}.{k}"] = v
60
- else:
61
- kv[k] = v
62
-
63
- name = kv.get("name", "")
64
- conv_parts = []
65
- for ck in ["conventions.naming", "conventions.test_cmd", "conventions.lint_cmd"]:
66
- if kv.get(ck):
67
- conv_parts.append(f"{ck.split('.')[-1]}={kv[ck]}")
68
- comm = kv.get("ai_behavior.communication", "")
69
-
70
- summary_parts = [name] if name else []
71
- if conv_parts:
72
- summary_parts.append(" ".join(conv_parts))
73
- if comm:
74
- summary_parts.append(f"lang:{comm}")
75
- if summary_parts:
76
- sections.append(f"@project: {' | '.join(summary_parts)}")
50
+ # Inject bounded profile digest skips discoverable info (conventions, ai_behavior)
51
+ # and surfaces only non-discoverable preferences via render_profile_digest_text.
52
+ digest = render_profile_digest_text(project_dir, max_chars=240)
53
+ if digest:
54
+ sections.append(f"@project: profile_digest={digest}")
77
55
  else:
78
56
  project = _read_file(project_path, 1000)
79
57
  if project:
@@ -243,7 +221,7 @@ if os.path.exists(tracker_path):
243
221
  # 6) Recent memory (on-demand)
244
222
  if get_feature_flag('memory'):
245
223
  try:
246
- from _memory import get_recent_memories
224
+ from hooks._memory import get_recent_memories
247
225
  recent = get_recent_memories(project_dir, max_files=3, max_chars_total=150)
248
226
  if recent:
249
227
  sections.append(f'@recent-memory: {recent}')
@@ -7,7 +7,9 @@ from __future__ import annotations
7
7
  import json
8
8
  import logging
9
9
  import os
10
+ import re
10
11
  import sys
12
+ from pathlib import Path
11
13
  from typing import Any, cast
12
14
 
13
15
  import yaml
@@ -35,6 +37,8 @@ from runtime.mcp_config_writers import ( # noqa: E402
35
37
  import runtime.providers.codex_provider # noqa: E402, F401
36
38
  import runtime.providers.gemini_provider # noqa: E402, F401
37
39
  import runtime.providers.kimi_provider # noqa: E402, F401
40
+ from runtime.plugin_interop import discover_host_plugin_state # noqa: E402
41
+ from runtime.validate import run_validate as _run_post_install_validate # noqa: E402
38
42
  from runtime.adoption import ( # noqa: E402
39
43
  CANONICAL_MODE_NAMES,
40
44
  CANONICAL_VERSION,
@@ -191,6 +195,28 @@ MCP_CATALOG: list[dict[str, Any]] = [
191
195
  "default": False,
192
196
  "category": "memory",
193
197
  },
198
+ {
199
+ "id": "notebooklm",
200
+ "name": "NotebookLM",
201
+ "description": "NotebookLM MCP server for AI-powered research and note-taking",
202
+ "command": "npx",
203
+ "args": ["-y", "notebooklm-mcp@latest"],
204
+ "default": False,
205
+ "category": "productivity",
206
+ "warning": (
207
+ "NOTEBOOKLM MCP WARNING\n\n"
208
+ "NotebookLM requires browser automation and has several important considerations:\n\n"
209
+ "BROWSER AUTOMATION: NotebookLM uses Puppeteer for browser automation. "
210
+ "This requires a Chromium/Chrome installation and may consume significant system resources.\n\n"
211
+ "FIRST-RUN DOWNLOAD: On first run, NotebookLM will download a full browser instance "
212
+ "(typically 100-300 MB). This is a one-time operation but may take several minutes.\n\n"
213
+ "DEDICATED ACCOUNT: It is strongly recommended to use a dedicated Google account "
214
+ "for NotebookLM MCP. Do NOT use your primary account, as the MCP will store credentials locally.\n\n"
215
+ "AUTH EXPIRY: Google authentication tokens expire periodically. "
216
+ "You may need to re-authenticate if the MCP fails with auth errors.\n\n"
217
+ "Enable NotebookLM only if you understand these requirements and accept the risks."
218
+ ),
219
+ },
194
220
  ]
195
221
 
196
222
  _MCP_ID_ALIASES: dict[str, str] = {
@@ -451,6 +477,12 @@ def check_auth() -> dict[str, Any]:
451
477
 
452
478
  _HTTP_MEMORY_MIN_LEVEL: int = _PRESET_LEVEL["interop"]
453
479
 
480
+ _PROFILE_ARCH_REQUEST_MAX = 8
481
+ _PROFILE_TAG_MAX = 12
482
+ _PROFILE_SUMMARY_MAX_CHARS = 240
483
+ _PROFILE_RECENT_UPDATES_MAX = 5
484
+ _PROFILE_GOVERNED_MAX = 24
485
+
454
486
 
455
487
  def get_mode_choices() -> list[str]:
456
488
  return list(CANONICAL_MODE_NAMES)
@@ -628,7 +660,7 @@ def set_preferences(project_dir: str, preferences: dict[str, Any]) -> dict[str,
628
660
  "gemini": {"subscription": "free", "max_parallel_agents": 1},
629
661
  "kimi": {"subscription": "free", "max_parallel_agents": 1},
630
662
  }
631
- preset = resolve_preset(cast(str | None, preferences.get("preset")))
663
+ preset = resolve_preset(cast(str, preferences.get("preset") or "safe"))
632
664
  default_config: dict[str, Any] = {
633
665
  "version": CANONICAL_VERSION,
634
666
  "preset": preset,
@@ -660,6 +692,8 @@ def set_preferences(project_dir: str, preferences: dict[str, Any]) -> dict[str,
660
692
  state_dir = os.path.join(project_dir, ".omg", "state")
661
693
  os.makedirs(state_dir, exist_ok=True)
662
694
 
695
+ _write_profile_learning_sections(state_dir, preferences)
696
+
663
697
  # Write config to YAML file
664
698
  config_path = os.path.join(state_dir, "cli-config.yaml")
665
699
  with open(config_path, "w") as f:
@@ -674,6 +708,260 @@ def set_preferences(project_dir: str, preferences: dict[str, Any]) -> dict[str,
674
708
  }
675
709
 
676
710
 
711
+ def _write_profile_learning_sections(state_dir: str, preferences: dict[str, Any]) -> None:
712
+ profile_path = os.path.join(state_dir, "profile.yaml")
713
+ profile_data = _load_profile_yaml(profile_path)
714
+ _ensure_profile_baseline(profile_data)
715
+
716
+ profile_data["preferences"] = _normalize_preferences_block(preferences.get("preferences"))
717
+ profile_data["user_vector"] = _normalize_user_vector_block(preferences.get("user_vector"))
718
+ profile_data["profile_provenance"] = _normalize_provenance_block(preferences.get("profile_provenance"))
719
+ profile_data["governed_preferences"] = _normalize_governed_preferences_block(preferences.get("governed_preferences"))
720
+
721
+ from runtime.profile_io import save_profile
722
+ save_profile(profile_path, profile_data)
723
+
724
+
725
+ def _render_explicit_empty_collections(dumped: str) -> str:
726
+ lines = dumped.splitlines()
727
+ output: list[str] = []
728
+
729
+ def _next_line(start: int) -> str:
730
+ if start + 1 < len(lines):
731
+ return lines[start + 1]
732
+ return ""
733
+
734
+ for idx, line in enumerate(lines):
735
+ next_line = _next_line(idx)
736
+ stripped = line.strip()
737
+
738
+ if stripped == "stack:" and (not next_line or not next_line.startswith("- ")):
739
+ output.append("stack: []")
740
+ continue
741
+ if stripped == "conventions:" and (not next_line or not next_line.startswith(" ")):
742
+ output.append("conventions: {}")
743
+ continue
744
+ if stripped == "ai_behavior:" and (not next_line or not next_line.startswith(" ")):
745
+ output.append("ai_behavior: {}")
746
+ continue
747
+ if stripped == "architecture_requests:" and (not next_line or not next_line.startswith(" - ")):
748
+ output.append(" architecture_requests: []")
749
+ continue
750
+ if stripped == "constraints:" and (not next_line or not next_line.startswith(" ")):
751
+ output.append(" constraints: {}")
752
+ continue
753
+ if stripped == "tags:" and (not next_line or not next_line.startswith(" - ")):
754
+ output.append(" tags: []")
755
+ continue
756
+ if stripped == "recent_updates:" and (not next_line or not next_line.startswith(" - ")):
757
+ output.append(" recent_updates: []")
758
+ continue
759
+
760
+ output.append(line)
761
+
762
+ return "\n".join(output) + "\n"
763
+
764
+
765
+ def _load_profile_yaml(profile_path: str) -> dict[str, Any]:
766
+ from runtime.profile_io import load_profile
767
+ return load_profile(profile_path)
768
+
769
+
770
+ def _ensure_profile_baseline(profile_data: dict[str, Any]) -> None:
771
+ profile_data.setdefault("name", "omg-project")
772
+ profile_data.setdefault("description", "initialized by OMG standalone compat bootstrap")
773
+ profile_data.setdefault("language", "unknown")
774
+ profile_data.setdefault("framework", "unknown")
775
+ profile_data.setdefault("stack", [])
776
+ profile_data.setdefault("conventions", {})
777
+ profile_data.setdefault("ai_behavior", {})
778
+ profile_data.setdefault("governed_preferences", {"style": [], "safety": []})
779
+
780
+
781
+ def _normalize_preferences_block(raw: Any) -> dict[str, Any]:
782
+ block = raw if isinstance(raw, dict) else {}
783
+
784
+ requests_obj = block.get("architecture_requests")
785
+ architecture_requests: list[str] = []
786
+ if isinstance(requests_obj, list):
787
+ for value in requests_obj:
788
+ text = str(value).strip()
789
+ if text:
790
+ architecture_requests.append(text)
791
+ if len(architecture_requests) >= _PROFILE_ARCH_REQUEST_MAX:
792
+ break
793
+
794
+ constraints_obj = block.get("constraints")
795
+ constraints: dict[str, Any] = {}
796
+ if isinstance(constraints_obj, dict):
797
+ for key, value in constraints_obj.items():
798
+ normalized_key = _normalize_constraint_key(str(key))
799
+ normalized_value = _normalize_constraint_value(value)
800
+ if normalized_key and normalized_value is not None:
801
+ constraints[normalized_key] = normalized_value
802
+
803
+ routing_obj = block.get("routing")
804
+ routing_map = routing_obj if isinstance(routing_obj, dict) else {}
805
+ routing = {
806
+ "prefer_clarification": bool(routing_map.get("prefer_clarification", False)),
807
+ }
808
+
809
+ return {
810
+ "architecture_requests": architecture_requests,
811
+ "constraints": constraints,
812
+ "routing": routing,
813
+ }
814
+
815
+
816
+ def _normalize_constraint_key(value: str) -> str:
817
+ normalized = value.strip().lower()
818
+ normalized = re.sub(r"\s+", "_", normalized)
819
+ normalized = re.sub(r"[^a-z0-9_]+", "", normalized)
820
+ return normalized
821
+
822
+
823
+ def _normalize_constraint_value(value: Any) -> str | int | float | bool | None:
824
+ if isinstance(value, bool):
825
+ return value
826
+ if isinstance(value, int):
827
+ return value
828
+ if isinstance(value, float):
829
+ return value
830
+ if value is None:
831
+ return None
832
+ text = str(value).strip().lower()
833
+ return text if text else None
834
+
835
+
836
+ def _normalize_user_vector_block(raw: Any) -> dict[str, Any]:
837
+ block = raw if isinstance(raw, dict) else {}
838
+
839
+ tags_obj = block.get("tags")
840
+ tags: list[str] = []
841
+ if isinstance(tags_obj, list):
842
+ for value in tags_obj:
843
+ token = _normalize_tag_token(str(value))
844
+ if token:
845
+ tags.append(token)
846
+ if len(tags) >= _PROFILE_TAG_MAX:
847
+ break
848
+
849
+ summary = ""
850
+ summary_obj = block.get("summary")
851
+ if isinstance(summary_obj, str):
852
+ summary = " ".join(summary_obj.strip().split())[:_PROFILE_SUMMARY_MAX_CHARS]
853
+
854
+ confidence = 0.0
855
+ confidence_obj = block.get("confidence")
856
+ if isinstance(confidence_obj, (int, float, str)):
857
+ try:
858
+ confidence = float(confidence_obj)
859
+ except (TypeError, ValueError):
860
+ confidence = 0.0
861
+ confidence = max(0.0, min(1.0, confidence))
862
+
863
+ return {
864
+ "tags": tags,
865
+ "summary": summary,
866
+ "confidence": confidence,
867
+ }
868
+
869
+
870
+ def _normalize_tag_token(value: str) -> str:
871
+ normalized = value.strip().lower()
872
+ normalized = re.sub(r"\s+", "_", normalized)
873
+ normalized = re.sub(r"[^a-z0-9_\-]+", "", normalized)
874
+ return normalized
875
+
876
+
877
+ def _normalize_provenance_block(raw: Any) -> dict[str, Any]:
878
+ block = raw if isinstance(raw, dict) else {}
879
+ updates_obj = block.get("recent_updates")
880
+ updates: list[dict[str, str]] = []
881
+ if isinstance(updates_obj, list):
882
+ for entry in updates_obj:
883
+ if not isinstance(entry, dict):
884
+ continue
885
+ run_id = str(entry.get("run_id", "")).strip()
886
+ source = str(entry.get("source", "")).strip()
887
+ field = str(entry.get("field", "")).strip()
888
+ updated_at = str(entry.get("updated_at", "")).strip()
889
+ if not (run_id and source and field and updated_at):
890
+ continue
891
+ updates.append(
892
+ {
893
+ "run_id": run_id,
894
+ "source": source,
895
+ "field": field,
896
+ "updated_at": updated_at,
897
+ }
898
+ )
899
+ if len(updates) >= _PROFILE_RECENT_UPDATES_MAX:
900
+ break
901
+ return {"recent_updates": updates}
902
+
903
+
904
+ def _normalize_governed_preferences_block(raw: Any) -> dict[str, list[dict[str, Any]]]:
905
+ block = raw if isinstance(raw, dict) else {}
906
+ style_raw = block.get("style")
907
+ safety_raw = block.get("safety")
908
+ return {
909
+ "style": _normalize_governed_section_entries(style_raw, section="style"),
910
+ "safety": _normalize_governed_section_entries(safety_raw, section="safety"),
911
+ }
912
+
913
+
914
+ def _normalize_governed_section_entries(raw: Any, *, section: str) -> list[dict[str, Any]]:
915
+ if not isinstance(raw, list):
916
+ return []
917
+ entries: list[dict[str, Any]] = []
918
+ for item in raw:
919
+ if not isinstance(item, dict):
920
+ continue
921
+ field = str(item.get("field", "")).strip()
922
+ value = " ".join(str(item.get("value", "")).strip().split())
923
+ source = str(item.get("source", "")).strip()
924
+ learned_at = str(item.get("learned_at", "")).strip()
925
+ updated_at = str(item.get("updated_at", "")).strip()
926
+ candidate_section = str(item.get("section", section)).strip().lower()
927
+ confirmation_state = str(item.get("confirmation_state", "")).strip().lower()
928
+
929
+ if not (field and value and source and learned_at and updated_at):
930
+ continue
931
+ if candidate_section != section:
932
+ continue
933
+ if confirmation_state not in ("confirmed", "pending_confirmation", "inferred"):
934
+ continue
935
+
936
+ entry: dict[str, Any] = {
937
+ "field": field,
938
+ "value": value,
939
+ "source": source,
940
+ "learned_at": learned_at,
941
+ "updated_at": updated_at,
942
+ "section": section,
943
+ "confirmation_state": confirmation_state,
944
+ }
945
+
946
+ if section == "style" and confirmation_state == "inferred":
947
+ decay_raw = item.get("decay_metadata")
948
+ decay = decay_raw if isinstance(decay_raw, dict) else {}
949
+ try:
950
+ score = float(decay.get("decay_score", 0.0))
951
+ except (TypeError, ValueError):
952
+ score = 0.0
953
+ entry["decay_metadata"] = {
954
+ "decay_score": max(0.0, min(1.0, score)),
955
+ "last_seen_at": str(decay.get("last_seen_at", updated_at)).strip() or updated_at,
956
+ "decay_reason": str(decay.get("decay_reason", "inferred_signal")).strip() or "inferred_signal",
957
+ }
958
+
959
+ entries.append(entry)
960
+ if len(entries) >= _PROFILE_GOVERNED_MAX:
961
+ break
962
+ return entries
963
+
964
+
677
965
  def _write_project_settings_preset(project_dir: str, preset: str) -> None:
678
966
  """Persist preset metadata into project settings when settings.json exists."""
679
967
  settings_path = os.path.join(project_dir, "settings.json")
@@ -745,6 +1033,11 @@ def run_setup_wizard(
745
1033
  preset=selected_preset,
746
1034
  adopt=adopt,
747
1035
  )
1036
+ foreign_plugins_discovered = 0
1037
+ if adoption.get("selected_mode") == "coexist" or selected_preset == "interop":
1038
+ host_plugin_state = discover_host_plugin_state(project_dir)
1039
+ foreign_plugins_discovered = len(host_plugin_state.records)
1040
+ adoption["foreign_plugins_discovered"] = foreign_plugins_discovered
748
1041
 
749
1042
  clis = detect_clis()
750
1043
  auth = check_auth()
@@ -760,7 +1053,7 @@ def run_setup_wizard(
760
1053
  report_path = write_adoption_report(project_dir, adoption)
761
1054
  adoption["report_path"] = report_path
762
1055
 
763
- return {
1056
+ result: dict[str, Any] = {
764
1057
  "status": "complete",
765
1058
  "setup_mode": {
766
1059
  "choices": get_mode_choices(),
@@ -773,3 +1066,50 @@ def run_setup_wizard(
773
1066
  "preferences": prefs,
774
1067
  "adoption": adoption,
775
1068
  }
1069
+
1070
+ # --- Post-install validation (runs AFTER all setup writes are complete) ---
1071
+ try:
1072
+ validation_result = _run_post_install_validate(root_dir=Path(_PROJECT_ROOT))
1073
+ except Exception as exc:
1074
+ validation_result = {
1075
+ "schema": "ValidateResult",
1076
+ "status": "fail",
1077
+ "checks": [
1078
+ {
1079
+ "name": "validation_error",
1080
+ "status": "blocker",
1081
+ "message": str(exc),
1082
+ "required": True,
1083
+ },
1084
+ ],
1085
+ "version": "",
1086
+ }
1087
+
1088
+ # Persist machine-readable validation artifact
1089
+ artifact_dir = os.path.join(project_dir, ".omg", "state")
1090
+ os.makedirs(artifact_dir, exist_ok=True)
1091
+ artifact_path = os.path.join(artifact_dir, "post-install-validation.json")
1092
+ try:
1093
+ with open(artifact_path, "w", encoding="utf-8") as f:
1094
+ json.dump(validation_result, f, indent=2, ensure_ascii=True)
1095
+ f.write("\n")
1096
+ except OSError:
1097
+ artifact_path = ""
1098
+
1099
+ checks = validation_result.get("checks", [])
1100
+ blockers = [
1101
+ c
1102
+ for c in checks
1103
+ if isinstance(c, dict) and c.get("status") == "blocker"
1104
+ ]
1105
+
1106
+ result["post_install_validation"] = {
1107
+ "status": validation_result.get("status", "fail"),
1108
+ "artifact_path": artifact_path,
1109
+ "blockers": [{"name": c["name"], "message": c["message"]} for c in blockers],
1110
+ }
1111
+
1112
+ if blockers:
1113
+ result["status"] = "validation_failed"
1114
+
1115
+ return result
@@ -146,7 +146,13 @@ def map_shadow_path(project_dir: str, run_id: str, file_path: str) -> str:
146
146
  return os.path.join(_shadow_root(project_dir), run_id, "overlay", rel)
147
147
 
148
148
 
149
- def record_shadow_write(project_dir: str, run_id: str, file_path: str, source: str = "tool") -> dict[str, Any]:
149
+ def record_shadow_write(
150
+ project_dir: str,
151
+ run_id: str,
152
+ file_path: str,
153
+ source: str = "tool",
154
+ step_id: str | None = None,
155
+ ) -> dict[str, Any]:
150
156
  run_id = _validated_run_id(run_id)
151
157
  run_dir = os.path.join(_shadow_root(project_dir), run_id)
152
158
  os.makedirs(run_dir, exist_ok=True)
@@ -155,7 +161,8 @@ def record_shadow_write(project_dir: str, run_id: str, file_path: str, source: s
155
161
  os.makedirs(os.path.dirname(shadow_path), exist_ok=True)
156
162
 
157
163
  abs_file = file_path if os.path.isabs(file_path) else os.path.join(project_dir, file_path)
158
- if os.path.exists(abs_file):
164
+ file_existed_before = os.path.exists(abs_file)
165
+ if file_existed_before:
159
166
  shutil.copy2(abs_file, shadow_path)
160
167
  file_hash = _hash_file(abs_file)
161
168
  else:
@@ -169,7 +176,10 @@ def record_shadow_write(project_dir: str, run_id: str, file_path: str, source: s
169
176
  "recorded_at": _utc_now(),
170
177
  "source": source,
171
178
  "sha256": file_hash,
179
+ "file_existed_before": file_existed_before,
172
180
  }
181
+ if step_id:
182
+ entry["step_id"] = step_id
173
183
 
174
184
  files = manifest.get("files", [])
175
185
  # Replace existing entry for same file.
@@ -181,6 +191,72 @@ def record_shadow_write(project_dir: str, run_id: str, file_path: str, source: s
181
191
  return entry
182
192
 
183
193
 
194
+ def restore_shadow_entry(project_dir: str, run_id: str, step_id: str) -> dict[str, Any]:
195
+ run_id = _validated_run_id(run_id)
196
+ run_dir = os.path.join(_shadow_root(project_dir), run_id)
197
+ manifest = _load_manifest(run_dir)
198
+ entries = manifest.get("files", [])
199
+
200
+ restored: list[str] = []
201
+ failed: list[dict[str, str]] = []
202
+
203
+ selected_entries: list[dict[str, Any]] = []
204
+ for item in entries:
205
+ if not isinstance(item, dict):
206
+ continue
207
+ item_step_id = str(item.get("step_id", "")).strip()
208
+ if item_step_id == step_id:
209
+ selected_entries.append(item)
210
+
211
+ if not selected_entries and isinstance(entries, list) and len(entries) == 1:
212
+ single = entries[0]
213
+ if isinstance(single, dict):
214
+ selected_entries = [single]
215
+
216
+ if not selected_entries:
217
+ return {"restored": restored, "failed": failed, "reason": "step not found"}
218
+
219
+ for item in selected_entries:
220
+ rel = str(item.get("file", "")).strip()
221
+ shadow_rel = str(item.get("shadow_file", "")).strip()
222
+ file_existed_before = bool(item.get("file_existed_before", True))
223
+ if not rel:
224
+ failed.append({"file": "", "reason": "missing file path"})
225
+ continue
226
+
227
+ dst = os.path.join(project_dir, rel)
228
+ src = os.path.join(run_dir, shadow_rel) if shadow_rel else ""
229
+
230
+ if src and os.path.exists(src):
231
+ try:
232
+ os.makedirs(os.path.dirname(dst), exist_ok=True)
233
+ shutil.copy2(src, dst)
234
+ restored.append(rel)
235
+ except Exception as exc:
236
+ failed.append({"file": rel, "reason": f"copy failed: {exc}"})
237
+ continue
238
+
239
+ if not file_existed_before:
240
+ try:
241
+ if os.path.exists(dst):
242
+ os.remove(dst)
243
+ restored.append(rel)
244
+ except Exception as exc:
245
+ failed.append({"file": rel, "reason": f"delete failed: {exc}"})
246
+ continue
247
+
248
+ failed.append({"file": rel, "reason": "shadow snapshot missing"})
249
+
250
+ manifest["status"] = "restored"
251
+ manifest["restored_at"] = _utc_now()
252
+ _save_manifest(run_dir, manifest)
253
+
254
+ reason = "shadow restore applied" if restored and not failed else "shadow restore partial"
255
+ if not restored and failed:
256
+ reason = "shadow restore failed"
257
+ return {"restored": restored, "failed": failed, "reason": reason}
258
+
259
+
184
260
  def create_evidence_pack(
185
261
  project_dir: str,
186
262
  run_id: str,
@@ -321,6 +397,34 @@ def drop_shadow(project_dir: str, run_id: str) -> dict[str, Any]:
321
397
  return {"run_id": run_id, "dropped": True}
322
398
 
323
399
 
400
+ def auto_journal_mutation(
401
+ project_dir: str,
402
+ tool: str,
403
+ file_path: str,
404
+ run_id: str,
405
+ ) -> dict[str, Any] | None:
406
+ try:
407
+ from runtime.interaction_journal import InteractionJournal
408
+ from runtime.release_run_coordinator import resolve_current_run_id
409
+ except ImportError:
410
+ return None
411
+
412
+ canonical_run_id = resolve_current_run_id(project_dir) or run_id
413
+ shadow_manifest = os.path.join(
414
+ _shadow_root(project_dir), run_id, "manifest.json"
415
+ )
416
+
417
+ journal = InteractionJournal(project_dir)
418
+ return journal.record_step(
419
+ tool.lower(),
420
+ {
421
+ "file_path": file_path,
422
+ "run_id": canonical_run_id,
423
+ "shadow_manifest": shadow_manifest,
424
+ },
425
+ )
426
+
427
+
324
428
  def _handle_post_tool_use(payload: dict[str, Any]) -> None:
325
429
  tool = payload.get("tool_name", "")
326
430
  if tool not in ("Write", "Edit", "MultiEdit"):
@@ -343,6 +447,11 @@ def _handle_post_tool_use(payload: dict[str, Any]) -> None:
343
447
  run_id = begin_shadow_run(project_dir, metadata={"source": "post-tool-use"})
344
448
  record_shadow_write(project_dir, run_id, file_path)
345
449
 
450
+ try:
451
+ auto_journal_mutation(project_dir, tool, file_path, run_id)
452
+ except Exception:
453
+ pass
454
+
346
455
 
347
456
  def _main() -> int:
348
457
  # Early-exit: skip all work if shadow/evidence mode is not enabled