@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
|
@@ -7,7 +7,9 @@ from __future__ import annotations
|
|
|
7
7
|
import json
|
|
8
8
|
import logging
|
|
9
9
|
import os
|
|
10
|
+
import re
|
|
10
11
|
import sys
|
|
12
|
+
from pathlib import Path
|
|
11
13
|
from typing import Any, cast
|
|
12
14
|
|
|
13
15
|
import yaml
|
|
@@ -35,6 +37,7 @@ from runtime.mcp_config_writers import ( # noqa: E402
|
|
|
35
37
|
import runtime.providers.codex_provider # noqa: E402, F401
|
|
36
38
|
import runtime.providers.gemini_provider # noqa: E402, F401
|
|
37
39
|
import runtime.providers.kimi_provider # noqa: E402, F401
|
|
40
|
+
from runtime.validate import run_validate as _run_post_install_validate # noqa: E402
|
|
38
41
|
from runtime.adoption import ( # noqa: E402
|
|
39
42
|
CANONICAL_MODE_NAMES,
|
|
40
43
|
CANONICAL_VERSION,
|
|
@@ -191,6 +194,28 @@ MCP_CATALOG: list[dict[str, Any]] = [
|
|
|
191
194
|
"default": False,
|
|
192
195
|
"category": "memory",
|
|
193
196
|
},
|
|
197
|
+
{
|
|
198
|
+
"id": "notebooklm",
|
|
199
|
+
"name": "NotebookLM",
|
|
200
|
+
"description": "NotebookLM MCP server for AI-powered research and note-taking",
|
|
201
|
+
"command": "npx",
|
|
202
|
+
"args": ["-y", "notebooklm-mcp@latest"],
|
|
203
|
+
"default": False,
|
|
204
|
+
"category": "productivity",
|
|
205
|
+
"warning": (
|
|
206
|
+
"NOTEBOOKLM MCP WARNING\n\n"
|
|
207
|
+
"NotebookLM requires browser automation and has several important considerations:\n\n"
|
|
208
|
+
"BROWSER AUTOMATION: NotebookLM uses Puppeteer for browser automation. "
|
|
209
|
+
"This requires a Chromium/Chrome installation and may consume significant system resources.\n\n"
|
|
210
|
+
"FIRST-RUN DOWNLOAD: On first run, NotebookLM will download a full browser instance "
|
|
211
|
+
"(typically 100-300 MB). This is a one-time operation but may take several minutes.\n\n"
|
|
212
|
+
"DEDICATED ACCOUNT: It is strongly recommended to use a dedicated Google account "
|
|
213
|
+
"for NotebookLM MCP. Do NOT use your primary account, as the MCP will store credentials locally.\n\n"
|
|
214
|
+
"AUTH EXPIRY: Google authentication tokens expire periodically. "
|
|
215
|
+
"You may need to re-authenticate if the MCP fails with auth errors.\n\n"
|
|
216
|
+
"Enable NotebookLM only if you understand these requirements and accept the risks."
|
|
217
|
+
),
|
|
218
|
+
},
|
|
194
219
|
]
|
|
195
220
|
|
|
196
221
|
_MCP_ID_ALIASES: dict[str, str] = {
|
|
@@ -451,6 +476,12 @@ def check_auth() -> dict[str, Any]:
|
|
|
451
476
|
|
|
452
477
|
_HTTP_MEMORY_MIN_LEVEL: int = _PRESET_LEVEL["interop"]
|
|
453
478
|
|
|
479
|
+
_PROFILE_ARCH_REQUEST_MAX = 8
|
|
480
|
+
_PROFILE_TAG_MAX = 12
|
|
481
|
+
_PROFILE_SUMMARY_MAX_CHARS = 240
|
|
482
|
+
_PROFILE_RECENT_UPDATES_MAX = 5
|
|
483
|
+
_PROFILE_GOVERNED_MAX = 24
|
|
484
|
+
|
|
454
485
|
|
|
455
486
|
def get_mode_choices() -> list[str]:
|
|
456
487
|
return list(CANONICAL_MODE_NAMES)
|
|
@@ -660,6 +691,8 @@ def set_preferences(project_dir: str, preferences: dict[str, Any]) -> dict[str,
|
|
|
660
691
|
state_dir = os.path.join(project_dir, ".omg", "state")
|
|
661
692
|
os.makedirs(state_dir, exist_ok=True)
|
|
662
693
|
|
|
694
|
+
_write_profile_learning_sections(state_dir, preferences)
|
|
695
|
+
|
|
663
696
|
# Write config to YAML file
|
|
664
697
|
config_path = os.path.join(state_dir, "cli-config.yaml")
|
|
665
698
|
with open(config_path, "w") as f:
|
|
@@ -674,6 +707,260 @@ def set_preferences(project_dir: str, preferences: dict[str, Any]) -> dict[str,
|
|
|
674
707
|
}
|
|
675
708
|
|
|
676
709
|
|
|
710
|
+
def _write_profile_learning_sections(state_dir: str, preferences: dict[str, Any]) -> None:
|
|
711
|
+
profile_path = os.path.join(state_dir, "profile.yaml")
|
|
712
|
+
profile_data = _load_profile_yaml(profile_path)
|
|
713
|
+
_ensure_profile_baseline(profile_data)
|
|
714
|
+
|
|
715
|
+
profile_data["preferences"] = _normalize_preferences_block(preferences.get("preferences"))
|
|
716
|
+
profile_data["user_vector"] = _normalize_user_vector_block(preferences.get("user_vector"))
|
|
717
|
+
profile_data["profile_provenance"] = _normalize_provenance_block(preferences.get("profile_provenance"))
|
|
718
|
+
profile_data["governed_preferences"] = _normalize_governed_preferences_block(preferences.get("governed_preferences"))
|
|
719
|
+
|
|
720
|
+
from runtime.profile_io import save_profile
|
|
721
|
+
save_profile(profile_path, profile_data)
|
|
722
|
+
|
|
723
|
+
|
|
724
|
+
def _render_explicit_empty_collections(dumped: str) -> str:
|
|
725
|
+
lines = dumped.splitlines()
|
|
726
|
+
output: list[str] = []
|
|
727
|
+
|
|
728
|
+
def _next_line(start: int) -> str:
|
|
729
|
+
if start + 1 < len(lines):
|
|
730
|
+
return lines[start + 1]
|
|
731
|
+
return ""
|
|
732
|
+
|
|
733
|
+
for idx, line in enumerate(lines):
|
|
734
|
+
next_line = _next_line(idx)
|
|
735
|
+
stripped = line.strip()
|
|
736
|
+
|
|
737
|
+
if stripped == "stack:" and (not next_line or not next_line.startswith("- ")):
|
|
738
|
+
output.append("stack: []")
|
|
739
|
+
continue
|
|
740
|
+
if stripped == "conventions:" and (not next_line or not next_line.startswith(" ")):
|
|
741
|
+
output.append("conventions: {}")
|
|
742
|
+
continue
|
|
743
|
+
if stripped == "ai_behavior:" and (not next_line or not next_line.startswith(" ")):
|
|
744
|
+
output.append("ai_behavior: {}")
|
|
745
|
+
continue
|
|
746
|
+
if stripped == "architecture_requests:" and (not next_line or not next_line.startswith(" - ")):
|
|
747
|
+
output.append(" architecture_requests: []")
|
|
748
|
+
continue
|
|
749
|
+
if stripped == "constraints:" and (not next_line or not next_line.startswith(" ")):
|
|
750
|
+
output.append(" constraints: {}")
|
|
751
|
+
continue
|
|
752
|
+
if stripped == "tags:" and (not next_line or not next_line.startswith(" - ")):
|
|
753
|
+
output.append(" tags: []")
|
|
754
|
+
continue
|
|
755
|
+
if stripped == "recent_updates:" and (not next_line or not next_line.startswith(" - ")):
|
|
756
|
+
output.append(" recent_updates: []")
|
|
757
|
+
continue
|
|
758
|
+
|
|
759
|
+
output.append(line)
|
|
760
|
+
|
|
761
|
+
return "\n".join(output) + "\n"
|
|
762
|
+
|
|
763
|
+
|
|
764
|
+
def _load_profile_yaml(profile_path: str) -> dict[str, Any]:
|
|
765
|
+
from runtime.profile_io import load_profile
|
|
766
|
+
return load_profile(profile_path)
|
|
767
|
+
|
|
768
|
+
|
|
769
|
+
def _ensure_profile_baseline(profile_data: dict[str, Any]) -> None:
|
|
770
|
+
profile_data.setdefault("name", "omg-project")
|
|
771
|
+
profile_data.setdefault("description", "initialized by OMG standalone compat bootstrap")
|
|
772
|
+
profile_data.setdefault("language", "unknown")
|
|
773
|
+
profile_data.setdefault("framework", "unknown")
|
|
774
|
+
profile_data.setdefault("stack", [])
|
|
775
|
+
profile_data.setdefault("conventions", {})
|
|
776
|
+
profile_data.setdefault("ai_behavior", {})
|
|
777
|
+
profile_data.setdefault("governed_preferences", {"style": [], "safety": []})
|
|
778
|
+
|
|
779
|
+
|
|
780
|
+
def _normalize_preferences_block(raw: Any) -> dict[str, Any]:
|
|
781
|
+
block = raw if isinstance(raw, dict) else {}
|
|
782
|
+
|
|
783
|
+
requests_obj = block.get("architecture_requests")
|
|
784
|
+
architecture_requests: list[str] = []
|
|
785
|
+
if isinstance(requests_obj, list):
|
|
786
|
+
for value in requests_obj:
|
|
787
|
+
text = str(value).strip()
|
|
788
|
+
if text:
|
|
789
|
+
architecture_requests.append(text)
|
|
790
|
+
if len(architecture_requests) >= _PROFILE_ARCH_REQUEST_MAX:
|
|
791
|
+
break
|
|
792
|
+
|
|
793
|
+
constraints_obj = block.get("constraints")
|
|
794
|
+
constraints: dict[str, Any] = {}
|
|
795
|
+
if isinstance(constraints_obj, dict):
|
|
796
|
+
for key, value in constraints_obj.items():
|
|
797
|
+
normalized_key = _normalize_constraint_key(str(key))
|
|
798
|
+
normalized_value = _normalize_constraint_value(value)
|
|
799
|
+
if normalized_key and normalized_value is not None:
|
|
800
|
+
constraints[normalized_key] = normalized_value
|
|
801
|
+
|
|
802
|
+
routing_obj = block.get("routing")
|
|
803
|
+
routing_map = routing_obj if isinstance(routing_obj, dict) else {}
|
|
804
|
+
routing = {
|
|
805
|
+
"prefer_clarification": bool(routing_map.get("prefer_clarification", False)),
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
return {
|
|
809
|
+
"architecture_requests": architecture_requests,
|
|
810
|
+
"constraints": constraints,
|
|
811
|
+
"routing": routing,
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
|
|
815
|
+
def _normalize_constraint_key(value: str) -> str:
|
|
816
|
+
normalized = value.strip().lower()
|
|
817
|
+
normalized = re.sub(r"\s+", "_", normalized)
|
|
818
|
+
normalized = re.sub(r"[^a-z0-9_]+", "", normalized)
|
|
819
|
+
return normalized
|
|
820
|
+
|
|
821
|
+
|
|
822
|
+
def _normalize_constraint_value(value: Any) -> str | int | float | bool | None:
|
|
823
|
+
if isinstance(value, bool):
|
|
824
|
+
return value
|
|
825
|
+
if isinstance(value, int):
|
|
826
|
+
return value
|
|
827
|
+
if isinstance(value, float):
|
|
828
|
+
return value
|
|
829
|
+
if value is None:
|
|
830
|
+
return None
|
|
831
|
+
text = str(value).strip().lower()
|
|
832
|
+
return text if text else None
|
|
833
|
+
|
|
834
|
+
|
|
835
|
+
def _normalize_user_vector_block(raw: Any) -> dict[str, Any]:
|
|
836
|
+
block = raw if isinstance(raw, dict) else {}
|
|
837
|
+
|
|
838
|
+
tags_obj = block.get("tags")
|
|
839
|
+
tags: list[str] = []
|
|
840
|
+
if isinstance(tags_obj, list):
|
|
841
|
+
for value in tags_obj:
|
|
842
|
+
token = _normalize_tag_token(str(value))
|
|
843
|
+
if token:
|
|
844
|
+
tags.append(token)
|
|
845
|
+
if len(tags) >= _PROFILE_TAG_MAX:
|
|
846
|
+
break
|
|
847
|
+
|
|
848
|
+
summary = ""
|
|
849
|
+
summary_obj = block.get("summary")
|
|
850
|
+
if isinstance(summary_obj, str):
|
|
851
|
+
summary = " ".join(summary_obj.strip().split())[:_PROFILE_SUMMARY_MAX_CHARS]
|
|
852
|
+
|
|
853
|
+
confidence = 0.0
|
|
854
|
+
confidence_obj = block.get("confidence")
|
|
855
|
+
if isinstance(confidence_obj, (int, float, str)):
|
|
856
|
+
try:
|
|
857
|
+
confidence = float(confidence_obj)
|
|
858
|
+
except (TypeError, ValueError):
|
|
859
|
+
confidence = 0.0
|
|
860
|
+
confidence = max(0.0, min(1.0, confidence))
|
|
861
|
+
|
|
862
|
+
return {
|
|
863
|
+
"tags": tags,
|
|
864
|
+
"summary": summary,
|
|
865
|
+
"confidence": confidence,
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
|
|
869
|
+
def _normalize_tag_token(value: str) -> str:
|
|
870
|
+
normalized = value.strip().lower()
|
|
871
|
+
normalized = re.sub(r"\s+", "_", normalized)
|
|
872
|
+
normalized = re.sub(r"[^a-z0-9_\-]+", "", normalized)
|
|
873
|
+
return normalized
|
|
874
|
+
|
|
875
|
+
|
|
876
|
+
def _normalize_provenance_block(raw: Any) -> dict[str, Any]:
|
|
877
|
+
block = raw if isinstance(raw, dict) else {}
|
|
878
|
+
updates_obj = block.get("recent_updates")
|
|
879
|
+
updates: list[dict[str, str]] = []
|
|
880
|
+
if isinstance(updates_obj, list):
|
|
881
|
+
for entry in updates_obj:
|
|
882
|
+
if not isinstance(entry, dict):
|
|
883
|
+
continue
|
|
884
|
+
run_id = str(entry.get("run_id", "")).strip()
|
|
885
|
+
source = str(entry.get("source", "")).strip()
|
|
886
|
+
field = str(entry.get("field", "")).strip()
|
|
887
|
+
updated_at = str(entry.get("updated_at", "")).strip()
|
|
888
|
+
if not (run_id and source and field and updated_at):
|
|
889
|
+
continue
|
|
890
|
+
updates.append(
|
|
891
|
+
{
|
|
892
|
+
"run_id": run_id,
|
|
893
|
+
"source": source,
|
|
894
|
+
"field": field,
|
|
895
|
+
"updated_at": updated_at,
|
|
896
|
+
}
|
|
897
|
+
)
|
|
898
|
+
if len(updates) >= _PROFILE_RECENT_UPDATES_MAX:
|
|
899
|
+
break
|
|
900
|
+
return {"recent_updates": updates}
|
|
901
|
+
|
|
902
|
+
|
|
903
|
+
def _normalize_governed_preferences_block(raw: Any) -> dict[str, list[dict[str, Any]]]:
|
|
904
|
+
block = raw if isinstance(raw, dict) else {}
|
|
905
|
+
style_raw = block.get("style")
|
|
906
|
+
safety_raw = block.get("safety")
|
|
907
|
+
return {
|
|
908
|
+
"style": _normalize_governed_section_entries(style_raw, section="style"),
|
|
909
|
+
"safety": _normalize_governed_section_entries(safety_raw, section="safety"),
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
|
|
913
|
+
def _normalize_governed_section_entries(raw: Any, *, section: str) -> list[dict[str, Any]]:
|
|
914
|
+
if not isinstance(raw, list):
|
|
915
|
+
return []
|
|
916
|
+
entries: list[dict[str, Any]] = []
|
|
917
|
+
for item in raw:
|
|
918
|
+
if not isinstance(item, dict):
|
|
919
|
+
continue
|
|
920
|
+
field = str(item.get("field", "")).strip()
|
|
921
|
+
value = " ".join(str(item.get("value", "")).strip().split())
|
|
922
|
+
source = str(item.get("source", "")).strip()
|
|
923
|
+
learned_at = str(item.get("learned_at", "")).strip()
|
|
924
|
+
updated_at = str(item.get("updated_at", "")).strip()
|
|
925
|
+
candidate_section = str(item.get("section", section)).strip().lower()
|
|
926
|
+
confirmation_state = str(item.get("confirmation_state", "")).strip().lower()
|
|
927
|
+
|
|
928
|
+
if not (field and value and source and learned_at and updated_at):
|
|
929
|
+
continue
|
|
930
|
+
if candidate_section != section:
|
|
931
|
+
continue
|
|
932
|
+
if confirmation_state not in ("confirmed", "pending_confirmation", "inferred"):
|
|
933
|
+
continue
|
|
934
|
+
|
|
935
|
+
entry: dict[str, Any] = {
|
|
936
|
+
"field": field,
|
|
937
|
+
"value": value,
|
|
938
|
+
"source": source,
|
|
939
|
+
"learned_at": learned_at,
|
|
940
|
+
"updated_at": updated_at,
|
|
941
|
+
"section": section,
|
|
942
|
+
"confirmation_state": confirmation_state,
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
if section == "style" and confirmation_state == "inferred":
|
|
946
|
+
decay_raw = item.get("decay_metadata")
|
|
947
|
+
decay = decay_raw if isinstance(decay_raw, dict) else {}
|
|
948
|
+
try:
|
|
949
|
+
score = float(decay.get("decay_score", 0.0))
|
|
950
|
+
except (TypeError, ValueError):
|
|
951
|
+
score = 0.0
|
|
952
|
+
entry["decay_metadata"] = {
|
|
953
|
+
"decay_score": max(0.0, min(1.0, score)),
|
|
954
|
+
"last_seen_at": str(decay.get("last_seen_at", updated_at)).strip() or updated_at,
|
|
955
|
+
"decay_reason": str(decay.get("decay_reason", "inferred_signal")).strip() or "inferred_signal",
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
entries.append(entry)
|
|
959
|
+
if len(entries) >= _PROFILE_GOVERNED_MAX:
|
|
960
|
+
break
|
|
961
|
+
return entries
|
|
962
|
+
|
|
963
|
+
|
|
677
964
|
def _write_project_settings_preset(project_dir: str, preset: str) -> None:
|
|
678
965
|
"""Persist preset metadata into project settings when settings.json exists."""
|
|
679
966
|
settings_path = os.path.join(project_dir, "settings.json")
|
|
@@ -760,7 +1047,7 @@ def run_setup_wizard(
|
|
|
760
1047
|
report_path = write_adoption_report(project_dir, adoption)
|
|
761
1048
|
adoption["report_path"] = report_path
|
|
762
1049
|
|
|
763
|
-
|
|
1050
|
+
result: dict[str, Any] = {
|
|
764
1051
|
"status": "complete",
|
|
765
1052
|
"setup_mode": {
|
|
766
1053
|
"choices": get_mode_choices(),
|
|
@@ -773,3 +1060,48 @@ def run_setup_wizard(
|
|
|
773
1060
|
"preferences": prefs,
|
|
774
1061
|
"adoption": adoption,
|
|
775
1062
|
}
|
|
1063
|
+
|
|
1064
|
+
# --- Post-install validation (runs AFTER all setup writes are complete) ---
|
|
1065
|
+
try:
|
|
1066
|
+
validation_result = _run_post_install_validate(root_dir=Path(_PROJECT_ROOT))
|
|
1067
|
+
except Exception as exc:
|
|
1068
|
+
validation_result = {
|
|
1069
|
+
"schema": "ValidateResult",
|
|
1070
|
+
"status": "fail",
|
|
1071
|
+
"checks": [
|
|
1072
|
+
{
|
|
1073
|
+
"name": "validation_error",
|
|
1074
|
+
"status": "blocker",
|
|
1075
|
+
"message": str(exc),
|
|
1076
|
+
"required": True,
|
|
1077
|
+
},
|
|
1078
|
+
],
|
|
1079
|
+
"version": "",
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
# Persist machine-readable validation artifact
|
|
1083
|
+
artifact_dir = os.path.join(project_dir, ".omg", "state")
|
|
1084
|
+
os.makedirs(artifact_dir, exist_ok=True)
|
|
1085
|
+
artifact_path = os.path.join(artifact_dir, "post-install-validation.json")
|
|
1086
|
+
try:
|
|
1087
|
+
with open(artifact_path, "w", encoding="utf-8") as f:
|
|
1088
|
+
json.dump(validation_result, f, indent=2, ensure_ascii=True)
|
|
1089
|
+
f.write("\n")
|
|
1090
|
+
except OSError:
|
|
1091
|
+
artifact_path = ""
|
|
1092
|
+
|
|
1093
|
+
blockers = [
|
|
1094
|
+
c for c in validation_result.get("checks", [])
|
|
1095
|
+
if c.get("status") == "blocker"
|
|
1096
|
+
]
|
|
1097
|
+
|
|
1098
|
+
result["post_install_validation"] = {
|
|
1099
|
+
"status": validation_result.get("status", "fail"),
|
|
1100
|
+
"artifact_path": artifact_path,
|
|
1101
|
+
"blockers": [{"name": c["name"], "message": c["message"]} for c in blockers],
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
if blockers:
|
|
1105
|
+
result["status"] = "validation_failed"
|
|
1106
|
+
|
|
1107
|
+
return result
|
|
@@ -146,7 +146,13 @@ def map_shadow_path(project_dir: str, run_id: str, file_path: str) -> str:
|
|
|
146
146
|
return os.path.join(_shadow_root(project_dir), run_id, "overlay", rel)
|
|
147
147
|
|
|
148
148
|
|
|
149
|
-
def record_shadow_write(
|
|
149
|
+
def record_shadow_write(
|
|
150
|
+
project_dir: str,
|
|
151
|
+
run_id: str,
|
|
152
|
+
file_path: str,
|
|
153
|
+
source: str = "tool",
|
|
154
|
+
step_id: str | None = None,
|
|
155
|
+
) -> dict[str, Any]:
|
|
150
156
|
run_id = _validated_run_id(run_id)
|
|
151
157
|
run_dir = os.path.join(_shadow_root(project_dir), run_id)
|
|
152
158
|
os.makedirs(run_dir, exist_ok=True)
|
|
@@ -155,7 +161,8 @@ def record_shadow_write(project_dir: str, run_id: str, file_path: str, source: s
|
|
|
155
161
|
os.makedirs(os.path.dirname(shadow_path), exist_ok=True)
|
|
156
162
|
|
|
157
163
|
abs_file = file_path if os.path.isabs(file_path) else os.path.join(project_dir, file_path)
|
|
158
|
-
|
|
164
|
+
file_existed_before = os.path.exists(abs_file)
|
|
165
|
+
if file_existed_before:
|
|
159
166
|
shutil.copy2(abs_file, shadow_path)
|
|
160
167
|
file_hash = _hash_file(abs_file)
|
|
161
168
|
else:
|
|
@@ -169,7 +176,10 @@ def record_shadow_write(project_dir: str, run_id: str, file_path: str, source: s
|
|
|
169
176
|
"recorded_at": _utc_now(),
|
|
170
177
|
"source": source,
|
|
171
178
|
"sha256": file_hash,
|
|
179
|
+
"file_existed_before": file_existed_before,
|
|
172
180
|
}
|
|
181
|
+
if step_id:
|
|
182
|
+
entry["step_id"] = step_id
|
|
173
183
|
|
|
174
184
|
files = manifest.get("files", [])
|
|
175
185
|
# Replace existing entry for same file.
|
|
@@ -181,6 +191,72 @@ def record_shadow_write(project_dir: str, run_id: str, file_path: str, source: s
|
|
|
181
191
|
return entry
|
|
182
192
|
|
|
183
193
|
|
|
194
|
+
def restore_shadow_entry(project_dir: str, run_id: str, step_id: str) -> dict[str, Any]:
|
|
195
|
+
run_id = _validated_run_id(run_id)
|
|
196
|
+
run_dir = os.path.join(_shadow_root(project_dir), run_id)
|
|
197
|
+
manifest = _load_manifest(run_dir)
|
|
198
|
+
entries = manifest.get("files", [])
|
|
199
|
+
|
|
200
|
+
restored: list[str] = []
|
|
201
|
+
failed: list[dict[str, str]] = []
|
|
202
|
+
|
|
203
|
+
selected_entries: list[dict[str, Any]] = []
|
|
204
|
+
for item in entries:
|
|
205
|
+
if not isinstance(item, dict):
|
|
206
|
+
continue
|
|
207
|
+
item_step_id = str(item.get("step_id", "")).strip()
|
|
208
|
+
if item_step_id == step_id:
|
|
209
|
+
selected_entries.append(item)
|
|
210
|
+
|
|
211
|
+
if not selected_entries and isinstance(entries, list) and len(entries) == 1:
|
|
212
|
+
single = entries[0]
|
|
213
|
+
if isinstance(single, dict):
|
|
214
|
+
selected_entries = [single]
|
|
215
|
+
|
|
216
|
+
if not selected_entries:
|
|
217
|
+
return {"restored": restored, "failed": failed, "reason": "step not found"}
|
|
218
|
+
|
|
219
|
+
for item in selected_entries:
|
|
220
|
+
rel = str(item.get("file", "")).strip()
|
|
221
|
+
shadow_rel = str(item.get("shadow_file", "")).strip()
|
|
222
|
+
file_existed_before = bool(item.get("file_existed_before", True))
|
|
223
|
+
if not rel:
|
|
224
|
+
failed.append({"file": "", "reason": "missing file path"})
|
|
225
|
+
continue
|
|
226
|
+
|
|
227
|
+
dst = os.path.join(project_dir, rel)
|
|
228
|
+
src = os.path.join(run_dir, shadow_rel) if shadow_rel else ""
|
|
229
|
+
|
|
230
|
+
if src and os.path.exists(src):
|
|
231
|
+
try:
|
|
232
|
+
os.makedirs(os.path.dirname(dst), exist_ok=True)
|
|
233
|
+
shutil.copy2(src, dst)
|
|
234
|
+
restored.append(rel)
|
|
235
|
+
except Exception as exc:
|
|
236
|
+
failed.append({"file": rel, "reason": f"copy failed: {exc}"})
|
|
237
|
+
continue
|
|
238
|
+
|
|
239
|
+
if not file_existed_before:
|
|
240
|
+
try:
|
|
241
|
+
if os.path.exists(dst):
|
|
242
|
+
os.remove(dst)
|
|
243
|
+
restored.append(rel)
|
|
244
|
+
except Exception as exc:
|
|
245
|
+
failed.append({"file": rel, "reason": f"delete failed: {exc}"})
|
|
246
|
+
continue
|
|
247
|
+
|
|
248
|
+
failed.append({"file": rel, "reason": "shadow snapshot missing"})
|
|
249
|
+
|
|
250
|
+
manifest["status"] = "restored"
|
|
251
|
+
manifest["restored_at"] = _utc_now()
|
|
252
|
+
_save_manifest(run_dir, manifest)
|
|
253
|
+
|
|
254
|
+
reason = "shadow restore applied" if restored and not failed else "shadow restore partial"
|
|
255
|
+
if not restored and failed:
|
|
256
|
+
reason = "shadow restore failed"
|
|
257
|
+
return {"restored": restored, "failed": failed, "reason": reason}
|
|
258
|
+
|
|
259
|
+
|
|
184
260
|
def create_evidence_pack(
|
|
185
261
|
project_dir: str,
|
|
186
262
|
run_id: str,
|
|
@@ -321,6 +397,34 @@ def drop_shadow(project_dir: str, run_id: str) -> dict[str, Any]:
|
|
|
321
397
|
return {"run_id": run_id, "dropped": True}
|
|
322
398
|
|
|
323
399
|
|
|
400
|
+
def auto_journal_mutation(
|
|
401
|
+
project_dir: str,
|
|
402
|
+
tool: str,
|
|
403
|
+
file_path: str,
|
|
404
|
+
run_id: str,
|
|
405
|
+
) -> dict[str, Any] | None:
|
|
406
|
+
try:
|
|
407
|
+
from runtime.interaction_journal import InteractionJournal
|
|
408
|
+
from runtime.release_run_coordinator import resolve_current_run_id
|
|
409
|
+
except ImportError:
|
|
410
|
+
return None
|
|
411
|
+
|
|
412
|
+
canonical_run_id = resolve_current_run_id(project_dir) or run_id
|
|
413
|
+
shadow_manifest = os.path.join(
|
|
414
|
+
_shadow_root(project_dir), run_id, "manifest.json"
|
|
415
|
+
)
|
|
416
|
+
|
|
417
|
+
journal = InteractionJournal(project_dir)
|
|
418
|
+
return journal.record_step(
|
|
419
|
+
tool.lower(),
|
|
420
|
+
{
|
|
421
|
+
"file_path": file_path,
|
|
422
|
+
"run_id": canonical_run_id,
|
|
423
|
+
"shadow_manifest": shadow_manifest,
|
|
424
|
+
},
|
|
425
|
+
)
|
|
426
|
+
|
|
427
|
+
|
|
324
428
|
def _handle_post_tool_use(payload: dict[str, Any]) -> None:
|
|
325
429
|
tool = payload.get("tool_name", "")
|
|
326
430
|
if tool not in ("Write", "Edit", "MultiEdit"):
|
|
@@ -343,6 +447,11 @@ def _handle_post_tool_use(payload: dict[str, Any]) -> None:
|
|
|
343
447
|
run_id = begin_shadow_run(project_dir, metadata={"source": "post-tool-use"})
|
|
344
448
|
record_shadow_write(project_dir, run_id, file_path)
|
|
345
449
|
|
|
450
|
+
try:
|
|
451
|
+
auto_journal_mutation(project_dir, tool, file_path, run_id)
|
|
452
|
+
except Exception:
|
|
453
|
+
pass
|
|
454
|
+
|
|
346
455
|
|
|
347
456
|
def _main() -> int:
|
|
348
457
|
# Early-exit: skip all work if shadow/evidence mode is not enabled
|
|
@@ -9,10 +9,12 @@ import subprocess
|
|
|
9
9
|
import sys
|
|
10
10
|
import time
|
|
11
11
|
from datetime import datetime, timedelta, timezone
|
|
12
|
+
import warnings
|
|
12
13
|
|
|
13
14
|
sys.path.insert(0, os.path.dirname(__file__))
|
|
15
|
+
sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))
|
|
14
16
|
|
|
15
|
-
from _common import ( # noqa: E402
|
|
17
|
+
from hooks._common import ( # noqa: E402
|
|
16
18
|
atomic_json_write,
|
|
17
19
|
block_decision,
|
|
18
20
|
check_performance_budget,
|
|
@@ -27,7 +29,10 @@ from _common import ( # noqa: E402
|
|
|
27
29
|
should_skip_stop_hooks,
|
|
28
30
|
STOP_CHECK_MAX_MS,
|
|
29
31
|
)
|
|
30
|
-
from state_migration import resolve_state_file # noqa: E402
|
|
32
|
+
from hooks.state_migration import resolve_state_file # noqa: E402
|
|
33
|
+
|
|
34
|
+
from runtime.release_run_coordinator import resolve_current_run_id # noqa: E402
|
|
35
|
+
from runtime import test_intent_lock # noqa: E402
|
|
31
36
|
|
|
32
37
|
|
|
33
38
|
setup_crash_handler("stop_dispatcher")
|
|
@@ -132,7 +137,7 @@ def _is_internal_control_path(file_path: str) -> bool:
|
|
|
132
137
|
|
|
133
138
|
|
|
134
139
|
try:
|
|
135
|
-
from shadow_manager import has_recent_evidence # type: ignore
|
|
140
|
+
from hooks.shadow_manager import has_recent_evidence # type: ignore
|
|
136
141
|
except Exception: # intentional: optional feature — shadow_manager may not exist
|
|
137
142
|
has_recent_evidence = None
|
|
138
143
|
|
|
@@ -621,6 +626,104 @@ def check_bare_done(data, project_dir):
|
|
|
621
626
|
|
|
622
627
|
return []
|
|
623
628
|
|
|
629
|
+
|
|
630
|
+
def _proof_chain_strict_enabled() -> bool:
|
|
631
|
+
return os.environ.get("OMG_PROOF_CHAIN_STRICT", "0").strip() == "1"
|
|
632
|
+
|
|
633
|
+
|
|
634
|
+
def _load_test_delta_from_evidence(project_dir: str, run_id: str | None) -> dict[str, object]:
|
|
635
|
+
evidence_dir = os.path.join(project_dir, ".omg", "evidence")
|
|
636
|
+
if not os.path.isdir(evidence_dir):
|
|
637
|
+
return {}
|
|
638
|
+
|
|
639
|
+
candidates = sorted(
|
|
640
|
+
[
|
|
641
|
+
os.path.join(evidence_dir, name)
|
|
642
|
+
for name in os.listdir(evidence_dir)
|
|
643
|
+
if name.endswith(".json")
|
|
644
|
+
],
|
|
645
|
+
key=os.path.getmtime,
|
|
646
|
+
reverse=True,
|
|
647
|
+
)
|
|
648
|
+
for path in candidates:
|
|
649
|
+
try:
|
|
650
|
+
with open(path, "r", encoding="utf-8", errors="ignore") as handle:
|
|
651
|
+
payload = json.load(handle)
|
|
652
|
+
except (OSError, json.JSONDecodeError):
|
|
653
|
+
continue
|
|
654
|
+
if not isinstance(payload, dict):
|
|
655
|
+
continue
|
|
656
|
+
if payload.get("schema") != "EvidencePack":
|
|
657
|
+
continue
|
|
658
|
+
payload_run_id = str(payload.get("run_id", "")).strip()
|
|
659
|
+
if run_id and payload_run_id and payload_run_id != run_id:
|
|
660
|
+
continue
|
|
661
|
+
test_delta = payload.get("test_delta")
|
|
662
|
+
if isinstance(test_delta, dict):
|
|
663
|
+
return test_delta
|
|
664
|
+
return {}
|
|
665
|
+
|
|
666
|
+
|
|
667
|
+
def _has_waiver_artifact(delta_summary: dict[str, object]) -> bool:
|
|
668
|
+
waiver = delta_summary.get("waiver_artifact")
|
|
669
|
+
if isinstance(waiver, dict):
|
|
670
|
+
for field in ("artifact_path", "path", "id", "reason"):
|
|
671
|
+
if str(waiver.get(field, "")).strip():
|
|
672
|
+
return True
|
|
673
|
+
return False
|
|
674
|
+
|
|
675
|
+
|
|
676
|
+
def _has_weakened_or_drift(delta_summary: dict[str, object]) -> bool:
|
|
677
|
+
flags = delta_summary.get("flags")
|
|
678
|
+
if not isinstance(flags, list):
|
|
679
|
+
return False
|
|
680
|
+
risk_flags = {
|
|
681
|
+
"weakened_assertions",
|
|
682
|
+
"tests_mismatch",
|
|
683
|
+
"selector_drift",
|
|
684
|
+
"removed_touched_area_coverage",
|
|
685
|
+
"integration_to_mock_downgrade",
|
|
686
|
+
"snapshot_only_refresh",
|
|
687
|
+
}
|
|
688
|
+
normalized = {str(item).strip().lower() for item in flags if str(item).strip()}
|
|
689
|
+
return bool(normalized & risk_flags)
|
|
690
|
+
|
|
691
|
+
|
|
692
|
+
def check_tdd_proof_chain(data, project_dir):
|
|
693
|
+
if not get_feature_flag("tdd_proof_chain", True):
|
|
694
|
+
return []
|
|
695
|
+
|
|
696
|
+
context = data["_stop_ctx"]
|
|
697
|
+
if not context.get("has_source_writes", False):
|
|
698
|
+
return []
|
|
699
|
+
|
|
700
|
+
run_id = resolve_current_run_id(project_dir=project_dir)
|
|
701
|
+
lock_verdict = test_intent_lock.verify_lock(project_dir, run_id=run_id)
|
|
702
|
+
delta_summary = data.get("_test_delta") if isinstance(data.get("_test_delta"), dict) else {}
|
|
703
|
+
if not delta_summary:
|
|
704
|
+
delta_summary = _load_test_delta_from_evidence(project_dir, run_id)
|
|
705
|
+
|
|
706
|
+
lock_missing = str(lock_verdict.get("status", "")).strip() != "ok"
|
|
707
|
+
weakened_without_waiver = _has_weakened_or_drift(delta_summary) and not _has_waiver_artifact(delta_summary)
|
|
708
|
+
if not lock_missing and not weakened_without_waiver:
|
|
709
|
+
return []
|
|
710
|
+
|
|
711
|
+
strict_mode = _proof_chain_strict_enabled()
|
|
712
|
+
if strict_mode:
|
|
713
|
+
return [json.dumps({"status": "blocked", "reason": "tdd_proof_chain_incomplete"}, sort_keys=True)]
|
|
714
|
+
|
|
715
|
+
warnings.warn(
|
|
716
|
+
"tdd_proof_chain_incomplete_permissive",
|
|
717
|
+
RuntimeWarning,
|
|
718
|
+
stacklevel=2,
|
|
719
|
+
)
|
|
720
|
+
advisories = data.setdefault("_stop_advisories", [])
|
|
721
|
+
advisories.append(
|
|
722
|
+
"[OMG advisory] tdd proof chain incomplete: active lock evidence or waiver artifact is missing. "
|
|
723
|
+
"Set OMG_PROOF_CHAIN_STRICT=1 to hard-block completion."
|
|
724
|
+
)
|
|
725
|
+
return []
|
|
726
|
+
|
|
624
727
|
def check_simplifier(data, project_dir):
|
|
625
728
|
"""CHECK 7: Code simplifier — advisory only, never blocks."""
|
|
626
729
|
if not get_feature_flag("simplifier", True):
|
|
@@ -832,7 +935,7 @@ def check_scope_drift(project_dir):
|
|
|
832
935
|
|
|
833
936
|
|
|
834
937
|
|
|
835
|
-
def check_todo_continuation(data: dict) -> dict | None:
|
|
938
|
+
def check_todo_continuation(data: dict[str, object]) -> dict[str, str] | None:
|
|
836
939
|
"""Check if agent should continue due to incomplete todos.
|
|
837
940
|
Returns a dict with continuation response if idle, None otherwise.
|
|
838
941
|
Budget: STOP_CHECK_MAX_MS (15s)
|
|
@@ -911,6 +1014,7 @@ def main():
|
|
|
911
1014
|
check_diff_budget,
|
|
912
1015
|
check_recent_failures,
|
|
913
1016
|
check_test_execution,
|
|
1017
|
+
check_tdd_proof_chain,
|
|
914
1018
|
check_test_validator_coverage,
|
|
915
1019
|
check_false_fix,
|
|
916
1020
|
check_write_failures,
|