@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
package/runtime/preflight.py
CHANGED
|
@@ -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)
|
package/runtime/proof_gate.py
CHANGED
|
@@ -1,14 +1,23 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import hashlib
|
|
4
|
+
import os
|
|
4
5
|
from pathlib import Path
|
|
5
6
|
from typing import Any
|
|
6
7
|
|
|
7
8
|
from runtime import artifact_parsers
|
|
9
|
+
from runtime.evidence_requirements import FULL_REQUIREMENTS, requirements_for_profile
|
|
8
10
|
|
|
9
11
|
|
|
10
12
|
_REQUIRED_ARTIFACT_FIELDS = ("kind", "path", "sha256", "parser", "summary", "trace_id")
|
|
11
13
|
|
|
14
|
+
_REQUIRED_ARTIFACT_TOKENS: dict[str, tuple[str, ...]] = {
|
|
15
|
+
"junit": ("junit", "junit.xml", "surefire"),
|
|
16
|
+
"coverage": ("coverage", "lcov", "coverage.xml"),
|
|
17
|
+
"sarif": ("sarif", ".sarif"),
|
|
18
|
+
"browser_trace": ("trace.zip", "browser_trace", "playwright", "browser-evidence"),
|
|
19
|
+
}
|
|
20
|
+
|
|
12
21
|
|
|
13
22
|
def evaluate_proof_gate(input: dict[str, Any]) -> dict[str, Any]:
|
|
14
23
|
claims = _as_claims(input.get("claims"))
|
|
@@ -17,6 +26,10 @@ def evaluate_proof_gate(input: dict[str, Any]) -> dict[str, Any]:
|
|
|
17
26
|
security_evidence = _as_dict(input.get("security_evidence"))
|
|
18
27
|
browser_evidence = _as_dict(input.get("browser_evidence"))
|
|
19
28
|
evidence_pack = _as_dict(input.get("evidence_pack"))
|
|
29
|
+
evidence_profile = _resolve_evidence_profile(input=input, evidence_pack=evidence_pack)
|
|
30
|
+
evidence_requirements = requirements_for_profile(evidence_profile)
|
|
31
|
+
test_intent_lock = _as_dict(input.get("test_intent_lock"))
|
|
32
|
+
test_delta = _as_dict(input.get("test_delta"))
|
|
20
33
|
|
|
21
34
|
blockers: list[str] = []
|
|
22
35
|
if not claims:
|
|
@@ -32,21 +45,37 @@ def evaluate_proof_gate(input: dict[str, Any]) -> dict[str, Any]:
|
|
|
32
45
|
blockers.append("proof_gate_proof_chain_blockers_invalid")
|
|
33
46
|
|
|
34
47
|
trace_id = str(proof_chain.get("trace_id", "")).strip()
|
|
35
|
-
blockers.extend(_validate_claim_artifacts(claims))
|
|
48
|
+
blockers.extend(_validate_claim_artifacts(claims, evidence_requirements=evidence_requirements))
|
|
36
49
|
blockers.extend(_validate_trace_linkage(claims=claims, trace_id=trace_id, eval_output=eval_output, browser_evidence=browser_evidence))
|
|
37
50
|
blockers.extend(_validate_security_and_browser_artifacts(claims=claims, security_evidence=security_evidence, browser_evidence=browser_evidence))
|
|
38
51
|
blockers.extend(_validate_evidence_pack(evidence_pack))
|
|
39
52
|
|
|
53
|
+
strict_mode = os.environ.get("OMG_PROOF_CHAIN_STRICT", "0").strip() == "1"
|
|
54
|
+
causal_chain_blockers = _validate_lock_delta_chain(
|
|
55
|
+
claims=claims,
|
|
56
|
+
test_intent_lock=test_intent_lock,
|
|
57
|
+
test_delta=test_delta,
|
|
58
|
+
evidence_pack=evidence_pack,
|
|
59
|
+
)
|
|
60
|
+
if strict_mode:
|
|
61
|
+
blockers.extend(causal_chain_blockers)
|
|
62
|
+
|
|
40
63
|
unique_blockers = list(dict.fromkeys(item for item in blockers if str(item).strip()))
|
|
64
|
+
advisories = [] if strict_mode else causal_chain_blockers
|
|
41
65
|
evidence_summary = {
|
|
42
66
|
"claim_count": len(claims),
|
|
43
67
|
"proof_chain_status": proof_status,
|
|
44
68
|
"proof_chain_blocker_count": len(proof_blockers) if isinstance(proof_blockers, list) else 0,
|
|
45
|
-
"required_artifacts":
|
|
69
|
+
"required_artifacts": _required_artifact_keys(evidence_requirements),
|
|
70
|
+
"evidence_profile": evidence_profile,
|
|
71
|
+
"evidence_requirements": list(evidence_requirements),
|
|
46
72
|
"trace_id": trace_id,
|
|
47
73
|
"eval_trace_id": str(eval_output.get("trace_id", "")).strip(),
|
|
48
74
|
"has_security_evidence": bool(security_evidence),
|
|
49
75
|
"has_browser_evidence": bool(browser_evidence),
|
|
76
|
+
"has_lock_evidence": _has_lock_evidence(claims, test_intent_lock, evidence_pack),
|
|
77
|
+
"has_waiver_artifact": _has_waiver_artifact(test_delta or _as_dict(evidence_pack.get("test_delta"))),
|
|
78
|
+
"advisories": advisories,
|
|
50
79
|
}
|
|
51
80
|
return {
|
|
52
81
|
"schema": "ProofGateResult",
|
|
@@ -100,7 +129,11 @@ def _collect_trace_ids(claim: dict[str, Any]) -> set[str]:
|
|
|
100
129
|
return {str(item).strip() for item in raw_trace_ids if str(item).strip()}
|
|
101
130
|
|
|
102
131
|
|
|
103
|
-
def _validate_claim_artifacts(
|
|
132
|
+
def _validate_claim_artifacts(
|
|
133
|
+
claims: list[dict[str, Any]],
|
|
134
|
+
*,
|
|
135
|
+
evidence_requirements: list[str],
|
|
136
|
+
) -> list[str]:
|
|
104
137
|
all_artifacts: list[str] = []
|
|
105
138
|
artifact_records: list[dict[str, Any]] = []
|
|
106
139
|
for claim in claims:
|
|
@@ -109,10 +142,9 @@ def _validate_claim_artifacts(claims: list[dict[str, Any]]) -> list[str]:
|
|
|
109
142
|
|
|
110
143
|
blockers: list[str] = []
|
|
111
144
|
required_tokens = {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
"browser_trace": ("trace.zip", "browser_trace", "playwright", "browser-evidence"),
|
|
145
|
+
key: _REQUIRED_ARTIFACT_TOKENS[key]
|
|
146
|
+
for key in _required_artifact_keys(evidence_requirements)
|
|
147
|
+
if key in _REQUIRED_ARTIFACT_TOKENS
|
|
116
148
|
}
|
|
117
149
|
for key, tokens in required_tokens.items():
|
|
118
150
|
if not any(any(token in artifact for token in tokens) for artifact in all_artifacts):
|
|
@@ -256,6 +288,89 @@ def _validate_artifact_hash(artifact: dict[str, Any]) -> str | None:
|
|
|
256
288
|
return None
|
|
257
289
|
|
|
258
290
|
|
|
291
|
+
def _has_lock_evidence(claims: list[dict[str, Any]], test_intent_lock: dict[str, Any], evidence_pack: dict[str, Any]) -> bool:
|
|
292
|
+
lock_status = str(test_intent_lock.get("status", "")).strip().lower()
|
|
293
|
+
if lock_status == "ok":
|
|
294
|
+
return True
|
|
295
|
+
if str(test_intent_lock.get("lock_id", "")).strip():
|
|
296
|
+
return True
|
|
297
|
+
|
|
298
|
+
test_delta = _as_dict(evidence_pack.get("test_delta"))
|
|
299
|
+
if str(test_delta.get("lock_id", "")).strip():
|
|
300
|
+
return True
|
|
301
|
+
|
|
302
|
+
for claim in claims:
|
|
303
|
+
if str(claim.get("lock_id", "")).strip():
|
|
304
|
+
return True
|
|
305
|
+
lock_verification = _as_dict(claim.get("lock_verification"))
|
|
306
|
+
if str(lock_verification.get("status", "")).strip().lower() == "ok":
|
|
307
|
+
return True
|
|
308
|
+
return False
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def _has_waiver_artifact(delta_summary: dict[str, Any]) -> bool:
|
|
312
|
+
waiver = delta_summary.get("waiver_artifact")
|
|
313
|
+
if isinstance(waiver, dict):
|
|
314
|
+
for field in ("artifact_path", "path", "id", "reason"):
|
|
315
|
+
if str(waiver.get(field, "")).strip():
|
|
316
|
+
return True
|
|
317
|
+
return bool(str(delta_summary.get("waiver_artifact_path", "")).strip())
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
def _is_weakened_or_drift_delta(delta_summary: dict[str, Any]) -> bool:
|
|
321
|
+
flags = delta_summary.get("flags")
|
|
322
|
+
if not isinstance(flags, list):
|
|
323
|
+
return False
|
|
324
|
+
risk_flags = {
|
|
325
|
+
"weakened_assertions",
|
|
326
|
+
"tests_mismatch",
|
|
327
|
+
"selector_drift",
|
|
328
|
+
"removed_touched_area_coverage",
|
|
329
|
+
"integration_to_mock_downgrade",
|
|
330
|
+
"snapshot_only_refresh",
|
|
331
|
+
}
|
|
332
|
+
normalized_flags = {str(item).strip().lower() for item in flags if str(item).strip()}
|
|
333
|
+
return bool(normalized_flags & risk_flags)
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
def _validate_lock_delta_chain(
|
|
337
|
+
*,
|
|
338
|
+
claims: list[dict[str, Any]],
|
|
339
|
+
test_intent_lock: dict[str, Any],
|
|
340
|
+
test_delta: dict[str, Any],
|
|
341
|
+
evidence_pack: dict[str, Any],
|
|
342
|
+
) -> list[str]:
|
|
343
|
+
blockers: list[str] = []
|
|
344
|
+
if not _has_lock_evidence(claims, test_intent_lock, evidence_pack):
|
|
345
|
+
blockers.append("proof_gate_missing_lock_evidence")
|
|
346
|
+
|
|
347
|
+
delta_summary = test_delta if test_delta else _as_dict(evidence_pack.get("test_delta"))
|
|
348
|
+
if _is_weakened_or_drift_delta(delta_summary) and not _has_waiver_artifact(delta_summary):
|
|
349
|
+
blockers.append("proof_gate_missing_waiver_artifact")
|
|
350
|
+
return blockers
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
def _resolve_evidence_profile(*, input: dict[str, Any], evidence_pack: dict[str, Any]) -> str:
|
|
354
|
+
profile = str(input.get("evidence_profile", "")).strip()
|
|
355
|
+
if profile:
|
|
356
|
+
return profile
|
|
357
|
+
return str(evidence_pack.get("evidence_profile", "")).strip()
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
def _required_artifact_keys(evidence_requirements: list[str]) -> list[str]:
|
|
361
|
+
required: list[str] = []
|
|
362
|
+
requirement_set = {str(item).strip() for item in evidence_requirements if str(item).strip()}
|
|
363
|
+
if "tests" in requirement_set:
|
|
364
|
+
required.append("junit")
|
|
365
|
+
if "build" in requirement_set:
|
|
366
|
+
required.append("coverage")
|
|
367
|
+
if "security_scan" in requirement_set:
|
|
368
|
+
required.append("sarif")
|
|
369
|
+
if requirement_set == set(FULL_REQUIREMENTS):
|
|
370
|
+
required.append("browser_trace")
|
|
371
|
+
return required
|
|
372
|
+
|
|
373
|
+
|
|
259
374
|
_PARSERS: dict[str, Any] = {
|
|
260
375
|
"junit": artifact_parsers.parse_junit,
|
|
261
376
|
"sarif": artifact_parsers.parse_sarif,
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
import shutil
|
|
6
|
+
import subprocess
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import cast
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
from runtime.cli_provider import CLIProvider, register_provider
|
|
12
|
+
from runtime.mcp_config_writers import _atomic_write_text # pyright: ignore[reportPrivateUsage]
|
|
13
|
+
|
|
14
|
+
HOST_RULES = {
|
|
15
|
+
"compilation_targets": ["opencode.json"],
|
|
16
|
+
"mcp": ["omg-control"],
|
|
17
|
+
"mcp_key": "mcp",
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class OpenCodeProvider(CLIProvider):
|
|
22
|
+
def get_name(self) -> str: # pyright: ignore[reportImplicitOverride]
|
|
23
|
+
return "opencode"
|
|
24
|
+
|
|
25
|
+
def detect(self) -> bool: # pyright: ignore[reportImplicitOverride]
|
|
26
|
+
return shutil.which("opencode") is not None
|
|
27
|
+
|
|
28
|
+
def check_auth(self) -> tuple[bool | None, str]: # pyright: ignore[reportImplicitOverride]
|
|
29
|
+
auth_path = Path(os.path.expanduser("~/.local/share/opencode/auth.json"))
|
|
30
|
+
if not auth_path.exists():
|
|
31
|
+
return False, f"auth not found: missing {auth_path}"
|
|
32
|
+
try:
|
|
33
|
+
parsed = cast(object, json.loads(auth_path.read_text()))
|
|
34
|
+
except (json.JSONDecodeError, ValueError) as exc:
|
|
35
|
+
return False, f"auth not found: invalid json: {exc}"
|
|
36
|
+
except OSError as exc:
|
|
37
|
+
return False, f"auth not found: unreadable file: {exc}"
|
|
38
|
+
|
|
39
|
+
if not parsed:
|
|
40
|
+
return False, "auth not found: empty auth content"
|
|
41
|
+
return True, "auth found"
|
|
42
|
+
|
|
43
|
+
def invoke(self, prompt: str, project_dir: str, timeout: int = 120) -> dict[str, Any]: # pyright: ignore[reportExplicitAny, reportImplicitOverride]
|
|
44
|
+
try:
|
|
45
|
+
result = self.run_tool(["opencode", prompt], timeout=timeout)
|
|
46
|
+
return {
|
|
47
|
+
"model": "opencode-cli",
|
|
48
|
+
"output": result.stdout,
|
|
49
|
+
"exit_code": result.returncode,
|
|
50
|
+
}
|
|
51
|
+
except subprocess.TimeoutExpired:
|
|
52
|
+
return {"error": "opencode-cli timeout", "fallback": "claude"}
|
|
53
|
+
except FileNotFoundError:
|
|
54
|
+
return {"error": "opencode-cli not found", "fallback": "claude"}
|
|
55
|
+
except Exception as exc:
|
|
56
|
+
return {"error": str(exc), "fallback": "claude"}
|
|
57
|
+
|
|
58
|
+
def invoke_tmux(self, prompt: str, project_dir: str, timeout: int = 120) -> dict[str, Any]: # pyright: ignore[reportExplicitAny, reportImplicitOverride]
|
|
59
|
+
return self.invoke(prompt, project_dir, timeout=timeout)
|
|
60
|
+
|
|
61
|
+
def get_non_interactive_cmd(self, prompt: str) -> list[str]: # pyright: ignore[reportImplicitOverride]
|
|
62
|
+
return ["opencode", prompt]
|
|
63
|
+
|
|
64
|
+
def get_config_path(self) -> str: # pyright: ignore[reportImplicitOverride]
|
|
65
|
+
return os.path.expanduser("~/.config/opencode/opencode.json")
|
|
66
|
+
|
|
67
|
+
def get_project_config_path(self, root: str = ".") -> str:
|
|
68
|
+
return str(Path(root) / "opencode.json")
|
|
69
|
+
|
|
70
|
+
def get_plugin_dir(self, root: str = ".") -> str:
|
|
71
|
+
return str(Path(root) / ".opencode" / "plugins")
|
|
72
|
+
|
|
73
|
+
def write_mcp_config(self, server_url: str, server_name: str = "memory-server") -> None: # pyright: ignore[reportImplicitOverride]
|
|
74
|
+
config_path = Path(self.get_config_path())
|
|
75
|
+
config: dict[str, object]
|
|
76
|
+
if config_path.exists():
|
|
77
|
+
try:
|
|
78
|
+
loaded = cast(object, json.loads(config_path.read_text()))
|
|
79
|
+
config = cast(dict[str, object], loaded) if isinstance(loaded, dict) else {}
|
|
80
|
+
except (json.JSONDecodeError, ValueError):
|
|
81
|
+
config = {}
|
|
82
|
+
else:
|
|
83
|
+
config = {}
|
|
84
|
+
|
|
85
|
+
mcp = config.get("mcp")
|
|
86
|
+
if not isinstance(mcp, dict):
|
|
87
|
+
mcp = {}
|
|
88
|
+
config["mcp"] = mcp
|
|
89
|
+
|
|
90
|
+
mcp[server_name] = {"type": "http", "url": server_url}
|
|
91
|
+
_atomic_write_text(config_path, json.dumps(config, indent=2) + "\n")
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
register_provider(OpenCodeProvider())
|