@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.
- package/.agents/skills/omg/AGENTS.fragment.md +2 -2
- package/.agents/skills/omg/algorithms/SKILL.md +1 -1
- package/.agents/skills/omg/algorithms/openai.yaml +1 -1
- package/.agents/skills/omg/api-twin/SKILL.md +1 -1
- package/.agents/skills/omg/api-twin/openai.yaml +1 -1
- package/.agents/skills/omg/claim-judge/SKILL.md +1 -1
- package/.agents/skills/omg/claim-judge/openai.yaml +1 -1
- package/.agents/skills/omg/codex-rules.md +1 -1
- package/.agents/skills/omg/control-plane/SKILL.md +1 -1
- package/.agents/skills/omg/control-plane/openai.yaml +1 -1
- package/.agents/skills/omg/data-lineage/SKILL.md +1 -1
- package/.agents/skills/omg/data-lineage/openai.yaml +1 -1
- package/.agents/skills/omg/delta-classifier/SKILL.md +1 -1
- package/.agents/skills/omg/delta-classifier/openai.yaml +1 -1
- package/.agents/skills/omg/eval-gate/SKILL.md +1 -1
- package/.agents/skills/omg/eval-gate/openai.yaml +1 -1
- package/.agents/skills/omg/health/SKILL.md +1 -1
- package/.agents/skills/omg/health/openai.yaml +1 -1
- package/.agents/skills/omg/hook-governor/SKILL.md +1 -1
- package/.agents/skills/omg/hook-governor/openai.yaml +1 -1
- package/.agents/skills/omg/incident-replay/SKILL.md +1 -1
- package/.agents/skills/omg/incident-replay/openai.yaml +1 -1
- package/.agents/skills/omg/lsp-pack/SKILL.md +1 -1
- package/.agents/skills/omg/lsp-pack/openai.yaml +1 -1
- package/.agents/skills/omg/mcp-fabric/SKILL.md +1 -1
- package/.agents/skills/omg/mcp-fabric/openai.yaml +1 -1
- package/.agents/skills/omg/plan-council/SKILL.md +1 -1
- package/.agents/skills/omg/plan-council/openai.yaml +1 -1
- package/.agents/skills/omg/preflight/SKILL.md +1 -1
- package/.agents/skills/omg/preflight/openai.yaml +1 -1
- package/.agents/skills/omg/proof-gate/SKILL.md +1 -1
- package/.agents/skills/omg/proof-gate/openai.yaml +1 -1
- package/.agents/skills/omg/remote-supervisor/SKILL.md +1 -1
- package/.agents/skills/omg/remote-supervisor/openai.yaml +1 -1
- package/.agents/skills/omg/robotics/SKILL.md +1 -1
- package/.agents/skills/omg/robotics/openai.yaml +1 -1
- package/.agents/skills/omg/secure-worktree-pipeline/SKILL.md +1 -1
- package/.agents/skills/omg/secure-worktree-pipeline/openai.yaml +1 -1
- package/.agents/skills/omg/security-check/SKILL.md +1 -1
- package/.agents/skills/omg/security-check/openai.yaml +1 -1
- package/.agents/skills/omg/test-intent-lock/SKILL.md +1 -1
- package/.agents/skills/omg/test-intent-lock/openai.yaml +1 -1
- package/.agents/skills/omg/tracebank/SKILL.md +1 -1
- package/.agents/skills/omg/tracebank/openai.yaml +1 -1
- package/.agents/skills/omg/vision/SKILL.md +1 -1
- package/.agents/skills/omg/vision/openai.yaml +1 -1
- package/.claude-plugin/marketplace.json +3 -3
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +16 -0
- package/CLI-ADAPTER-MAP.md +3 -3
- package/OMG-setup.sh +114 -1
- package/OMG_COMPAT_CONTRACT.md +1 -1
- package/README.md +2 -1
- package/artifacts/release/.claude-plugin/marketplace.json +3 -3
- package/artifacts/release/.claude-plugin/plugin.json +1 -1
- package/artifacts/release/OMG_COMPAT_CONTRACT.md +1 -1
- package/artifacts/release/dist/enterprise/bundle/.claude-plugin/marketplace.json +3 -3
- package/artifacts/release/dist/enterprise/bundle/.claude-plugin/plugin.json +1 -1
- package/artifacts/release/dist/enterprise/bundle/OMG_COMPAT_CONTRACT.md +1 -1
- package/artifacts/release/dist/enterprise/bundle/plugins/advanced/plugin.json +26 -9
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/algorithms.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/api-twin.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/claim-judge.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/control-plane.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/data-lineage.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/delta-classifier.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/eval-gate.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/health.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/hook-governor.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/incident-replay.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/lsp-pack.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/mcp-fabric.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/plan-council.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/preflight.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/proof-gate.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/remote-supervisor.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/robotics.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/secure-worktree-pipeline.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/security-check.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/test-intent-lock.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/tracebank.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/bundles/vision.yaml +1 -1
- package/artifacts/release/dist/enterprise/bundle/registry/omg-capability.schema.json +1 -1
- package/artifacts/release/dist/enterprise/bundle/settings.json +3 -3
- package/artifacts/release/dist/enterprise/manifest.json +30 -40
- package/artifacts/release/dist/public/bundle/.claude-plugin/marketplace.json +3 -3
- package/artifacts/release/dist/public/bundle/.claude-plugin/plugin.json +1 -1
- package/artifacts/release/dist/public/bundle/OMG_COMPAT_CONTRACT.md +1 -1
- package/artifacts/release/dist/public/bundle/plugins/advanced/plugin.json +26 -9
- package/artifacts/release/dist/public/bundle/registry/bundles/algorithms.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/api-twin.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/claim-judge.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/control-plane.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/data-lineage.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/delta-classifier.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/eval-gate.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/health.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/hook-governor.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/incident-replay.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/lsp-pack.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/mcp-fabric.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/plan-council.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/preflight.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/proof-gate.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/remote-supervisor.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/robotics.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/secure-worktree-pipeline.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/security-check.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/test-intent-lock.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/tracebank.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/bundles/vision.yaml +1 -1
- package/artifacts/release/dist/public/bundle/registry/omg-capability.schema.json +1 -1
- package/artifacts/release/dist/public/bundle/settings.json +3 -3
- package/artifacts/release/dist/public/manifest.json +30 -40
- package/artifacts/release/plugins/advanced/plugin.json +26 -9
- package/artifacts/release/registry/bundles/algorithms.yaml +1 -1
- package/artifacts/release/registry/bundles/api-twin.yaml +1 -1
- package/artifacts/release/registry/bundles/claim-judge.yaml +1 -1
- package/artifacts/release/registry/bundles/control-plane.yaml +1 -1
- package/artifacts/release/registry/bundles/data-lineage.yaml +1 -1
- package/artifacts/release/registry/bundles/delta-classifier.yaml +1 -1
- package/artifacts/release/registry/bundles/eval-gate.yaml +1 -1
- package/artifacts/release/registry/bundles/health.yaml +1 -1
- package/artifacts/release/registry/bundles/hook-governor.yaml +1 -1
- package/artifacts/release/registry/bundles/incident-replay.yaml +1 -1
- package/artifacts/release/registry/bundles/lsp-pack.yaml +1 -1
- package/artifacts/release/registry/bundles/mcp-fabric.yaml +1 -1
- package/artifacts/release/registry/bundles/plan-council.yaml +1 -1
- package/artifacts/release/registry/bundles/preflight.yaml +1 -1
- package/artifacts/release/registry/bundles/proof-gate.yaml +1 -1
- package/artifacts/release/registry/bundles/remote-supervisor.yaml +1 -1
- package/artifacts/release/registry/bundles/robotics.yaml +1 -1
- package/artifacts/release/registry/bundles/secure-worktree-pipeline.yaml +1 -1
- package/artifacts/release/registry/bundles/security-check.yaml +1 -1
- package/artifacts/release/registry/bundles/test-intent-lock.yaml +1 -1
- package/artifacts/release/registry/bundles/tracebank.yaml +1 -1
- package/artifacts/release/registry/bundles/vision.yaml +1 -1
- package/artifacts/release/registry/omg-capability.schema.json +1 -1
- package/artifacts/release/settings.json +3 -3
- package/build/lib/commands/OMG:profile-review.md +43 -0
- package/build/lib/commands/OMG:validate.md +59 -0
- package/build/lib/control_plane/service.py +40 -1
- package/build/lib/hooks/_agent_registry.py +2 -0
- package/build/lib/hooks/firewall.py +263 -4
- package/build/lib/hooks/intentgate-keyword-detector.py +15 -3
- package/build/lib/hooks/policy_engine.py +77 -0
- package/build/lib/hooks/prompt-enhancer.py +146 -0
- package/build/lib/hooks/session-end-capture.py +374 -6
- package/build/lib/hooks/session-start.py +13 -35
- package/build/lib/hooks/setup_wizard.py +333 -1
- package/build/lib/hooks/shadow_manager.py +111 -2
- package/build/lib/hooks/stop_dispatcher.py +108 -4
- package/build/lib/plugins/advanced/plugin.json +26 -9
- package/build/lib/plugins/core/plugin.json +44 -11
- package/build/lib/registry/bundles/algorithms.yaml +1 -1
- package/build/lib/registry/bundles/api-twin.yaml +1 -1
- package/build/lib/registry/bundles/claim-judge.yaml +1 -1
- package/build/lib/registry/bundles/control-plane.yaml +1 -1
- package/build/lib/registry/bundles/data-lineage.yaml +1 -1
- package/build/lib/registry/bundles/delta-classifier.yaml +1 -1
- package/build/lib/registry/bundles/eval-gate.yaml +1 -1
- package/build/lib/registry/bundles/health.yaml +1 -1
- package/build/lib/registry/bundles/hook-governor.yaml +1 -1
- package/build/lib/registry/bundles/incident-replay.yaml +1 -1
- package/build/lib/registry/bundles/lsp-pack.yaml +1 -1
- package/build/lib/registry/bundles/mcp-fabric.yaml +1 -1
- package/build/lib/registry/bundles/plan-council.yaml +1 -1
- package/build/lib/registry/bundles/preflight.yaml +1 -1
- package/build/lib/registry/bundles/proof-gate.yaml +1 -1
- package/build/lib/registry/bundles/remote-supervisor.yaml +1 -1
- package/build/lib/registry/bundles/robotics.yaml +1 -1
- package/build/lib/registry/bundles/secure-worktree-pipeline.yaml +1 -1
- package/build/lib/registry/bundles/security-check.yaml +1 -1
- package/build/lib/registry/bundles/test-intent-lock.yaml +1 -1
- package/build/lib/registry/bundles/tracebank.yaml +1 -1
- package/build/lib/registry/bundles/vision.yaml +1 -1
- package/build/lib/registry/omg-capability.schema.json +1 -1
- package/build/lib/runtime/adoption.py +1 -1
- package/build/lib/runtime/background_verification.py +62 -0
- package/build/lib/runtime/claim_judge.py +165 -5
- package/build/lib/runtime/compat.py +15 -1
- package/build/lib/runtime/context_engine.py +261 -4
- package/build/lib/runtime/contract_compiler.py +370 -93
- package/build/lib/runtime/defense_state.py +79 -8
- package/build/lib/runtime/delta_classifier.py +80 -2
- package/build/lib/runtime/evidence_query.py +56 -4
- package/build/lib/runtime/evidence_registry.py +16 -0
- package/build/lib/runtime/evidence_requirements.py +47 -0
- package/build/lib/runtime/forge_agents.py +179 -9
- package/build/lib/runtime/forge_contracts.py +208 -2
- package/build/lib/runtime/interaction_journal.py +318 -42
- package/build/lib/runtime/memory_store.py +134 -1
- package/build/lib/runtime/mutation_gate.py +57 -11
- package/build/lib/runtime/omg_compat_contract_snapshot.json +2 -2
- package/build/lib/runtime/omg_mcp_server.py +14 -4
- package/build/lib/runtime/preflight.py +14 -0
- package/build/lib/runtime/profile_io.py +294 -0
- package/build/lib/runtime/proof_gate.py +122 -7
- package/build/lib/runtime/release_run_coordinator.py +339 -0
- package/build/lib/runtime/release_surfaces.py +243 -0
- package/build/lib/runtime/rollback_manifest.py +136 -0
- package/build/lib/runtime/router_critics.py +50 -0
- package/build/lib/runtime/runtime_contracts.py +137 -0
- package/build/lib/runtime/security_check.py +16 -0
- package/build/lib/runtime/session_health.py +60 -16
- package/build/lib/runtime/team_router.py +156 -13
- package/build/lib/runtime/test_intent_lock.py +102 -0
- package/build/lib/runtime/tool_plan_gate.py +249 -24
- package/build/lib/runtime/validate.py +194 -0
- package/build/lib/runtime/verification_controller.py +52 -2
- package/commands/OMG:diagnose-plugins.md +33 -0
- package/commands/OMG:profile-review.md +43 -0
- package/commands/OMG:setup.md +4 -1
- package/commands/OMG:validate.md +59 -0
- package/control_plane/service.py +40 -1
- package/dist/enterprise/bundle/.claude-plugin/marketplace.json +3 -3
- package/dist/enterprise/bundle/.claude-plugin/plugin.json +1 -1
- package/dist/enterprise/bundle/OMG_COMPAT_CONTRACT.md +1 -1
- package/dist/enterprise/bundle/plugins/advanced/plugin.json +26 -9
- package/dist/enterprise/bundle/registry/bundles/algorithms.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/api-twin.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/claim-judge.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/control-plane.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/data-lineage.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/delta-classifier.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/eval-gate.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/health.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/hook-governor.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/incident-replay.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/lsp-pack.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/mcp-fabric.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/plan-council.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/preflight.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/proof-gate.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/remote-supervisor.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/robotics.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/secure-worktree-pipeline.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/security-check.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/test-intent-lock.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/tracebank.yaml +1 -1
- package/dist/enterprise/bundle/registry/bundles/vision.yaml +1 -1
- package/dist/enterprise/bundle/registry/omg-capability.schema.json +1 -1
- package/dist/enterprise/bundle/settings.json +3 -3
- package/dist/enterprise/manifest.json +30 -40
- package/dist/oh_my_god-2.1.4-py3-none-any.whl +0 -0
- package/dist/oh_my_god-2.1.4.tar.gz +0 -0
- package/dist/public/bundle/.claude-plugin/marketplace.json +3 -3
- package/dist/public/bundle/.claude-plugin/plugin.json +1 -1
- package/dist/public/bundle/OMG_COMPAT_CONTRACT.md +1 -1
- package/dist/public/bundle/plugins/advanced/plugin.json +26 -9
- package/dist/public/bundle/registry/bundles/algorithms.yaml +1 -1
- package/dist/public/bundle/registry/bundles/api-twin.yaml +1 -1
- package/dist/public/bundle/registry/bundles/claim-judge.yaml +1 -1
- package/dist/public/bundle/registry/bundles/control-plane.yaml +1 -1
- package/dist/public/bundle/registry/bundles/data-lineage.yaml +1 -1
- package/dist/public/bundle/registry/bundles/delta-classifier.yaml +1 -1
- package/dist/public/bundle/registry/bundles/eval-gate.yaml +1 -1
- package/dist/public/bundle/registry/bundles/health.yaml +1 -1
- package/dist/public/bundle/registry/bundles/hook-governor.yaml +1 -1
- package/dist/public/bundle/registry/bundles/incident-replay.yaml +1 -1
- package/dist/public/bundle/registry/bundles/lsp-pack.yaml +1 -1
- package/dist/public/bundle/registry/bundles/mcp-fabric.yaml +1 -1
- package/dist/public/bundle/registry/bundles/plan-council.yaml +1 -1
- package/dist/public/bundle/registry/bundles/preflight.yaml +1 -1
- package/dist/public/bundle/registry/bundles/proof-gate.yaml +1 -1
- package/dist/public/bundle/registry/bundles/remote-supervisor.yaml +1 -1
- package/dist/public/bundle/registry/bundles/robotics.yaml +1 -1
- package/dist/public/bundle/registry/bundles/secure-worktree-pipeline.yaml +1 -1
- package/dist/public/bundle/registry/bundles/security-check.yaml +1 -1
- package/dist/public/bundle/registry/bundles/test-intent-lock.yaml +1 -1
- package/dist/public/bundle/registry/bundles/tracebank.yaml +1 -1
- package/dist/public/bundle/registry/bundles/vision.yaml +1 -1
- package/dist/public/bundle/registry/omg-capability.schema.json +1 -1
- package/dist/public/bundle/settings.json +3 -3
- package/dist/public/manifest.json +30 -40
- package/docs/install/opencode.md +30 -0
- package/docs/proof.md +11 -0
- package/docs/release-checklist.md +14 -1
- package/hooks/_agent_registry.py +2 -0
- package/hooks/firewall.py +263 -4
- package/hooks/intentgate-keyword-detector.py +15 -3
- package/hooks/policy_engine.py +77 -0
- package/hooks/prompt-enhancer.py +146 -0
- package/hooks/session-end-capture.py +374 -6
- package/hooks/session-start.py +13 -35
- package/hooks/setup_wizard.py +342 -2
- package/hooks/shadow_manager.py +111 -2
- package/hooks/stop_dispatcher.py +108 -4
- package/hud/omg-hud.mjs +30 -6
- package/lab/pipeline.py +197 -26
- package/package.json +1 -1
- package/plugins/advanced/plugin.json +26 -9
- package/plugins/core/plugin.json +51 -12
- package/pyproject.toml +10 -2
- package/registry/bundles/algorithms.yaml +1 -1
- package/registry/bundles/api-twin.yaml +1 -1
- package/registry/bundles/claim-judge.yaml +1 -1
- package/registry/bundles/control-plane.yaml +1 -1
- package/registry/bundles/data-lineage.yaml +1 -1
- package/registry/bundles/delta-classifier.yaml +1 -1
- package/registry/bundles/eval-gate.yaml +1 -1
- package/registry/bundles/health.yaml +1 -1
- package/registry/bundles/hook-governor.yaml +1 -1
- package/registry/bundles/incident-replay.yaml +1 -1
- package/registry/bundles/lsp-pack.yaml +1 -1
- package/registry/bundles/mcp-fabric.yaml +1 -1
- package/registry/bundles/plan-council.yaml +1 -1
- package/registry/bundles/preflight.yaml +1 -1
- package/registry/bundles/proof-gate.yaml +1 -1
- package/registry/bundles/remote-supervisor.yaml +1 -1
- package/registry/bundles/robotics.yaml +1 -1
- package/registry/bundles/secure-worktree-pipeline.yaml +1 -1
- package/registry/bundles/security-check.yaml +1 -1
- package/registry/bundles/test-intent-lock.yaml +1 -1
- package/registry/bundles/tracebank.yaml +1 -1
- package/registry/bundles/vision.yaml +1 -1
- package/registry/omg-capability.schema.json +1 -1
- package/runtime/adoption.py +7 -5
- package/runtime/background_verification.py +62 -0
- package/runtime/claim_judge.py +165 -5
- package/runtime/compat.py +48 -1
- package/runtime/context_engine.py +261 -4
- package/runtime/contract_compiler.py +370 -93
- package/runtime/defense_state.py +79 -8
- package/runtime/delta_classifier.py +80 -2
- package/runtime/evidence_query.py +56 -4
- package/runtime/evidence_registry.py +16 -0
- package/runtime/evidence_requirements.py +47 -0
- package/runtime/forge_agents.py +179 -9
- package/runtime/forge_contracts.py +208 -2
- package/runtime/hook_governor.py +19 -1
- package/runtime/interaction_journal.py +318 -42
- package/runtime/memory_store.py +134 -1
- package/runtime/mutation_gate.py +57 -11
- package/runtime/omg_compat_contract_snapshot.json +2 -2
- package/runtime/omg_mcp_server.py +14 -4
- package/runtime/plugin_diagnostics.py +248 -0
- package/runtime/plugin_interop.py +1007 -0
- package/runtime/preflight.py +14 -0
- package/runtime/profile_io.py +294 -0
- package/runtime/proof_gate.py +122 -7
- package/runtime/providers/opencode_provider.py +94 -0
- package/runtime/release_run_coordinator.py +339 -0
- package/runtime/release_surfaces.py +243 -0
- package/runtime/rollback_manifest.py +136 -0
- package/runtime/router_critics.py +50 -0
- package/runtime/runtime_contracts.py +137 -0
- package/runtime/security_check.py +16 -0
- package/runtime/session_health.py +60 -16
- package/runtime/team_router.py +156 -13
- package/runtime/test_intent_lock.py +102 -0
- package/runtime/tool_plan_gate.py +218 -19
- package/runtime/validate.py +289 -0
- package/runtime/verification_controller.py +52 -2
- package/scripts/omg.py +306 -3
- package/scripts/prepare-release-proof-fixtures.py +191 -7
- package/scripts/print-canonical-version.py +80 -0
- package/scripts/sync-release-identity.py +476 -0
- package/scripts/validate-release-identity.py +246 -0
- package/scripts/verify-standalone.sh +3 -0
- package/settings.json +3 -3
- package/templates/plugins-allowlist.yaml +19 -0
- package/artifacts/release/dist/enterprise/bundle/.gemini/settings.json +0 -11
- package/artifacts/release/dist/enterprise/bundle/.kimi/mcp.json +0 -11
- package/artifacts/release/dist/public/bundle/.gemini/settings.json +0 -11
- package/artifacts/release/dist/public/bundle/.kimi/mcp.json +0 -11
- package/build/lib/yaml.py +0 -321
- package/dist/enterprise/bundle/.gemini/settings.json +0 -11
- package/dist/enterprise/bundle/.kimi/mcp.json +0 -11
- package/dist/public/bundle/.gemini/settings.json +0 -11
- package/dist/public/bundle/.kimi/mcp.json +0 -11
|
@@ -2,13 +2,25 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
4
|
import os
|
|
5
|
+
import re
|
|
6
|
+
import warnings
|
|
5
7
|
from datetime import datetime, timezone
|
|
6
8
|
from hashlib import sha256
|
|
7
9
|
from pathlib import Path
|
|
8
10
|
|
|
11
|
+
from runtime.release_run_coordinator import resolve_current_run_id
|
|
12
|
+
from runtime.test_intent_lock import verify_lock
|
|
13
|
+
|
|
9
14
|
|
|
10
15
|
_MUTATING_TOOLS = frozenset({"Write", "Edit", "MultiEdit"})
|
|
11
16
|
_EXEMPTIONS = frozenset({"docs", "config", "generated", "test"})
|
|
17
|
+
_MUTATION_BASH_PATTERNS = (
|
|
18
|
+
r"\b(git\s+(add|commit|push|rebase|cherry-pick|merge|tag))\b",
|
|
19
|
+
r"\b(rm|mv|cp|tee|touch|mkdir|rmdir|ln)\b",
|
|
20
|
+
r"\b(sed\s+-i|perl\s+-pi)\b",
|
|
21
|
+
r"\b(chmod|chown)\b",
|
|
22
|
+
r">\s*[^\s]",
|
|
23
|
+
)
|
|
12
24
|
|
|
13
25
|
|
|
14
26
|
def check_mutation_allowed(
|
|
@@ -18,11 +30,25 @@ def check_mutation_allowed(
|
|
|
18
30
|
lock_id: str | None,
|
|
19
31
|
*,
|
|
20
32
|
exemption: str | None = None,
|
|
33
|
+
command: str | None = None,
|
|
34
|
+
run_id: str | None = None,
|
|
35
|
+
metadata: dict[str, object] | None = None,
|
|
21
36
|
) -> dict[str, str | None]:
|
|
22
37
|
normalized_tool = str(tool or "").strip()
|
|
23
38
|
normalized_file_path = str(file_path or "").strip()
|
|
24
39
|
normalized_lock_id = str(lock_id or "").strip() or None
|
|
25
40
|
normalized_exemption = str(exemption or "").strip().lower() or None
|
|
41
|
+
normalized_command = str(command or "").strip()
|
|
42
|
+
metadata_obj = metadata if isinstance(metadata, dict) else {}
|
|
43
|
+
explicit_exempt = bool(metadata_obj.get("exempt") is True)
|
|
44
|
+
strict_mode = os.environ.get("OMG_TDD_GATE_STRICT", "0").strip() == "1"
|
|
45
|
+
|
|
46
|
+
if explicit_exempt:
|
|
47
|
+
return {
|
|
48
|
+
"status": "exempt",
|
|
49
|
+
"reason": "metadata exemption allows mutation without lock",
|
|
50
|
+
"lock_id": normalized_lock_id,
|
|
51
|
+
}
|
|
26
52
|
|
|
27
53
|
if normalized_exemption in _EXEMPTIONS:
|
|
28
54
|
return {
|
|
@@ -31,36 +57,56 @@ def check_mutation_allowed(
|
|
|
31
57
|
"lock_id": normalized_lock_id,
|
|
32
58
|
}
|
|
33
59
|
|
|
34
|
-
|
|
60
|
+
requires_lock = normalized_tool in _MUTATING_TOOLS
|
|
61
|
+
if normalized_tool == "Bash":
|
|
62
|
+
requires_lock = _is_mutation_capable_bash(normalized_command)
|
|
63
|
+
|
|
64
|
+
if not requires_lock:
|
|
35
65
|
return {
|
|
36
66
|
"status": "allowed",
|
|
37
67
|
"reason": "tool is read-only for mutation gate",
|
|
38
68
|
"lock_id": normalized_lock_id,
|
|
39
69
|
}
|
|
40
70
|
|
|
41
|
-
|
|
71
|
+
resolved_run_id = str(run_id or "").strip() or resolve_current_run_id(project_dir=project_dir)
|
|
72
|
+
verification = verify_lock(project_dir, run_id=resolved_run_id, lock_id=normalized_lock_id)
|
|
73
|
+
if verification.get("status") == "ok":
|
|
42
74
|
return {
|
|
43
75
|
"status": "allowed",
|
|
44
76
|
"reason": "active test intent lock found",
|
|
77
|
+
"lock_id": str(verification.get("lock_id") or normalized_lock_id or "") or None,
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
reason = str(verification.get("reason", "no_active_test_intent_lock"))
|
|
81
|
+
if strict_mode:
|
|
82
|
+
_write_block_artifact(project_dir, normalized_tool, normalized_file_path, reason)
|
|
83
|
+
return {
|
|
84
|
+
"status": "blocked",
|
|
85
|
+
"reason": reason,
|
|
45
86
|
"lock_id": normalized_lock_id,
|
|
46
87
|
}
|
|
47
88
|
|
|
48
|
-
|
|
49
|
-
"
|
|
50
|
-
|
|
51
|
-
|
|
89
|
+
warnings.warn(
|
|
90
|
+
f"mutation_gate_permissive_allow:{normalized_tool}:{reason}",
|
|
91
|
+
RuntimeWarning,
|
|
92
|
+
stacklevel=2,
|
|
52
93
|
)
|
|
53
|
-
_write_block_artifact(project_dir, normalized_tool, normalized_file_path, reason)
|
|
54
94
|
return {
|
|
55
|
-
"status": "
|
|
95
|
+
"status": "allowed",
|
|
56
96
|
"reason": reason,
|
|
57
97
|
"lock_id": normalized_lock_id,
|
|
58
98
|
}
|
|
59
99
|
|
|
60
100
|
|
|
61
|
-
def
|
|
62
|
-
|
|
63
|
-
|
|
101
|
+
def _is_mutation_capable_bash(command: str) -> bool:
|
|
102
|
+
normalized_command = str(command or "").strip()
|
|
103
|
+
if not normalized_command:
|
|
104
|
+
return False
|
|
105
|
+
lowered = normalized_command.lower()
|
|
106
|
+
for pattern in _MUTATION_BASH_PATTERNS:
|
|
107
|
+
if re.search(pattern, lowered):
|
|
108
|
+
return True
|
|
109
|
+
return False
|
|
64
110
|
|
|
65
111
|
|
|
66
112
|
def _write_block_artifact(project_dir: str, tool: str, file_path: str, reason: str) -> None:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema": "OmgCompatContractSnapshot",
|
|
3
|
-
"contract_version": "2.1.
|
|
3
|
+
"contract_version": "2.1.4",
|
|
4
4
|
"count": 47,
|
|
5
5
|
"contracts": [
|
|
6
6
|
{
|
|
@@ -912,5 +912,5 @@
|
|
|
912
912
|
"notes": "Writes long-form memory artifact for writing workflows."
|
|
913
913
|
}
|
|
914
914
|
],
|
|
915
|
-
"generated_at": "2026-03-
|
|
915
|
+
"generated_at": "2026-03-09T21:20:44.496932+00:00"
|
|
916
916
|
}
|
|
@@ -125,7 +125,7 @@ def _read_repo_text(rel_path: str) -> str:
|
|
|
125
125
|
|
|
126
126
|
@mcp.tool()
|
|
127
127
|
def omg_policy_evaluate(tool: str, input: dict[str, Any]) -> dict[str, Any]:
|
|
128
|
-
if tool in {"Write", "Edit", "MultiEdit"}:
|
|
128
|
+
if tool in {"Write", "Edit", "MultiEdit", "Bash"}:
|
|
129
129
|
run_id_candidate = input.get("run_id") if isinstance(input, dict) else None
|
|
130
130
|
run_id: str | None = None
|
|
131
131
|
if isinstance(run_id_candidate, str) and run_id_candidate.strip():
|
|
@@ -138,9 +138,10 @@ def omg_policy_evaluate(tool: str, input: dict[str, Any]) -> dict[str, Any]:
|
|
|
138
138
|
if not run_id:
|
|
139
139
|
run_id = resolve_current_run_id()
|
|
140
140
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
141
|
+
if tool in {"Write", "Edit", "MultiEdit"}:
|
|
142
|
+
tool_plan_result = tool_plan_gate_check(_service().project_dir, run_id, tool)
|
|
143
|
+
if tool_plan_result.get("status") == "blocked":
|
|
144
|
+
return tool_plan_result
|
|
144
145
|
|
|
145
146
|
lock_id: str | None = None
|
|
146
147
|
if isinstance(metadata, dict):
|
|
@@ -158,6 +159,9 @@ def omg_policy_evaluate(tool: str, input: dict[str, Any]) -> dict[str, Any]:
|
|
|
158
159
|
"file_path": str(input.get("file_path", "")) if isinstance(input, dict) else "",
|
|
159
160
|
"lock_id": lock_id,
|
|
160
161
|
"exemption": exemption,
|
|
162
|
+
"command": str(input.get("command", "")) if isinstance(input, dict) else "",
|
|
163
|
+
"run_id": run_id,
|
|
164
|
+
"metadata": metadata if isinstance(metadata, dict) else None,
|
|
161
165
|
}
|
|
162
166
|
)
|
|
163
167
|
if gate_status == 200 and gate_payload.get("status") == "blocked":
|
|
@@ -238,6 +242,12 @@ def omg_guide_assert(candidate: str, rules: dict[str, Any]) -> dict[str, Any]:
|
|
|
238
242
|
return payload
|
|
239
243
|
|
|
240
244
|
|
|
245
|
+
@mcp.tool()
|
|
246
|
+
def omg_get_session_health(run_id: str | None = None) -> dict[str, Any]:
|
|
247
|
+
_status, payload = _service().session_health({"run_id": run_id})
|
|
248
|
+
return payload
|
|
249
|
+
|
|
250
|
+
|
|
241
251
|
@mcp.prompt(name="omg_contract_summary", description="Summarize the OMG production contract and generated host outputs")
|
|
242
252
|
def omg_contract_summary(channel: str = "public") -> str:
|
|
243
253
|
return (
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Structured preflight routing for OMG."""
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
|
+
import importlib
|
|
4
5
|
from runtime.delta_classifier import classify_project_changes
|
|
5
6
|
from runtime.tracebank import record_trace
|
|
6
7
|
from typing import Any
|
|
@@ -45,6 +46,7 @@ def run_preflight(project_dir: str, *, goal: str) -> dict[str, Any]:
|
|
|
45
46
|
categories = set(delta["categories"])
|
|
46
47
|
requires_security_check = _requires_security_check(delta)
|
|
47
48
|
domain_packs = [category for category in delta["categories"] if category in {"robotics", "vision", "algorithms", "health"}]
|
|
49
|
+
evidence_requirements = _requirements_for_profile(delta.get("evidence_profile"))
|
|
48
50
|
|
|
49
51
|
if requires_security_check:
|
|
50
52
|
task_class = "security"
|
|
@@ -83,6 +85,7 @@ def run_preflight(project_dir: str, *, goal: str) -> dict[str, Any]:
|
|
|
83
85
|
"required_mcps": ["omg-control"] if route in {"security-check", "api-twin", "crazy"} else [],
|
|
84
86
|
"missing_constraints": [],
|
|
85
87
|
"evidence_plan": _evidence_plan(route),
|
|
88
|
+
"evidence_requirements": evidence_requirements,
|
|
86
89
|
"delta_classification": delta,
|
|
87
90
|
"domain_packs": domain_packs,
|
|
88
91
|
"trace": {"trace_id": trace["trace_id"], "path": trace["path"]},
|
|
@@ -101,6 +104,17 @@ def _requires_security_check(delta: dict[str, Any]) -> bool:
|
|
|
101
104
|
return False
|
|
102
105
|
|
|
103
106
|
|
|
107
|
+
def _requirements_for_profile(evidence_profile: str | None) -> list[str]:
|
|
108
|
+
module = importlib.import_module("runtime.evidence_requirements")
|
|
109
|
+
resolver = getattr(module, "requirements_for_profile", None)
|
|
110
|
+
if callable(resolver):
|
|
111
|
+
resolved = resolver(evidence_profile)
|
|
112
|
+
if isinstance(resolved, (list, tuple, set)):
|
|
113
|
+
return [str(item) for item in resolved]
|
|
114
|
+
full = getattr(module, "FULL_REQUIREMENTS", [])
|
|
115
|
+
return [str(item) for item in full]
|
|
116
|
+
|
|
117
|
+
|
|
104
118
|
def _required_tools(route: str) -> list[str]:
|
|
105
119
|
return {
|
|
106
120
|
"security-check": ["security"],
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
"""Canonical profile I/O — single owner of profile.yaml read/write/version.
|
|
2
|
+
|
|
3
|
+
All profile consumers should use this module to load and persist profile data.
|
|
4
|
+
``profile_version`` is derived from a canonical key-sorted JSON hash of the
|
|
5
|
+
parsed dict, never from raw file text.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import hashlib
|
|
11
|
+
import json
|
|
12
|
+
import os
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
import yaml
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def load_profile(path: str) -> dict[str, Any]:
|
|
19
|
+
"""Load a profile from *path*, returning a dict.
|
|
20
|
+
|
|
21
|
+
Handles both YAML and JSON-shaped content transparently (backward compat).
|
|
22
|
+
Returns ``{}`` when the file is missing or unreadable.
|
|
23
|
+
"""
|
|
24
|
+
if not os.path.exists(path):
|
|
25
|
+
return {}
|
|
26
|
+
try:
|
|
27
|
+
with open(path, "r", encoding="utf-8", errors="replace") as fh:
|
|
28
|
+
payload = yaml.safe_load(fh)
|
|
29
|
+
except Exception:
|
|
30
|
+
return {}
|
|
31
|
+
if isinstance(payload, dict):
|
|
32
|
+
return payload
|
|
33
|
+
return {}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def save_profile(path: str, data: dict[str, Any]) -> None:
|
|
37
|
+
"""Persist *data* as canonical YAML to *path*.
|
|
38
|
+
|
|
39
|
+
Creates parent directories when needed. Output is always YAML (never JSON)
|
|
40
|
+
so that ``profile_version_from_map`` produces a deterministic hash
|
|
41
|
+
regardless of how the file was previously written.
|
|
42
|
+
|
|
43
|
+
Uses an internal serializer that correctly round-trips empty collections
|
|
44
|
+
as ``[]`` / ``{}`` instead of bare ``key:`` (which the project yaml shim
|
|
45
|
+
parses back as ``None``).
|
|
46
|
+
"""
|
|
47
|
+
parent = os.path.dirname(path)
|
|
48
|
+
if parent:
|
|
49
|
+
os.makedirs(parent, exist_ok=True)
|
|
50
|
+
dumped = "\n".join(_dump_lines(data, 0)) + "\n"
|
|
51
|
+
with open(path, "w", encoding="utf-8") as fh:
|
|
52
|
+
fh.write(dumped)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def profile_version_from_map(data: dict[str, Any]) -> str:
|
|
56
|
+
"""Derive a deterministic version string from a parsed profile dict.
|
|
57
|
+
|
|
58
|
+
The version is the full SHA-256 hex digest of the canonical key-sorted
|
|
59
|
+
JSON representation. This is independent of on-disk formatting (JSON
|
|
60
|
+
vs YAML, whitespace, key ordering).
|
|
61
|
+
"""
|
|
62
|
+
canonical = json.dumps(data, sort_keys=True, ensure_ascii=True)
|
|
63
|
+
return hashlib.sha256(canonical.encode("utf-8")).hexdigest()
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
# ---------------------------------------------------------------------------
|
|
67
|
+
# Internal YAML serializer — mirrors the project yaml shim's ``_dump_lines``
|
|
68
|
+
# but renders empty collections as ``[]`` / ``{}`` so that ``yaml.safe_load``
|
|
69
|
+
# round-trips them correctly.
|
|
70
|
+
# ---------------------------------------------------------------------------
|
|
71
|
+
|
|
72
|
+
_YAML_SPECIAL_WORDS = frozenset(
|
|
73
|
+
("true", "false", "null", "yes", "no", "on", "off")
|
|
74
|
+
)
|
|
75
|
+
_YAML_SPECIAL_CHARS = set(":#{}[],&*?|>!%@`\"'")
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _dump_scalar(value: object) -> str:
|
|
79
|
+
"""Serialize a scalar to a YAML-safe string."""
|
|
80
|
+
if value is None:
|
|
81
|
+
return "null"
|
|
82
|
+
if isinstance(value, bool):
|
|
83
|
+
return "true" if value else "false"
|
|
84
|
+
if isinstance(value, (int, float)):
|
|
85
|
+
return str(value)
|
|
86
|
+
text = str(value)
|
|
87
|
+
if (
|
|
88
|
+
text.lower() in _YAML_SPECIAL_WORDS
|
|
89
|
+
or text != text.strip()
|
|
90
|
+
or any(ch in _YAML_SPECIAL_CHARS for ch in text)
|
|
91
|
+
or not text
|
|
92
|
+
or _looks_numeric(text)
|
|
93
|
+
):
|
|
94
|
+
return json.dumps(text, ensure_ascii=False)
|
|
95
|
+
return text
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _looks_numeric(text: str) -> bool:
|
|
99
|
+
"""Return True if *text* would be parsed as a number by yaml.safe_load."""
|
|
100
|
+
try:
|
|
101
|
+
float(text)
|
|
102
|
+
return True
|
|
103
|
+
except (TypeError, ValueError):
|
|
104
|
+
return False
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _dump_lines(value: object, indent: int) -> list[str]:
|
|
108
|
+
"""Recursively serialize *value* to a list of YAML lines."""
|
|
109
|
+
prefix = " " * indent
|
|
110
|
+
if isinstance(value, dict):
|
|
111
|
+
if not value:
|
|
112
|
+
return [f"{prefix}{{}}"]
|
|
113
|
+
lines: list[str] = []
|
|
114
|
+
for key, item in value.items():
|
|
115
|
+
if isinstance(item, dict):
|
|
116
|
+
if not item:
|
|
117
|
+
lines.append(f"{prefix}{key}: {{}}")
|
|
118
|
+
else:
|
|
119
|
+
lines.append(f"{prefix}{key}:")
|
|
120
|
+
lines.extend(_dump_lines(item, indent + 2))
|
|
121
|
+
elif isinstance(item, list):
|
|
122
|
+
if not item:
|
|
123
|
+
lines.append(f"{prefix}{key}: []")
|
|
124
|
+
else:
|
|
125
|
+
lines.append(f"{prefix}{key}:")
|
|
126
|
+
lines.extend(_dump_lines(item, indent + 2))
|
|
127
|
+
else:
|
|
128
|
+
lines.append(f"{prefix}{key}: {_dump_scalar(item)}")
|
|
129
|
+
return lines
|
|
130
|
+
if isinstance(value, list):
|
|
131
|
+
if not value:
|
|
132
|
+
return [f"{prefix}[]"]
|
|
133
|
+
lines = []
|
|
134
|
+
for item in value:
|
|
135
|
+
if isinstance(item, dict):
|
|
136
|
+
nested = _dump_lines(item, indent + 2)
|
|
137
|
+
if nested:
|
|
138
|
+
first = nested[0].strip()
|
|
139
|
+
lines.append(f"{prefix}- {first}")
|
|
140
|
+
lines.extend(nested[1:])
|
|
141
|
+
else:
|
|
142
|
+
lines.append(f"{prefix}- {{}}")
|
|
143
|
+
elif isinstance(item, list):
|
|
144
|
+
lines.append(f"{prefix}-")
|
|
145
|
+
lines.extend(_dump_lines(item, indent + 2))
|
|
146
|
+
else:
|
|
147
|
+
lines.append(f"{prefix}- {_dump_scalar(item)}")
|
|
148
|
+
return lines
|
|
149
|
+
return [f"{prefix}{_dump_scalar(value)}"]
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
_GOVERNED_SECTIONS = ("style", "safety")
|
|
153
|
+
_GOVERNED_CONFIRMATION_STATES = ("confirmed", "pending_confirmation", "inferred")
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def classify_preference_section(field: str, value: str) -> str:
|
|
157
|
+
token = f"{field} {value}".lower()
|
|
158
|
+
if "safety" in token or "guardrail" in token or "override" in token:
|
|
159
|
+
return "safety"
|
|
160
|
+
return "style"
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def is_destructive_preference(field: str, value: str) -> bool:
|
|
164
|
+
token = f"{field} {value}".lower()
|
|
165
|
+
destructive_tokens = (
|
|
166
|
+
"disable",
|
|
167
|
+
"off",
|
|
168
|
+
"bypass",
|
|
169
|
+
"ignore",
|
|
170
|
+
"override",
|
|
171
|
+
"unsafe",
|
|
172
|
+
"no_guard",
|
|
173
|
+
"no guard",
|
|
174
|
+
)
|
|
175
|
+
return any(piece in token for piece in destructive_tokens)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def ensure_governed_preferences(profile: dict[str, Any]) -> None:
|
|
179
|
+
governed_obj = profile.get("governed_preferences")
|
|
180
|
+
governed = governed_obj if isinstance(governed_obj, dict) else {}
|
|
181
|
+
for section in _GOVERNED_SECTIONS:
|
|
182
|
+
raw_entries = governed.get(section)
|
|
183
|
+
if not isinstance(raw_entries, list):
|
|
184
|
+
governed[section] = []
|
|
185
|
+
continue
|
|
186
|
+
normalized: list[dict[str, Any]] = []
|
|
187
|
+
for raw in raw_entries:
|
|
188
|
+
if not isinstance(raw, dict):
|
|
189
|
+
continue
|
|
190
|
+
entry = _normalize_governed_entry(raw, section)
|
|
191
|
+
if entry is not None:
|
|
192
|
+
normalized.append(entry)
|
|
193
|
+
governed[section] = normalized
|
|
194
|
+
profile["governed_preferences"] = governed
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def upsert_governed_preference(
|
|
198
|
+
profile: dict[str, Any],
|
|
199
|
+
*,
|
|
200
|
+
field: str,
|
|
201
|
+
value: str,
|
|
202
|
+
source: str,
|
|
203
|
+
learned_at: str,
|
|
204
|
+
updated_at: str,
|
|
205
|
+
section: str,
|
|
206
|
+
confirmation_state: str,
|
|
207
|
+
decay_metadata: dict[str, Any] | None,
|
|
208
|
+
) -> None:
|
|
209
|
+
ensure_governed_preferences(profile)
|
|
210
|
+
governed = profile["governed_preferences"]
|
|
211
|
+
if not isinstance(governed, dict):
|
|
212
|
+
return
|
|
213
|
+
target = governed.get(section)
|
|
214
|
+
if not isinstance(target, list):
|
|
215
|
+
target = []
|
|
216
|
+
|
|
217
|
+
match_index = -1
|
|
218
|
+
for idx, raw_entry in enumerate(target):
|
|
219
|
+
if not isinstance(raw_entry, dict):
|
|
220
|
+
continue
|
|
221
|
+
if str(raw_entry.get("field", "")).strip() == field and str(raw_entry.get("value", "")).strip() == value:
|
|
222
|
+
match_index = idx
|
|
223
|
+
break
|
|
224
|
+
|
|
225
|
+
baseline: dict[str, Any] = {
|
|
226
|
+
"field": field,
|
|
227
|
+
"value": value,
|
|
228
|
+
"source": source,
|
|
229
|
+
"learned_at": learned_at,
|
|
230
|
+
"updated_at": updated_at,
|
|
231
|
+
"section": section,
|
|
232
|
+
"confirmation_state": confirmation_state,
|
|
233
|
+
}
|
|
234
|
+
if section == "style" and decay_metadata is not None:
|
|
235
|
+
baseline["decay_metadata"] = decay_metadata
|
|
236
|
+
|
|
237
|
+
if match_index == -1:
|
|
238
|
+
target.append(baseline)
|
|
239
|
+
else:
|
|
240
|
+
existing = target[match_index]
|
|
241
|
+
if isinstance(existing, dict):
|
|
242
|
+
baseline["learned_at"] = str(existing.get("learned_at", learned_at)).strip() or learned_at
|
|
243
|
+
target[match_index] = baseline
|
|
244
|
+
|
|
245
|
+
governed[section] = target
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def _normalize_governed_entry(raw: dict[str, Any], section: str) -> dict[str, Any] | None:
|
|
249
|
+
field = str(raw.get("field", "")).strip()
|
|
250
|
+
value = " ".join(str(raw.get("value", "")).strip().split())
|
|
251
|
+
source = str(raw.get("source", "")).strip()
|
|
252
|
+
learned_at = str(raw.get("learned_at", "")).strip()
|
|
253
|
+
updated_at = str(raw.get("updated_at", "")).strip()
|
|
254
|
+
section_raw = str(raw.get("section", section)).strip().lower()
|
|
255
|
+
confirmation_state = str(raw.get("confirmation_state", "")).strip().lower()
|
|
256
|
+
|
|
257
|
+
if not (field and value and source and learned_at and updated_at):
|
|
258
|
+
return None
|
|
259
|
+
if section_raw not in _GOVERNED_SECTIONS:
|
|
260
|
+
return None
|
|
261
|
+
if confirmation_state not in _GOVERNED_CONFIRMATION_STATES:
|
|
262
|
+
return None
|
|
263
|
+
|
|
264
|
+
out: dict[str, Any] = {
|
|
265
|
+
"field": field,
|
|
266
|
+
"value": value,
|
|
267
|
+
"source": source,
|
|
268
|
+
"learned_at": learned_at,
|
|
269
|
+
"updated_at": updated_at,
|
|
270
|
+
"section": section_raw,
|
|
271
|
+
"confirmation_state": confirmation_state,
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
if section_raw == "style" and confirmation_state == "inferred":
|
|
275
|
+
decay_raw = raw.get("decay_metadata")
|
|
276
|
+
decay = decay_raw if isinstance(decay_raw, dict) else {}
|
|
277
|
+
out["decay_metadata"] = {
|
|
278
|
+
"decay_score": _coerce_decay_score(decay.get("decay_score", 0.0)),
|
|
279
|
+
"last_seen_at": str(decay.get("last_seen_at", updated_at)).strip() or updated_at,
|
|
280
|
+
"decay_reason": str(decay.get("decay_reason", "inferred_signal")).strip() or "inferred_signal",
|
|
281
|
+
}
|
|
282
|
+
return out
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
def _coerce_decay_score(raw: Any) -> float:
|
|
286
|
+
try:
|
|
287
|
+
score = float(raw)
|
|
288
|
+
except (TypeError, ValueError):
|
|
289
|
+
score = 0.0
|
|
290
|
+
if score < 0.0:
|
|
291
|
+
return 0.0
|
|
292
|
+
if score > 1.0:
|
|
293
|
+
return 1.0
|
|
294
|
+
return round(score, 3)
|