@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
@@ -14,7 +14,9 @@ Inspired by earlier OMG routing experiments. Key upgrades:
14
14
 
15
15
  No dependency on CLAUDE.md or AGENTS.md.
16
16
  """
17
+ import hashlib
17
18
  import json, sys, os, re, time
19
+ from datetime import datetime, timezone
18
20
  import importlib
19
21
 
20
22
  HOOKS_DIR = os.path.dirname(__file__)
@@ -77,6 +79,48 @@ def signal_matches_text(signal, text):
77
79
  return signal in text
78
80
  return re.search(r'\b' + re.escape(signal) + r'\b', text, re.IGNORECASE) is not None
79
81
 
82
+
83
+ def _hash_prompt(value):
84
+ return hashlib.sha256(value.encode("utf-8")).hexdigest()
85
+
86
+
87
+ def _single_line(text, max_chars=220):
88
+ line = " ".join(str(text).replace("\r", " ").replace("\n", " ").split())
89
+ if len(line) > max_chars:
90
+ return line[: max_chars - 3] + "..."
91
+ return line
92
+
93
+
94
+ def _resolve_run_id(payload, prompt_hash):
95
+ if isinstance(payload, dict):
96
+ run_id = payload.get("run_id")
97
+ if isinstance(run_id, str) and run_id.strip():
98
+ return run_id.strip()
99
+ tool_input = payload.get("tool_input")
100
+ if isinstance(tool_input, dict):
101
+ metadata = tool_input.get("metadata")
102
+ if isinstance(metadata, dict):
103
+ metadata_run_id = metadata.get("run_id")
104
+ if isinstance(metadata_run_id, str) and metadata_run_id.strip():
105
+ return metadata_run_id.strip()
106
+ env_run_id = os.environ.get("OMG_RUN_ID", "").strip()
107
+ if env_run_id:
108
+ return env_run_id
109
+ return prompt_hash[:24]
110
+
111
+
112
+ def _count_signals(signals, text):
113
+ return sum(1 for sig in signals if signal_matches_text(sig, text))
114
+
115
+
116
+ def _write_intent_gate_artifact(state_root, run_id, artifact):
117
+ state_path = os.path.join(state_root, "intent_gate", f"{run_id}.json")
118
+ os.makedirs(os.path.dirname(state_path), exist_ok=True)
119
+ tmp_path = f"{state_path}.tmp"
120
+ with open(tmp_path, "w", encoding="utf-8") as tmp_file:
121
+ json.dump(artifact, tmp_file, indent=2, ensure_ascii=True)
122
+ os.replace(tmp_path, state_path)
123
+
80
124
  # ── Zero-injection optimization ──
81
125
  # Simple prompts (≤10 words, no coding/mode/routing signals) get zero overhead
82
126
  _word_count_early = len(prompt_lower.split())
@@ -88,6 +132,8 @@ _has_any_signal = any([
88
132
  "codex","gemini","ccg","screenshot","screen",
89
133
  "security","warning","hook error","resume","handoff",
90
134
  "continue","domain","scaffold","debug","deploy",
135
+ "setup","credential","credentials","preference",
136
+ "preferences","remember","settings","memory",
91
137
  "수정","구현","버그","에러","고쳐","스크린샷","보안"]),
92
138
  _word_count_early > 10,
93
139
  ])
@@ -127,14 +173,109 @@ INTENT_MAP = {
127
173
  "구현", "빌드", "생성", "만들", "추가", "기능", "개발"],
128
174
  "directive": "IMPLEMENT — Plan → code → test → verify. Follow existing patterns"
129
175
  },
176
+ "auth_setup": {
177
+ "signals": ["auth", "login", "credential", "credentials", "oauth", "token", "setup auth",
178
+ "setup credential", "set up credential", "인증", "로그인", "자격 증명", "토큰"],
179
+ "directive": "AUTH_SETUP — Clarify auth target and credential source before changes"
180
+ },
181
+ "preference_memory": {
182
+ "signals": ["remember my settings", "remember settings", "preference", "preferences", "settings",
183
+ "memory style", "save my setup", "기억", "설정", "선호"],
184
+ "directive": "PREFERENCE_MEMORY — Clarify scope and persistence policy before changes"
185
+ },
186
+ "ambiguous_config": {
187
+ "signals": ["setup", "configure", "config", "remember", "save", "setup my", "set up",
188
+ "설정해", "구성", "저장"],
189
+ "directive": "AMBIGUOUS_CONFIG — Ask one-line clarification before any implementation"
190
+ },
130
191
  }
131
192
 
193
+ IMPLEMENTATION_ACTION_SIGNALS = [
194
+ "implement", "build", "create", "add", "fix", "refactor", "update", "write", "code",
195
+ "debug", "test", "migrate", "deploy", "patch", "change", "구현", "추가", "수정", "개발",
196
+ ]
197
+ AUTH_PROVIDER_SIGNALS = [
198
+ "codex", "claude", "gemini", "github", "google", "openai", "provider", "service", "cli",
199
+ ]
200
+ AUTH_SOURCE_SIGNALS = [
201
+ "env", "token", "key", "secret", "credential", "password", "vault", "profile", "yaml",
202
+ ]
203
+ PREFERENCE_SCOPE_SIGNALS = ["session", "project", "global", "default", "always", "workspace", "local"]
204
+
205
+
206
+ def _build_intent_gate_state(prompt_text, payload):
207
+ has_auth = _count_signals(INTENT_MAP["auth_setup"]["signals"], prompt_text) > 0
208
+ has_pref = _count_signals(INTENT_MAP["preference_memory"]["signals"], prompt_text) > 0
209
+ has_ambiguous_config = _count_signals(INTENT_MAP["ambiguous_config"]["signals"], prompt_text) > 0
210
+ if not (has_auth or has_pref or has_ambiguous_config):
211
+ return None
212
+
213
+ has_specific_action = any(signal_matches_text(sig, prompt_text) for sig in IMPLEMENTATION_ACTION_SIGNALS)
214
+
215
+ if has_auth and has_pref:
216
+ intent_class = "ambiguous_config"
217
+ elif has_auth and has_ambiguous_config and not has_specific_action:
218
+ intent_class = "ambiguous_config"
219
+ elif has_auth:
220
+ intent_class = "auth_setup"
221
+ elif has_pref:
222
+ intent_class = "preference_memory"
223
+ else:
224
+ intent_class = "ambiguous_config"
225
+
226
+ missing_slots = []
227
+ if intent_class in ("auth_setup", "ambiguous_config"):
228
+ if not any(signal_matches_text(sig, prompt_text) for sig in AUTH_PROVIDER_SIGNALS):
229
+ missing_slots.append("provider")
230
+ if not any(signal_matches_text(sig, prompt_text) for sig in AUTH_SOURCE_SIGNALS):
231
+ missing_slots.append("credential_source")
232
+ if intent_class in ("preference_memory", "ambiguous_config"):
233
+ if not any(signal_matches_text(sig, prompt_text) for sig in PREFERENCE_SCOPE_SIGNALS):
234
+ missing_slots.append("scope")
235
+ if not has_specific_action:
236
+ missing_slots.append("desired_action")
237
+
238
+ requires_clarification = intent_class == "ambiguous_config" or bool(missing_slots)
239
+ clarification_prompt = ""
240
+ if requires_clarification:
241
+ prompt_map = {
242
+ "auth_setup": "Clarify auth setup in one line: target service, credential source, and exact action to run.",
243
+ "preference_memory": "Clarify preference memory in one line: settings to save, scope, and when to apply them.",
244
+ "ambiguous_config": "Clarify in one line whether this is auth setup or preference memory, plus the exact action to execute.",
245
+ }
246
+ clarification_prompt = _single_line(prompt_map[intent_class])
247
+
248
+ source_prompt_hash = _hash_prompt(prompt_text)
249
+ run_id = _resolve_run_id(payload, source_prompt_hash)
250
+ signal_hits = _count_signals(INTENT_MAP[intent_class]["signals"], prompt_text)
251
+ confidence = min(0.99, 0.76 + (signal_hits * 0.07) + (0.06 if intent_class == "ambiguous_config" else 0.0))
252
+
253
+ return {
254
+ "schema": "IntentGateClarificationState",
255
+ "schema_version": "1.0.0",
256
+ "run_id": run_id,
257
+ "intent_class": intent_class,
258
+ "confidence": round(confidence, 2),
259
+ "requires_clarification": requires_clarification,
260
+ "missing_slots": missing_slots,
261
+ "clarification_prompt": clarification_prompt,
262
+ "source_prompt_hash": source_prompt_hash,
263
+ "updated_at": datetime.now(timezone.utc).isoformat(),
264
+ }
265
+
132
266
  detected_intent = None
133
267
  for intent_key, intent_data in INTENT_MAP.items():
134
268
  if any(signal_matches_text(sig, prompt) for sig in intent_data["signals"]):
135
269
  detected_intent = intent_key
136
270
  break
137
271
 
272
+ intent_gate_state = _build_intent_gate_state(prompt, data)
273
+ if intent_gate_state is not None:
274
+ try:
275
+ _write_intent_gate_artifact(state_dir, intent_gate_state["run_id"], intent_gate_state)
276
+ except Exception:
277
+ pass
278
+
138
279
  # ═══════════════════════════════════════════════════════════
139
280
  # 2. DISCIPLINE SYSTEM (Sisyphus-grade)
140
281
  # ═══════════════════════════════════════════════════════════
@@ -143,6 +284,11 @@ parts = []
143
284
  if detected_intent:
144
285
  parts.append(f"@intent: {INTENT_MAP[detected_intent]['directive']}")
145
286
 
287
+ if intent_gate_state and intent_gate_state.get("requires_clarification"):
288
+ clarification_line = _single_line(intent_gate_state.get("clarification_prompt", ""))
289
+ if clarification_line:
290
+ parts.append(f"@intent-clarify: {clarification_line}")
291
+
146
292
  parts.append(
147
293
  "@discipline: Senior-eng mode. Clean minimal code. "
148
294
  "VERIFY changes. NEVER claim done unverified. "
@@ -9,14 +9,21 @@ Features are implemented in later tasks:
9
9
  import sys
10
10
  import os
11
11
  import json
12
+ import glob
12
13
  from datetime import datetime
13
14
  from typing import Callable, cast
14
15
 
15
- sys.path.insert(0, os.path.dirname(__file__))
16
- from _common import setup_crash_handler as _setup_crash_handler
17
- from _common import json_input as _json_input
18
- from _common import get_feature_flag as _get_feature_flag
19
- from _common import log_hook_error as _log_hook_error
16
+ import yaml
17
+
18
+ _HOOKS_DIR = os.path.dirname(__file__)
19
+ _PROJECT_ROOT = os.path.dirname(_HOOKS_DIR)
20
+ if _PROJECT_ROOT not in sys.path:
21
+ sys.path.insert(0, _PROJECT_ROOT)
22
+
23
+ from hooks._common import setup_crash_handler as _setup_crash_handler
24
+ from hooks._common import json_input as _json_input
25
+ from hooks._common import get_feature_flag as _get_feature_flag
26
+ from hooks._common import log_hook_error as _log_hook_error
20
27
 
21
28
  setup_crash_handler = cast(Callable[[str, bool], None], _setup_crash_handler)
22
29
  json_input = cast(Callable[[], dict[str, str]], _json_input)
@@ -25,6 +32,362 @@ log_hook_error = cast(Callable[[str, str], None], _log_hook_error)
25
32
 
26
33
  setup_crash_handler('session-end-capture', False)
27
34
 
35
+ _PROFILE_ARCH_REQUEST_MAX = 8
36
+ _PROFILE_TAG_MAX = 12
37
+ _PROFILE_RECENT_UPDATES_MAX = 5
38
+ _MIN_SIGNAL_CONFIDENCE = 0.7
39
+
40
+
41
+ def _read_json_file(path: str) -> dict[str, object]:
42
+ if not os.path.exists(path):
43
+ return {}
44
+ try:
45
+ with open(path, "r", encoding="utf-8", errors="replace") as file_obj:
46
+ payload = json.load(file_obj)
47
+ except (OSError, json.JSONDecodeError, ValueError):
48
+ return {}
49
+ if isinstance(payload, dict):
50
+ return payload
51
+ return {}
52
+
53
+
54
+ def _read_recent_failure_entries(project_dir: str, limit: int = 20) -> list[dict[str, object]]:
55
+ ledger_dir = os.path.join(project_dir, ".omg", "state", "ledger")
56
+ if not os.path.exists(ledger_dir):
57
+ return []
58
+
59
+ entries: list[dict[str, object]] = []
60
+ try:
61
+ failure_files = sorted(glob.glob(os.path.join(ledger_dir, "failure-*.jsonl")))
62
+ except OSError:
63
+ return []
64
+
65
+ for file_path in failure_files[-5:]:
66
+ try:
67
+ with open(file_path, "r", encoding="utf-8", errors="replace") as file_obj:
68
+ for line in file_obj:
69
+ text = line.strip()
70
+ if not text:
71
+ continue
72
+ try:
73
+ payload = json.loads(text)
74
+ except (TypeError, ValueError, json.JSONDecodeError):
75
+ continue
76
+ if isinstance(payload, dict):
77
+ entries.append(cast(dict[str, object], payload))
78
+ except OSError:
79
+ continue
80
+ return entries[-limit:]
81
+
82
+
83
+ def _normalize_tag_token(value: str) -> str:
84
+ lowered = value.strip().lower().replace(" ", "_")
85
+ return "".join(ch for ch in lowered if ch.isalnum() or ch in ("_", "-"))
86
+
87
+
88
+ def _normalize_constraint_key(value: str) -> str:
89
+ lowered = value.strip().lower().replace(" ", "_")
90
+ return "".join(ch for ch in lowered if ch.isalnum() or ch == "_")
91
+
92
+
93
+ def _normalize_constraint_value(value: object) -> str | int | float | bool | None:
94
+ if isinstance(value, bool):
95
+ return value
96
+ if isinstance(value, int):
97
+ return value
98
+ if isinstance(value, float):
99
+ return value
100
+ if value is None:
101
+ return None
102
+ text = " ".join(str(value).strip().split()).lower()
103
+ return text if text else None
104
+
105
+
106
+ def _ensure_profile_baseline(profile: dict[str, object]) -> None:
107
+ preferences_obj = profile.get("preferences")
108
+ preferences = preferences_obj if isinstance(preferences_obj, dict) else {}
109
+ arch_obj = preferences.get("architecture_requests")
110
+ preferences["architecture_requests"] = arch_obj if isinstance(arch_obj, list) else []
111
+ constraints_obj = preferences.get("constraints")
112
+ preferences["constraints"] = constraints_obj if isinstance(constraints_obj, dict) else {}
113
+ profile["preferences"] = preferences
114
+
115
+ user_vector_obj = profile.get("user_vector")
116
+ user_vector = user_vector_obj if isinstance(user_vector_obj, dict) else {}
117
+ tags_obj = user_vector.get("tags")
118
+ user_vector["tags"] = tags_obj if isinstance(tags_obj, list) else []
119
+ profile["user_vector"] = user_vector
120
+
121
+ provenance_obj = profile.get("profile_provenance")
122
+ provenance = provenance_obj if isinstance(provenance_obj, dict) else {}
123
+ updates_obj = provenance.get("recent_updates")
124
+ provenance["recent_updates"] = updates_obj if isinstance(updates_obj, list) else []
125
+ profile["profile_provenance"] = provenance
126
+
127
+ try:
128
+ from runtime.profile_io import ensure_governed_preferences
129
+ ensure_governed_preferences(cast(dict[str, object], profile))
130
+ except Exception:
131
+ profile["governed_preferences"] = {"style": [], "safety": []}
132
+
133
+
134
+ def _record_provenance(
135
+ profile: dict[str, object],
136
+ *,
137
+ run_id: str,
138
+ source: str,
139
+ field: str,
140
+ updated_at: str,
141
+ ) -> None:
142
+ provenance = cast(dict[str, object], profile["profile_provenance"])
143
+ raw_updates = provenance.get("recent_updates", [])
144
+ updates = raw_updates if isinstance(raw_updates, list) else []
145
+ updates.append(
146
+ {
147
+ "run_id": run_id,
148
+ "source": source,
149
+ "field": field,
150
+ "updated_at": updated_at,
151
+ }
152
+ )
153
+ provenance["recent_updates"] = updates[-_PROFILE_RECENT_UPDATES_MAX:]
154
+
155
+
156
+ def _signal_is_contradicted(
157
+ signal: dict[str, object],
158
+ council_payload: dict[str, object],
159
+ failure_entries: list[dict[str, object]],
160
+ ) -> bool:
161
+ if bool(signal.get("contradicted") is True):
162
+ return True
163
+
164
+ field = str(signal.get("field", "")).strip()
165
+ verdicts_obj = council_payload.get("verdicts")
166
+ verdicts = verdicts_obj if isinstance(verdicts_obj, dict) else {}
167
+ for verdict in verdicts.values():
168
+ if not isinstance(verdict, dict):
169
+ continue
170
+ notes = " ".join(
171
+ str(verdict.get(part, ""))
172
+ for part in ("reason", "notes", "message", "detail")
173
+ ).lower()
174
+ if "contradict" in notes and (not field or field in notes):
175
+ return True
176
+
177
+ for entry in failure_entries:
178
+ if bool(entry.get("contradicted") is True):
179
+ entry_field = str(entry.get("field", "")).strip()
180
+ if not entry_field or entry_field == field:
181
+ return True
182
+ detail = " ".join(
183
+ str(entry.get(part, ""))
184
+ for part in ("error", "reason", "message", "detail")
185
+ ).lower()
186
+ if "contradict" in detail and (not field or field in detail):
187
+ return True
188
+ return False
189
+
190
+
191
+ def _promote_preference_learning(project_dir: str, run_id: str) -> None:
192
+ project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
193
+ if project_root not in sys.path:
194
+ sys.path.insert(0, project_root)
195
+ try:
196
+ from runtime.memory_store import project_preference_signals
197
+ except Exception:
198
+ return
199
+
200
+ intent_gate_path = os.path.join(project_dir, ".omg", "state", "intent_gate", f"{run_id}.json")
201
+ council_path = os.path.join(project_dir, ".omg", "state", "council_verdicts", f"{run_id}.json")
202
+ health_path = os.path.join(project_dir, ".omg", "state", "session_health", f"{run_id}.json")
203
+ profile_path = os.path.join(project_dir, ".omg", "state", "profile.yaml")
204
+
205
+ intent_gate = _read_json_file(intent_gate_path)
206
+ council_payload = _read_json_file(council_path)
207
+ session_health = _read_json_file(health_path)
208
+ failure_entries = _read_recent_failure_entries(project_dir)
209
+
210
+ candidate_signals = project_preference_signals(project_dir, max_signals=12)
211
+ if not candidate_signals:
212
+ return
213
+
214
+ if not os.path.exists(profile_path):
215
+ return
216
+
217
+ try:
218
+ from runtime.profile_io import load_profile
219
+ profile = cast(dict[str, object], load_profile(profile_path))
220
+ except Exception:
221
+ return
222
+ if not profile:
223
+ return
224
+ had_governed = isinstance(profile.get("governed_preferences"), dict)
225
+ _ensure_profile_baseline(profile)
226
+ baseline_changed = not had_governed
227
+
228
+ health_status = str(session_health.get("status", "")).strip().lower()
229
+ health_action = str(session_health.get("recommended_action", "")).strip().lower()
230
+ council_status = str(council_payload.get("status", "")).strip().lower()
231
+ requires_clarification = bool(intent_gate.get("requires_clarification") is True)
232
+ inferred_blocked = (
233
+ health_status == "blocked"
234
+ or health_action == "block"
235
+ or council_status in ("blocked", "fail")
236
+ or requires_clarification
237
+ )
238
+
239
+ inferred_counts: dict[tuple[str, str], int] = {}
240
+ explicit_to_apply: list[dict[str, object]] = []
241
+ inferred_to_apply: list[dict[str, object]] = []
242
+
243
+ for signal in candidate_signals:
244
+ confidence = signal.get("confidence", 0.0)
245
+ try:
246
+ signal_confidence = float(confidence)
247
+ except (TypeError, ValueError):
248
+ signal_confidence = 0.0
249
+ if signal_confidence < _MIN_SIGNAL_CONFIDENCE:
250
+ continue
251
+ if _signal_is_contradicted(signal, council_payload, failure_entries):
252
+ continue
253
+
254
+ source = str(signal.get("source", "")).strip().lower()
255
+ if source in ("explicit_user", "direct_user", "user_stated"):
256
+ explicit_to_apply.append(signal)
257
+ continue
258
+
259
+ field = str(signal.get("field", "")).strip()
260
+ value = " ".join(str(signal.get("value", "")).strip().split())
261
+ if not field or not value:
262
+ continue
263
+ key = (field, value)
264
+ inferred_counts[key] = inferred_counts.get(key, 0) + 1
265
+ inferred_to_apply.append(signal)
266
+
267
+ pending_writes: list[dict[str, object]] = list(explicit_to_apply)
268
+ if not inferred_blocked:
269
+ for signal in inferred_to_apply:
270
+ field = str(signal.get("field", "")).strip()
271
+ value = " ".join(str(signal.get("value", "")).strip().split())
272
+ if inferred_counts.get((field, value), 0) >= 2:
273
+ pending_writes.append(signal)
274
+
275
+ if not pending_writes:
276
+ if baseline_changed:
277
+ try:
278
+ from runtime.profile_io import save_profile
279
+ save_profile(profile_path, profile)
280
+ except Exception:
281
+ pass
282
+ return
283
+
284
+ updated_at = datetime.utcnow().isoformat() + "Z"
285
+ changed = False
286
+ for signal in pending_writes:
287
+ field = str(signal.get("field", "")).strip()
288
+ value = " ".join(str(signal.get("value", "")).strip().split())
289
+ if not field or not value:
290
+ continue
291
+
292
+ source = str(signal.get("source", "")).strip().lower() or "inferred_observation"
293
+ section = str(signal.get("section", "style")).strip().lower() or "style"
294
+ is_destructive = bool(signal.get("destructive") is True)
295
+
296
+ if section not in ("style", "safety"):
297
+ section = "style"
298
+
299
+ if is_destructive:
300
+ confirmation_state = "pending_confirmation"
301
+ elif source in ("explicit_user", "direct_user", "user_stated"):
302
+ confirmation_state = "confirmed"
303
+ else:
304
+ confirmation_state = "inferred"
305
+
306
+ decay_metadata: dict[str, object] | None = None
307
+ if section == "style" and confirmation_state == "inferred":
308
+ decay_metadata = {
309
+ "decay_score": 0.0,
310
+ "last_seen_at": updated_at,
311
+ "decay_reason": "inferred_signal",
312
+ }
313
+
314
+ try:
315
+ from runtime.profile_io import upsert_governed_preference
316
+ upsert_governed_preference(
317
+ cast(dict[str, object], profile),
318
+ field=field,
319
+ value=value,
320
+ source=source,
321
+ learned_at=updated_at,
322
+ updated_at=updated_at,
323
+ section=section,
324
+ confirmation_state=confirmation_state,
325
+ decay_metadata=decay_metadata,
326
+ )
327
+ changed = True
328
+ except Exception:
329
+ pass
330
+
331
+ if confirmation_state == "pending_confirmation":
332
+ continue
333
+
334
+ preferences = cast(dict[str, object], profile["preferences"])
335
+ user_vector = cast(dict[str, object], profile["user_vector"])
336
+
337
+ if field == "preferences.architecture_requests":
338
+ raw_arch = preferences.get("architecture_requests", [])
339
+ arch = raw_arch if isinstance(raw_arch, list) else []
340
+ if value in arch:
341
+ continue
342
+ arch.append(value)
343
+ preferences["architecture_requests"] = arch[-_PROFILE_ARCH_REQUEST_MAX:]
344
+ _record_provenance(profile, run_id=run_id, source=source, field=field, updated_at=updated_at)
345
+ changed = True
346
+ continue
347
+
348
+ if field.startswith("preferences.constraints."):
349
+ constraint_key = _normalize_constraint_key(field.split("preferences.constraints.", 1)[1])
350
+ constraint_value = _normalize_constraint_value(value)
351
+ if not constraint_key or constraint_value is None:
352
+ continue
353
+ raw_constraints = preferences.get("constraints", {})
354
+ constraints = raw_constraints if isinstance(raw_constraints, dict) else {}
355
+ if constraints.get(constraint_key) == constraint_value:
356
+ continue
357
+ constraints[constraint_key] = constraint_value
358
+ preferences["constraints"] = constraints
359
+ _record_provenance(profile, run_id=run_id, source=source, field=field, updated_at=updated_at)
360
+ changed = True
361
+ continue
362
+
363
+ if field == "user_vector.tags":
364
+ token = _normalize_tag_token(value)
365
+ if not token:
366
+ continue
367
+ raw_tags = user_vector.get("tags", [])
368
+ tags = raw_tags if isinstance(raw_tags, list) else []
369
+ if token in tags:
370
+ continue
371
+ tags.append(token)
372
+ user_vector["tags"] = tags[-_PROFILE_TAG_MAX:]
373
+ _record_provenance(profile, run_id=run_id, source=source, field=field, updated_at=updated_at)
374
+ changed = True
375
+
376
+ if not changed:
377
+ if baseline_changed:
378
+ try:
379
+ from runtime.profile_io import save_profile
380
+ save_profile(profile_path, profile)
381
+ except Exception:
382
+ pass
383
+ return
384
+
385
+ try:
386
+ from runtime.profile_io import save_profile
387
+ save_profile(profile_path, profile)
388
+ except Exception:
389
+ return
390
+
28
391
  data = json_input()
29
392
  session_id = data.get('session_id', 'unknown')
30
393
  cwd = data.get('cwd', os.getcwd())
@@ -32,7 +395,7 @@ cwd = data.get('cwd', os.getcwd())
32
395
  # Capture A: Memory (implemented in Task 19)
33
396
  if get_feature_flag('memory'):
34
397
  try:
35
- from _memory import save_memory, rotate_memories
398
+ from hooks._memory import save_memory, rotate_memories
36
399
 
37
400
  summary_parts = [f"# Session: {datetime.now().strftime('%Y-%m-%d')} ({session_id[:8]})"]
38
401
 
@@ -134,4 +497,9 @@ if get_feature_flag('compound_learning'):
134
497
  except Exception as e:
135
498
  log_hook_error('session-end-capture', str(e))
136
499
 
500
+ try:
501
+ _promote_preference_learning(cwd, session_id)
502
+ except Exception as e:
503
+ log_hook_error('session-end-capture', str(e))
504
+
137
505
  sys.exit(0)
@@ -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}')