@trac3er/oh-my-god 2.0.4 → 2.0.5
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 +1 -1
- package/.agents/skills/omg/algorithms/SKILL.md +11 -0
- package/.agents/skills/omg/algorithms/openai.yaml +11 -0
- package/.agents/skills/omg/api-twin/SKILL.md +11 -0
- package/.agents/skills/omg/api-twin/openai.yaml +12 -0
- 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 +11 -0
- package/.agents/skills/omg/data-lineage/openai.yaml +12 -0
- package/.agents/skills/omg/delta-classifier/SKILL.md +11 -0
- package/.agents/skills/omg/delta-classifier/openai.yaml +12 -0
- package/.agents/skills/omg/eval-gate/SKILL.md +11 -0
- package/.agents/skills/omg/eval-gate/openai.yaml +12 -0
- package/.agents/skills/omg/health/SKILL.md +11 -0
- package/.agents/skills/omg/health/openai.yaml +11 -0
- 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 +11 -0
- package/.agents/skills/omg/incident-replay/openai.yaml +12 -0
- 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/preflight/SKILL.md +11 -0
- package/.agents/skills/omg/preflight/openai.yaml +12 -0
- package/.agents/skills/omg/remote-supervisor/SKILL.md +11 -0
- package/.agents/skills/omg/remote-supervisor/openai.yaml +12 -0
- package/.agents/skills/omg/robotics/SKILL.md +11 -0
- package/.agents/skills/omg/robotics/openai.yaml +11 -0
- 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 +11 -0
- package/.agents/skills/omg/security-check/openai.yaml +13 -0
- package/.agents/skills/omg/tracebank/SKILL.md +11 -0
- package/.agents/skills/omg/tracebank/openai.yaml +12 -0
- package/.agents/skills/omg/vision/SKILL.md +11 -0
- package/.agents/skills/omg/vision/openai.yaml +11 -0
- package/.claude-plugin/marketplace.json +3 -3
- package/.claude-plugin/plugin.json +1 -1
- package/.claude-plugin/scripts/uninstall.sh +2 -2
- package/OMG-setup.sh +1 -1
- package/OMG_COMPAT_CONTRACT.md +1 -1
- package/README.md +2 -2
- package/commands/__init__.py +1 -0
- package/control_plane/__init__.py +2 -0
- package/control_plane/openapi.yaml +228 -0
- package/control_plane/server.py +123 -0
- package/control_plane/service.py +185 -0
- package/dist/enterprise/bundle/.agents/skills/omg/algorithms/SKILL.md +11 -0
- package/dist/enterprise/bundle/.agents/skills/omg/algorithms/openai.yaml +11 -0
- package/dist/enterprise/bundle/.agents/skills/omg/api-twin/SKILL.md +11 -0
- package/dist/enterprise/bundle/.agents/skills/omg/api-twin/openai.yaml +12 -0
- package/dist/enterprise/bundle/.agents/skills/omg/data-lineage/SKILL.md +11 -0
- package/dist/enterprise/bundle/.agents/skills/omg/data-lineage/openai.yaml +12 -0
- package/dist/enterprise/bundle/.agents/skills/omg/delta-classifier/SKILL.md +11 -0
- package/dist/enterprise/bundle/.agents/skills/omg/delta-classifier/openai.yaml +12 -0
- package/dist/enterprise/bundle/.agents/skills/omg/eval-gate/SKILL.md +11 -0
- package/dist/enterprise/bundle/.agents/skills/omg/eval-gate/openai.yaml +12 -0
- package/dist/enterprise/bundle/.agents/skills/omg/health/SKILL.md +11 -0
- package/dist/enterprise/bundle/.agents/skills/omg/health/openai.yaml +11 -0
- package/dist/enterprise/bundle/.agents/skills/omg/incident-replay/SKILL.md +11 -0
- package/dist/enterprise/bundle/.agents/skills/omg/incident-replay/openai.yaml +12 -0
- package/dist/enterprise/bundle/.agents/skills/omg/preflight/SKILL.md +11 -0
- package/dist/enterprise/bundle/.agents/skills/omg/preflight/openai.yaml +12 -0
- package/dist/enterprise/bundle/.agents/skills/omg/remote-supervisor/SKILL.md +11 -0
- package/dist/enterprise/bundle/.agents/skills/omg/remote-supervisor/openai.yaml +12 -0
- package/dist/enterprise/bundle/.agents/skills/omg/robotics/SKILL.md +11 -0
- package/dist/enterprise/bundle/.agents/skills/omg/robotics/openai.yaml +11 -0
- package/dist/enterprise/bundle/.agents/skills/omg/security-check/SKILL.md +11 -0
- package/dist/enterprise/bundle/.agents/skills/omg/security-check/openai.yaml +13 -0
- package/dist/enterprise/bundle/.agents/skills/omg/tracebank/SKILL.md +11 -0
- package/dist/enterprise/bundle/.agents/skills/omg/tracebank/openai.yaml +12 -0
- package/dist/enterprise/bundle/.agents/skills/omg/vision/SKILL.md +11 -0
- package/dist/enterprise/bundle/.agents/skills/omg/vision/openai.yaml +11 -0
- 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/registry/bundles/algorithms.yaml +45 -0
- package/dist/enterprise/bundle/registry/bundles/api-twin.yaml +48 -0
- package/dist/enterprise/bundle/registry/bundles/control-plane.yaml +61 -0
- package/dist/enterprise/bundle/registry/bundles/data-lineage.yaml +47 -0
- package/dist/enterprise/bundle/registry/bundles/delta-classifier.yaml +47 -0
- package/dist/enterprise/bundle/registry/bundles/eval-gate.yaml +47 -0
- package/dist/enterprise/bundle/registry/bundles/health.yaml +45 -0
- package/dist/enterprise/bundle/registry/bundles/hook-governor.yaml +97 -0
- package/dist/enterprise/bundle/registry/bundles/incident-replay.yaml +47 -0
- package/dist/enterprise/bundle/registry/bundles/lsp-pack.yaml +48 -0
- package/dist/enterprise/bundle/registry/bundles/mcp-fabric.yaml +53 -0
- package/dist/enterprise/bundle/registry/bundles/preflight.yaml +48 -0
- package/dist/enterprise/bundle/registry/bundles/remote-supervisor.yaml +49 -0
- package/dist/enterprise/bundle/registry/bundles/robotics.yaml +45 -0
- package/dist/enterprise/bundle/registry/bundles/secure-worktree-pipeline.yaml +54 -0
- package/dist/enterprise/bundle/registry/bundles/security-check.yaml +50 -0
- package/dist/enterprise/bundle/registry/bundles/tracebank.yaml +47 -0
- package/dist/enterprise/bundle/registry/bundles/vision.yaml +45 -0
- package/dist/enterprise/bundle/registry/omg-capability.schema.json +80 -0
- package/dist/enterprise/bundle/settings.json +21 -6
- package/dist/enterprise/manifest.json +167 -11
- package/dist/public/bundle/.agents/skills/omg/algorithms/SKILL.md +11 -0
- package/dist/public/bundle/.agents/skills/omg/algorithms/openai.yaml +11 -0
- package/dist/public/bundle/.agents/skills/omg/api-twin/SKILL.md +11 -0
- package/dist/public/bundle/.agents/skills/omg/api-twin/openai.yaml +12 -0
- package/dist/public/bundle/.agents/skills/omg/data-lineage/SKILL.md +11 -0
- package/dist/public/bundle/.agents/skills/omg/data-lineage/openai.yaml +12 -0
- package/dist/public/bundle/.agents/skills/omg/delta-classifier/SKILL.md +11 -0
- package/dist/public/bundle/.agents/skills/omg/delta-classifier/openai.yaml +12 -0
- package/dist/public/bundle/.agents/skills/omg/eval-gate/SKILL.md +11 -0
- package/dist/public/bundle/.agents/skills/omg/eval-gate/openai.yaml +12 -0
- package/dist/public/bundle/.agents/skills/omg/health/SKILL.md +11 -0
- package/dist/public/bundle/.agents/skills/omg/health/openai.yaml +11 -0
- package/dist/public/bundle/.agents/skills/omg/incident-replay/SKILL.md +11 -0
- package/dist/public/bundle/.agents/skills/omg/incident-replay/openai.yaml +12 -0
- package/dist/public/bundle/.agents/skills/omg/preflight/SKILL.md +11 -0
- package/dist/public/bundle/.agents/skills/omg/preflight/openai.yaml +12 -0
- package/dist/public/bundle/.agents/skills/omg/remote-supervisor/SKILL.md +11 -0
- package/dist/public/bundle/.agents/skills/omg/remote-supervisor/openai.yaml +12 -0
- package/dist/public/bundle/.agents/skills/omg/robotics/SKILL.md +11 -0
- package/dist/public/bundle/.agents/skills/omg/robotics/openai.yaml +11 -0
- package/dist/public/bundle/.agents/skills/omg/security-check/SKILL.md +11 -0
- package/dist/public/bundle/.agents/skills/omg/security-check/openai.yaml +13 -0
- package/dist/public/bundle/.agents/skills/omg/tracebank/SKILL.md +11 -0
- package/dist/public/bundle/.agents/skills/omg/tracebank/openai.yaml +12 -0
- package/dist/public/bundle/.agents/skills/omg/vision/SKILL.md +11 -0
- package/dist/public/bundle/.agents/skills/omg/vision/openai.yaml +11 -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/registry/bundles/algorithms.yaml +45 -0
- package/dist/public/bundle/registry/bundles/api-twin.yaml +48 -0
- package/dist/public/bundle/registry/bundles/control-plane.yaml +61 -0
- package/dist/public/bundle/registry/bundles/data-lineage.yaml +47 -0
- package/dist/public/bundle/registry/bundles/delta-classifier.yaml +47 -0
- package/dist/public/bundle/registry/bundles/eval-gate.yaml +47 -0
- package/dist/public/bundle/registry/bundles/health.yaml +45 -0
- package/dist/public/bundle/registry/bundles/hook-governor.yaml +97 -0
- package/dist/public/bundle/registry/bundles/incident-replay.yaml +47 -0
- package/dist/public/bundle/registry/bundles/lsp-pack.yaml +48 -0
- package/dist/public/bundle/registry/bundles/mcp-fabric.yaml +53 -0
- package/dist/public/bundle/registry/bundles/preflight.yaml +48 -0
- package/dist/public/bundle/registry/bundles/remote-supervisor.yaml +49 -0
- package/dist/public/bundle/registry/bundles/robotics.yaml +45 -0
- package/dist/public/bundle/registry/bundles/secure-worktree-pipeline.yaml +54 -0
- package/dist/public/bundle/registry/bundles/security-check.yaml +50 -0
- package/dist/public/bundle/registry/bundles/tracebank.yaml +47 -0
- package/dist/public/bundle/registry/bundles/vision.yaml +45 -0
- package/dist/public/bundle/registry/omg-capability.schema.json +80 -0
- package/dist/public/bundle/settings.json +17 -4
- package/dist/public/manifest.json +167 -11
- package/docs/assets/omg-hud.svg +32 -0
- package/docs/install/claude-code.md +31 -0
- package/docs/install/codex.md +29 -0
- package/docs/migration/native-adoption.md +57 -0
- package/docs/proof.md +55 -0
- package/docs/release-checklist.md +38 -0
- package/docs/transcripts/crazy.md +17 -0
- package/docs/transcripts/setup.md +25 -0
- package/hooks/shadow_manager.py +6 -0
- package/package.json +1 -1
- package/plugins/__init__.py +1 -0
- package/plugins/core/plugin.json +1 -1
- package/pyproject.toml +38 -2
- package/registry/__init__.py +1 -0
- package/registry/bundles/algorithms.yaml +45 -0
- package/registry/bundles/api-twin.yaml +48 -0
- package/registry/bundles/control-plane.yaml +61 -0
- package/registry/bundles/data-lineage.yaml +47 -0
- package/registry/bundles/delta-classifier.yaml +47 -0
- package/registry/bundles/eval-gate.yaml +47 -0
- package/registry/bundles/health.yaml +45 -0
- package/registry/bundles/hook-governor.yaml +97 -0
- package/registry/bundles/incident-replay.yaml +47 -0
- package/registry/bundles/lsp-pack.yaml +48 -0
- package/registry/bundles/mcp-fabric.yaml +53 -0
- package/registry/bundles/preflight.yaml +48 -0
- package/registry/bundles/remote-supervisor.yaml +49 -0
- package/registry/bundles/robotics.yaml +45 -0
- package/registry/bundles/secure-worktree-pipeline.yaml +54 -0
- package/registry/bundles/security-check.yaml +50 -0
- package/registry/bundles/tracebank.yaml +47 -0
- package/registry/bundles/vision.yaml +45 -0
- package/registry/omg-capability.schema.json +80 -0
- package/registry/verify_artifact.py +90 -0
- package/runtime/adapters/claude.py +3 -0
- package/runtime/adapters/gpt.py +3 -0
- package/runtime/adapters/local.py +3 -0
- package/runtime/adoption.py +1 -1
- package/runtime/api_twin.py +60 -11
- package/runtime/asset_loader.py +62 -0
- package/runtime/compat.py +3 -2
- package/runtime/contract_compiler.py +171 -22
- package/runtime/data_lineage.py +73 -0
- package/runtime/delta_classifier.py +81 -0
- package/runtime/domain_packs.py +12 -0
- package/runtime/ecosystem.py +1 -1
- package/runtime/eval_gate.py +50 -0
- package/runtime/incident_replay.py +47 -0
- package/runtime/mcp_memory_server.py +1 -1
- package/runtime/omg_compat_contract_snapshot.json +1 -1
- package/runtime/omg_contract_snapshot.json +1 -1
- package/runtime/omg_mcp_server.py +3 -1
- package/runtime/preflight.py +22 -1
- package/runtime/remote_supervisor.py +64 -0
- package/runtime/security_check.py +119 -2
- package/runtime/tracebank.py +53 -0
- package/scripts/omg.py +187 -2
- package/settings.json +21 -6
|
@@ -7,10 +7,15 @@ import json
|
|
|
7
7
|
import os
|
|
8
8
|
from pathlib import Path
|
|
9
9
|
import shutil
|
|
10
|
+
import subprocess
|
|
11
|
+
import sys
|
|
12
|
+
import tempfile
|
|
10
13
|
from typing import Any, Iterable
|
|
14
|
+
import zipfile
|
|
11
15
|
|
|
12
16
|
import yaml
|
|
13
17
|
|
|
18
|
+
from runtime.asset_loader import resolve_asset, resolve_assets
|
|
14
19
|
from runtime.adoption import (
|
|
15
20
|
CANONICAL_MARKETPLACE_ID,
|
|
16
21
|
CANONICAL_PACKAGE_NAME,
|
|
@@ -31,6 +36,19 @@ DEFAULT_REQUIRED_BUNDLES = (
|
|
|
31
36
|
"mcp-fabric",
|
|
32
37
|
"lsp-pack",
|
|
33
38
|
"secure-worktree-pipeline",
|
|
39
|
+
"security-check",
|
|
40
|
+
"api-twin",
|
|
41
|
+
"preflight",
|
|
42
|
+
"robotics",
|
|
43
|
+
"vision",
|
|
44
|
+
"algorithms",
|
|
45
|
+
"health",
|
|
46
|
+
"tracebank",
|
|
47
|
+
"eval-gate",
|
|
48
|
+
"delta-classifier",
|
|
49
|
+
"incident-replay",
|
|
50
|
+
"data-lineage",
|
|
51
|
+
"remote-supervisor",
|
|
34
52
|
)
|
|
35
53
|
REQUIRED_DOC_TOKENS = (
|
|
36
54
|
"execution_contract",
|
|
@@ -96,24 +114,36 @@ def _sha256_file(path: Path) -> str:
|
|
|
96
114
|
|
|
97
115
|
|
|
98
116
|
def load_contract_doc(root_dir: str | Path | None = None) -> str:
|
|
99
|
-
|
|
100
|
-
|
|
117
|
+
if root_dir is not None:
|
|
118
|
+
root = _resolve_root(root_dir)
|
|
119
|
+
candidate = root / CONTRACT_DOC_PATH
|
|
120
|
+
if candidate.exists():
|
|
121
|
+
return candidate.read_text(encoding="utf-8")
|
|
122
|
+
return resolve_asset(CONTRACT_DOC_PATH).read_text(encoding="utf-8")
|
|
101
123
|
|
|
102
124
|
|
|
103
125
|
def load_contract_schema(root_dir: str | Path | None = None) -> dict[str, Any]:
|
|
104
|
-
|
|
105
|
-
|
|
126
|
+
if root_dir is not None:
|
|
127
|
+
root = _resolve_root(root_dir)
|
|
128
|
+
candidate = root / SCHEMA_PATH
|
|
129
|
+
if candidate.exists():
|
|
130
|
+
return _load_json(candidate)
|
|
131
|
+
return _load_json(resolve_asset(SCHEMA_PATH))
|
|
106
132
|
|
|
107
133
|
|
|
108
134
|
def load_contract_bundles(root_dir: str | Path | None = None) -> list[dict[str, Any]]:
|
|
109
135
|
root = _resolve_root(root_dir)
|
|
110
136
|
bundles: list[dict[str, Any]] = []
|
|
111
|
-
|
|
137
|
+
paths = sorted((root / BUNDLES_DIR).glob("*.yaml")) if (root / BUNDLES_DIR).exists() else resolve_assets(BUNDLES_DIR, suffix=".yaml")
|
|
138
|
+
for path in paths:
|
|
112
139
|
parsed = yaml.safe_load(path.read_text(encoding="utf-8"))
|
|
113
140
|
if not isinstance(parsed, dict):
|
|
114
141
|
raise ValueError(f"Expected mapping bundle manifest in {path}")
|
|
115
142
|
bundle = dict(parsed)
|
|
116
|
-
|
|
143
|
+
try:
|
|
144
|
+
bundle["_path"] = str(path.relative_to(root))
|
|
145
|
+
except ValueError:
|
|
146
|
+
bundle["_path"] = str(Path(BUNDLES_DIR) / path.name)
|
|
117
147
|
bundles.append(bundle)
|
|
118
148
|
return bundles
|
|
119
149
|
|
|
@@ -133,34 +163,30 @@ def validate_contract_registry(root_dir: str | Path | None = None) -> dict[str,
|
|
|
133
163
|
root = _resolve_root(root_dir)
|
|
134
164
|
errors: list[str] = []
|
|
135
165
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
if not doc_path.exists():
|
|
166
|
+
try:
|
|
167
|
+
doc_text = load_contract_doc(root)
|
|
168
|
+
except FileNotFoundError:
|
|
141
169
|
errors.append(f"missing contract doc: {CONTRACT_DOC_PATH}")
|
|
142
170
|
doc_text = ""
|
|
143
171
|
else:
|
|
144
|
-
doc_text = doc_path.read_text(encoding="utf-8")
|
|
145
172
|
for token in REQUIRED_DOC_TOKENS:
|
|
146
173
|
if token not in doc_text:
|
|
147
174
|
errors.append(f"contract doc missing token: {token}")
|
|
148
175
|
if CANONICAL_VERSION not in doc_text:
|
|
149
176
|
errors.append(f"contract doc missing version: {CANONICAL_VERSION}")
|
|
150
177
|
|
|
151
|
-
|
|
178
|
+
try:
|
|
179
|
+
schema_payload = load_contract_schema(root)
|
|
180
|
+
except FileNotFoundError:
|
|
152
181
|
errors.append(f"missing contract schema: {SCHEMA_PATH}")
|
|
153
182
|
schema_payload: dict[str, Any] = {}
|
|
154
183
|
else:
|
|
155
|
-
schema_payload = _load_json(schema_path)
|
|
156
184
|
if str(schema_payload.get("version", "")) != CANONICAL_VERSION:
|
|
157
185
|
errors.append(f"contract schema version drift: {schema_payload.get('version')!r}")
|
|
158
186
|
|
|
159
|
-
|
|
187
|
+
bundles = load_contract_bundles(root)
|
|
188
|
+
if not bundles:
|
|
160
189
|
errors.append(f"missing bundles directory: {BUNDLES_DIR}")
|
|
161
|
-
bundles: list[dict[str, Any]] = []
|
|
162
|
-
else:
|
|
163
|
-
bundles = load_contract_bundles(root)
|
|
164
190
|
|
|
165
191
|
bundle_ids = set()
|
|
166
192
|
bundle_summaries: list[dict[str, Any]] = []
|
|
@@ -208,14 +234,15 @@ def validate_contract_registry(root_dir: str | Path | None = None) -> dict[str,
|
|
|
208
234
|
def _copy_contract_inputs(root: Path, output_root: Path) -> list[Path]:
|
|
209
235
|
copied: list[Path] = []
|
|
210
236
|
for rel_path in [CONTRACT_DOC_PATH, SCHEMA_PATH]:
|
|
211
|
-
src =
|
|
237
|
+
src = resolve_asset(rel_path)
|
|
212
238
|
dst = output_root / rel_path
|
|
213
239
|
_write_text(dst, src.read_text(encoding="utf-8"))
|
|
214
240
|
copied.append(dst)
|
|
215
|
-
for
|
|
216
|
-
rel_path =
|
|
241
|
+
for bundle in load_contract_bundles(root):
|
|
242
|
+
rel_path = Path(str(bundle["_path"]))
|
|
243
|
+
src = resolve_asset(rel_path)
|
|
217
244
|
dst = output_root / rel_path
|
|
218
|
-
_write_text(dst,
|
|
245
|
+
_write_text(dst, src.read_text(encoding="utf-8"))
|
|
219
246
|
copied.append(dst)
|
|
220
247
|
return copied
|
|
221
248
|
|
|
@@ -368,6 +395,8 @@ def _compile_claude_outputs(
|
|
|
368
395
|
artifacts.append(output_root / ".mcp.json")
|
|
369
396
|
|
|
370
397
|
settings_path = root / "settings.json"
|
|
398
|
+
if not settings_path.exists():
|
|
399
|
+
settings_path = resolve_asset("settings.json")
|
|
371
400
|
settings = _load_json(settings_path)
|
|
372
401
|
hook_bundle = _bundle_map(bundles)["hook-governor"]
|
|
373
402
|
settings["hooks"] = _compile_hook_settings(hook_bundle)
|
|
@@ -673,6 +702,31 @@ def build_release_readiness(
|
|
|
673
702
|
blockers.append(f"missing compiled outputs: {', '.join(missing_outputs)}")
|
|
674
703
|
checks["compiled_outputs"] = {"missing": missing_outputs}
|
|
675
704
|
|
|
705
|
+
required_bundle_outputs: list[Path] = []
|
|
706
|
+
for bundle_id in DEFAULT_REQUIRED_BUNDLES:
|
|
707
|
+
required_bundle_outputs.extend(
|
|
708
|
+
[
|
|
709
|
+
output / ".agents" / "skills" / "omg" / bundle_id / "SKILL.md",
|
|
710
|
+
output / ".agents" / "skills" / "omg" / bundle_id / "openai.yaml",
|
|
711
|
+
]
|
|
712
|
+
)
|
|
713
|
+
missing_bundle_outputs = [str(path.relative_to(output)) for path in required_bundle_outputs if not path.exists()]
|
|
714
|
+
if missing_bundle_outputs:
|
|
715
|
+
blockers.append(f"missing bundle outputs: {', '.join(missing_bundle_outputs)}")
|
|
716
|
+
checks["bundle_outputs"] = {"missing": missing_bundle_outputs}
|
|
717
|
+
|
|
718
|
+
evidence_check = _check_recent_evidence(output)
|
|
719
|
+
checks["evidence"] = evidence_check
|
|
720
|
+
blockers.extend(evidence_check.get("blockers", []))
|
|
721
|
+
|
|
722
|
+
eval_check = _check_eval_gate(output)
|
|
723
|
+
checks["eval_gate"] = eval_check
|
|
724
|
+
blockers.extend(eval_check.get("blockers", []))
|
|
725
|
+
|
|
726
|
+
package_check = _check_packaged_install_smoke(root)
|
|
727
|
+
checks["package_smoke"] = package_check
|
|
728
|
+
blockers.extend(package_check.get("blockers", []))
|
|
729
|
+
|
|
676
730
|
providers = _provider_statuses()
|
|
677
731
|
checks["providers"] = providers
|
|
678
732
|
for provider_name, status in providers.items():
|
|
@@ -696,3 +750,98 @@ def build_release_readiness(
|
|
|
696
750
|
"blockers": blockers,
|
|
697
751
|
"checks": checks,
|
|
698
752
|
}
|
|
753
|
+
|
|
754
|
+
|
|
755
|
+
def _check_recent_evidence(output_root: Path) -> dict[str, Any]:
|
|
756
|
+
evidence_dir = output_root / ".omg" / "evidence"
|
|
757
|
+
if not evidence_dir.exists():
|
|
758
|
+
return {"status": "missing", "blockers": []}
|
|
759
|
+
|
|
760
|
+
evidence_files = sorted(path for path in evidence_dir.glob("*.json") if path.is_file())
|
|
761
|
+
if not evidence_files:
|
|
762
|
+
return {"status": "missing", "blockers": []}
|
|
763
|
+
|
|
764
|
+
evidence_payloads: list[tuple[Path, dict[str, Any]]] = []
|
|
765
|
+
for path in evidence_files:
|
|
766
|
+
try:
|
|
767
|
+
payload = _load_json(path)
|
|
768
|
+
except Exception:
|
|
769
|
+
continue
|
|
770
|
+
if payload.get("schema") == "EvidencePack":
|
|
771
|
+
evidence_payloads.append((path, payload))
|
|
772
|
+
|
|
773
|
+
if not evidence_payloads:
|
|
774
|
+
return {"status": "missing", "blockers": []}
|
|
775
|
+
|
|
776
|
+
evidence_path, payload = evidence_payloads[-1]
|
|
777
|
+
blockers: list[str] = []
|
|
778
|
+
if not payload.get("security_scans"):
|
|
779
|
+
blockers.append("cosmetic evidence: security_scans is empty")
|
|
780
|
+
if not payload.get("provenance"):
|
|
781
|
+
blockers.append("cosmetic evidence: provenance is empty")
|
|
782
|
+
if not payload.get("trace_ids"):
|
|
783
|
+
blockers.append("missing trace ids in evidence")
|
|
784
|
+
if not payload.get("lineage"):
|
|
785
|
+
blockers.append("missing lineage in evidence")
|
|
786
|
+
tests = payload.get("tests", [])
|
|
787
|
+
if isinstance(tests, list):
|
|
788
|
+
for item in tests:
|
|
789
|
+
if isinstance(item, dict) and item.get("name") == "worker_implementation" and not item.get("passed", False):
|
|
790
|
+
blockers.append("simulated worker evidence detected")
|
|
791
|
+
break
|
|
792
|
+
return {
|
|
793
|
+
"status": "ok" if not blockers else "error",
|
|
794
|
+
"evidence_file": str(evidence_path.relative_to(output_root)),
|
|
795
|
+
"blockers": blockers,
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
|
|
799
|
+
def _check_eval_gate(output_root: Path) -> dict[str, Any]:
|
|
800
|
+
latest_path = output_root / ".omg" / "evals" / "latest.json"
|
|
801
|
+
if not latest_path.exists():
|
|
802
|
+
return {"status": "missing", "blockers": []}
|
|
803
|
+
payload = _load_json(latest_path)
|
|
804
|
+
blockers: list[str] = []
|
|
805
|
+
if payload.get("status") != "ok" or bool(payload.get("summary", {}).get("regressed")):
|
|
806
|
+
blockers.append("eval regression detected")
|
|
807
|
+
return {
|
|
808
|
+
"status": "ok" if not blockers else "error",
|
|
809
|
+
"path": str(latest_path.relative_to(output_root)),
|
|
810
|
+
"blockers": blockers,
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
|
|
814
|
+
def _check_packaged_install_smoke(root: Path) -> dict[str, Any]:
|
|
815
|
+
blockers: list[str] = []
|
|
816
|
+
with tempfile.TemporaryDirectory(prefix="omg-wheel-") as tmp_dir:
|
|
817
|
+
proc = subprocess.run(
|
|
818
|
+
[sys.executable, "-m", "pip", "wheel", ".", "--no-deps", "-w", tmp_dir],
|
|
819
|
+
cwd=str(root),
|
|
820
|
+
capture_output=True,
|
|
821
|
+
text=True,
|
|
822
|
+
check=False,
|
|
823
|
+
timeout=120,
|
|
824
|
+
)
|
|
825
|
+
if proc.returncode != 0:
|
|
826
|
+
return {
|
|
827
|
+
"status": "error",
|
|
828
|
+
"blockers": ["package smoke failed to build wheel"],
|
|
829
|
+
"stdout": proc.stdout,
|
|
830
|
+
"stderr": proc.stderr,
|
|
831
|
+
}
|
|
832
|
+
wheels = sorted(Path(tmp_dir).glob("*.whl"))
|
|
833
|
+
if not wheels:
|
|
834
|
+
return {"status": "error", "blockers": ["package smoke did not produce a wheel"]}
|
|
835
|
+
with zipfile.ZipFile(wheels[-1]) as archive:
|
|
836
|
+
names = set(archive.namelist())
|
|
837
|
+
required_suffixes = (
|
|
838
|
+
"control_plane/service.py",
|
|
839
|
+
"registry/verify_artifact.py",
|
|
840
|
+
"plugins/dephealth/cve_scanner.py",
|
|
841
|
+
"OMG_COMPAT_CONTRACT.md",
|
|
842
|
+
".agents/skills/omg/security-check/SKILL.md",
|
|
843
|
+
)
|
|
844
|
+
for suffix in required_suffixes:
|
|
845
|
+
if not any(name.endswith(suffix) for name in names):
|
|
846
|
+
blockers.append(f"package parity missing {suffix}")
|
|
847
|
+
return {"status": "ok" if not blockers else "error", "blockers": blockers}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"""Lineage manifests for generated OMG artifacts."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from datetime import datetime, timezone
|
|
5
|
+
import json
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any
|
|
8
|
+
from uuid import uuid4
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _now() -> str:
|
|
12
|
+
return datetime.now(timezone.utc).isoformat()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def build_lineage_manifest(
|
|
16
|
+
project_dir: str,
|
|
17
|
+
*,
|
|
18
|
+
artifact_type: str,
|
|
19
|
+
sources: list[dict[str, Any]],
|
|
20
|
+
privacy: str,
|
|
21
|
+
license: str,
|
|
22
|
+
derivation: dict[str, Any],
|
|
23
|
+
trace_id: str | None = None,
|
|
24
|
+
) -> dict[str, Any]:
|
|
25
|
+
lineage_id = f"lineage-{uuid4().hex}"
|
|
26
|
+
payload = {
|
|
27
|
+
"schema": "DataLineageManifest",
|
|
28
|
+
"lineage_id": lineage_id,
|
|
29
|
+
"generated_at": _now(),
|
|
30
|
+
"artifact_type": artifact_type,
|
|
31
|
+
"sources": sources,
|
|
32
|
+
"privacy": privacy,
|
|
33
|
+
"license": license,
|
|
34
|
+
"derivation": derivation,
|
|
35
|
+
"trace_id": trace_id or "",
|
|
36
|
+
}
|
|
37
|
+
validation = validate_lineage_manifest(payload)
|
|
38
|
+
payload["status"] = validation["status"]
|
|
39
|
+
payload["errors"] = validation["errors"]
|
|
40
|
+
|
|
41
|
+
rel_path = Path(".omg") / "lineage" / f"{lineage_id}.json"
|
|
42
|
+
path = Path(project_dir) / rel_path
|
|
43
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
44
|
+
path.write_text(json.dumps(payload, indent=2, ensure_ascii=True) + "\n", encoding="utf-8")
|
|
45
|
+
payload["path"] = rel_path.as_posix()
|
|
46
|
+
return payload
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def validate_lineage_manifest(payload: dict[str, Any]) -> dict[str, Any]:
|
|
50
|
+
errors: list[str] = []
|
|
51
|
+
if not payload.get("artifact_type"):
|
|
52
|
+
errors.append("artifact_type is required")
|
|
53
|
+
if not payload.get("privacy"):
|
|
54
|
+
errors.append("privacy is required")
|
|
55
|
+
if not payload.get("license"):
|
|
56
|
+
errors.append("license is required")
|
|
57
|
+
sources = payload.get("sources", [])
|
|
58
|
+
if not isinstance(sources, list) or not sources:
|
|
59
|
+
errors.append("sources are required")
|
|
60
|
+
else:
|
|
61
|
+
for idx, source in enumerate(sources):
|
|
62
|
+
if not isinstance(source, dict):
|
|
63
|
+
errors.append(f"source {idx} must be an object")
|
|
64
|
+
continue
|
|
65
|
+
if not source.get("license"):
|
|
66
|
+
errors.append(f"source {idx} missing license")
|
|
67
|
+
if not source.get("path"):
|
|
68
|
+
errors.append(f"source {idx} missing path")
|
|
69
|
+
return {
|
|
70
|
+
"schema": "DataLineageValidationResult",
|
|
71
|
+
"status": "ok" if not errors else "error",
|
|
72
|
+
"errors": errors,
|
|
73
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""Repo-aware change classification for routing and policy attachment."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import json
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
import subprocess
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
_CATEGORY_RULES: dict[str, tuple[str, ...]] = {
|
|
11
|
+
"auth": ("auth", "token", "secret", "login", "credential"),
|
|
12
|
+
"payment": ("payment", "billing", "invoice", "stripe", "checkout"),
|
|
13
|
+
"db": ("migration", "schema", "database", "sql", "postgres", "mysql"),
|
|
14
|
+
"infra": ("terraform", ".tf", "deploy", "helm", "k8s", "docker"),
|
|
15
|
+
"api": ("openapi", "swagger", "postman", "endpoint", "api"),
|
|
16
|
+
"data": ("dataset", "lineage", "privacy", "warehouse", "etl"),
|
|
17
|
+
"compliance": ("gdpr", "hipaa", "pci", "soc2", "privacy"),
|
|
18
|
+
"robotics": ("robot", "actuator", "sensor", "simulator"),
|
|
19
|
+
"vision": ("vision", "image", "camera", "cv"),
|
|
20
|
+
"health": ("health", "patient", "clinical", "medical"),
|
|
21
|
+
"algorithms": ("algorithm", "benchmark", "determinism", "complexity"),
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def classify_project_changes(
|
|
26
|
+
project_dir: str,
|
|
27
|
+
*,
|
|
28
|
+
touched_files: list[str] | None = None,
|
|
29
|
+
goal: str = "",
|
|
30
|
+
) -> dict[str, Any]:
|
|
31
|
+
files = touched_files if touched_files is not None else _discover_files(project_dir)
|
|
32
|
+
manifest_names = sorted(path.name for path in Path(project_dir).glob("*") if path.is_file())
|
|
33
|
+
haystacks = [goal.lower(), *[file.lower() for file in files], *[name.lower() for name in manifest_names]]
|
|
34
|
+
|
|
35
|
+
categories = {
|
|
36
|
+
category
|
|
37
|
+
for category, tokens in _CATEGORY_RULES.items()
|
|
38
|
+
if any(token in haystack for token in tokens for haystack in haystacks)
|
|
39
|
+
}
|
|
40
|
+
if not categories:
|
|
41
|
+
categories.add("implementation")
|
|
42
|
+
|
|
43
|
+
result = {
|
|
44
|
+
"schema": "DeltaClassification",
|
|
45
|
+
"project_dir": project_dir,
|
|
46
|
+
"goal": goal,
|
|
47
|
+
"categories": sorted(categories),
|
|
48
|
+
"touched_files": files,
|
|
49
|
+
"manifests": manifest_names,
|
|
50
|
+
}
|
|
51
|
+
return result
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _discover_files(project_dir: str) -> list[str]:
|
|
55
|
+
root = Path(project_dir)
|
|
56
|
+
git_dir = root / ".git"
|
|
57
|
+
if git_dir.exists():
|
|
58
|
+
proc = subprocess.run(
|
|
59
|
+
["git", "diff", "--name-only", "HEAD"],
|
|
60
|
+
cwd=str(root),
|
|
61
|
+
capture_output=True,
|
|
62
|
+
text=True,
|
|
63
|
+
check=False,
|
|
64
|
+
timeout=10,
|
|
65
|
+
)
|
|
66
|
+
if proc.returncode == 0:
|
|
67
|
+
files = [line.strip() for line in proc.stdout.splitlines() if line.strip()]
|
|
68
|
+
if files:
|
|
69
|
+
return files
|
|
70
|
+
|
|
71
|
+
discovered: list[str] = []
|
|
72
|
+
for path in sorted(root.rglob("*")):
|
|
73
|
+
if not path.is_file():
|
|
74
|
+
continue
|
|
75
|
+
rel = path.relative_to(root).as_posix()
|
|
76
|
+
if rel.startswith(".omg/"):
|
|
77
|
+
continue
|
|
78
|
+
discovered.append(rel)
|
|
79
|
+
if len(discovered) >= 32:
|
|
80
|
+
break
|
|
81
|
+
return discovered
|
package/runtime/domain_packs.py
CHANGED
|
@@ -9,21 +9,33 @@ DOMAIN_PACKS: dict[str, dict[str, Any]] = {
|
|
|
9
9
|
"name": "robotics",
|
|
10
10
|
"required_approvals": ["actuation-approval"],
|
|
11
11
|
"required_evidence": ["simulator-replay", "kill-switch-check"],
|
|
12
|
+
"policy_modules": ["safe-actuation", "simulator-gate"],
|
|
13
|
+
"eval_hooks": ["robotics-sim"],
|
|
14
|
+
"replay_hooks": ["incident-replay"],
|
|
12
15
|
},
|
|
13
16
|
"vision": {
|
|
14
17
|
"name": "vision",
|
|
15
18
|
"required_approvals": [],
|
|
16
19
|
"required_evidence": ["dataset-provenance", "drift-check"],
|
|
20
|
+
"policy_modules": ["dataset-lineage", "drift-gate"],
|
|
21
|
+
"eval_hooks": ["vision-regression"],
|
|
22
|
+
"replay_hooks": ["incident-replay"],
|
|
17
23
|
},
|
|
18
24
|
"algorithms": {
|
|
19
25
|
"name": "algorithms",
|
|
20
26
|
"required_approvals": [],
|
|
21
27
|
"required_evidence": ["benchmark-harness", "determinism-check"],
|
|
28
|
+
"policy_modules": ["benchmark-gate", "determinism-gate"],
|
|
29
|
+
"eval_hooks": ["algorithm-benchmarks"],
|
|
30
|
+
"replay_hooks": ["incident-replay"],
|
|
22
31
|
},
|
|
23
32
|
"health": {
|
|
24
33
|
"name": "health",
|
|
25
34
|
"required_approvals": ["human-review"],
|
|
26
35
|
"required_evidence": ["audit-trail", "restricted-tools", "provenance"],
|
|
36
|
+
"policy_modules": ["human-review", "privacy-gate"],
|
|
37
|
+
"eval_hooks": ["health-safety"],
|
|
38
|
+
"replay_hooks": ["incident-replay"],
|
|
27
39
|
},
|
|
28
40
|
}
|
|
29
41
|
|
package/runtime/ecosystem.py
CHANGED
|
@@ -8,7 +8,7 @@ import subprocess
|
|
|
8
8
|
from typing import Any
|
|
9
9
|
|
|
10
10
|
ECOSYSTEM_SCHEMA = "OmgEcosystemCatalog"
|
|
11
|
-
ECOSYSTEM_CATALOG_VERSION = "
|
|
11
|
+
ECOSYSTEM_CATALOG_VERSION = "2.0.5"
|
|
12
12
|
ECOSYSTEM_LOCK_SCHEMA = "OmgEcosystemLock"
|
|
13
13
|
DEFAULT_ECOSYSTEM_REPO_DIR = ".omg/ecosystem/repos"
|
|
14
14
|
DEFAULT_ECOSYSTEM_LOCK_PATH = ".omg/state/ecosystem-lock.json"
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""Reproducible evaluation results for release gating."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from datetime import datetime, timezone
|
|
5
|
+
import json
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
EVAL_GATE_LATEST_REL_PATH = Path(".omg") / "evals" / "latest.json"
|
|
11
|
+
EVAL_GATE_HISTORY_REL_PATH = Path(".omg") / "evals" / "history.jsonl"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _now() -> str:
|
|
15
|
+
return datetime.now(timezone.utc).isoformat()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def evaluate_trace(
|
|
19
|
+
project_dir: str,
|
|
20
|
+
*,
|
|
21
|
+
trace_id: str,
|
|
22
|
+
suites: list[str],
|
|
23
|
+
metrics: dict[str, float],
|
|
24
|
+
regression_threshold: float = 0.95,
|
|
25
|
+
) -> dict[str, Any]:
|
|
26
|
+
scorecard = {name: float(metrics.get(name, 0.0)) for name in suites}
|
|
27
|
+
regressed = any(score < regression_threshold for score in scorecard.values())
|
|
28
|
+
result = {
|
|
29
|
+
"schema": "EvalGateResult",
|
|
30
|
+
"trace_id": trace_id,
|
|
31
|
+
"evaluated_at": _now(),
|
|
32
|
+
"status": "fail" if regressed else "ok",
|
|
33
|
+
"suites": suites,
|
|
34
|
+
"metrics": scorecard,
|
|
35
|
+
"summary": {
|
|
36
|
+
"regressed": regressed,
|
|
37
|
+
"regression_threshold": regression_threshold,
|
|
38
|
+
},
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
latest_path = Path(project_dir) / EVAL_GATE_LATEST_REL_PATH
|
|
42
|
+
latest_path.parent.mkdir(parents=True, exist_ok=True)
|
|
43
|
+
latest_path.write_text(json.dumps(result, indent=2, ensure_ascii=True) + "\n", encoding="utf-8")
|
|
44
|
+
|
|
45
|
+
history_path = Path(project_dir) / EVAL_GATE_HISTORY_REL_PATH
|
|
46
|
+
with history_path.open("a", encoding="utf-8") as handle:
|
|
47
|
+
handle.write(json.dumps(result, ensure_ascii=True) + "\n")
|
|
48
|
+
|
|
49
|
+
result["path"] = EVAL_GATE_LATEST_REL_PATH.as_posix()
|
|
50
|
+
return result
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""Replayable incident pack generation."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from datetime import datetime, timezone
|
|
5
|
+
import json
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any
|
|
8
|
+
from uuid import uuid4
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _now() -> str:
|
|
12
|
+
return datetime.now(timezone.utc).isoformat()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def build_incident_pack(
|
|
16
|
+
project_dir: str,
|
|
17
|
+
*,
|
|
18
|
+
title: str,
|
|
19
|
+
failing_tests: list[str],
|
|
20
|
+
logs: list[str],
|
|
21
|
+
diff_summary: dict[str, Any],
|
|
22
|
+
trace_id: str | None = None,
|
|
23
|
+
) -> dict[str, Any]:
|
|
24
|
+
incident_id = f"incident-{uuid4().hex}"
|
|
25
|
+
payload = {
|
|
26
|
+
"schema": "IncidentReplayPack",
|
|
27
|
+
"incident_id": incident_id,
|
|
28
|
+
"title": title,
|
|
29
|
+
"generated_at": _now(),
|
|
30
|
+
"trace_id": trace_id or "",
|
|
31
|
+
"failing_tests": failing_tests,
|
|
32
|
+
"logs": logs,
|
|
33
|
+
"diff_summary": diff_summary,
|
|
34
|
+
"reproduction_steps": [
|
|
35
|
+
"Replay the failing tests.",
|
|
36
|
+
"Inspect the attached logs.",
|
|
37
|
+
"Validate the diff summary before patching.",
|
|
38
|
+
],
|
|
39
|
+
"regression_guards": failing_tests,
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
rel_path = Path(".omg") / "incidents" / f"{incident_id}.json"
|
|
43
|
+
path = Path(project_dir) / rel_path
|
|
44
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
45
|
+
path.write_text(json.dumps(payload, indent=2, ensure_ascii=True) + "\n", encoding="utf-8")
|
|
46
|
+
payload["path"] = rel_path.as_posix()
|
|
47
|
+
return payload
|
|
@@ -66,7 +66,7 @@ mcp = FastMCP("OMG Memory Server", lifespan=lifespan)
|
|
|
66
66
|
|
|
67
67
|
@mcp.custom_route("/health", methods=["GET"])
|
|
68
68
|
async def health(_: Request) -> JSONResponse:
|
|
69
|
-
return JSONResponse({"status": "ok", "version": "
|
|
69
|
+
return JSONResponse({"status": "ok", "version": "2.0.5"})
|
|
70
70
|
|
|
71
71
|
|
|
72
72
|
@mcp.tool()
|
|
@@ -7,6 +7,8 @@ from dataclasses import dataclass
|
|
|
7
7
|
from pathlib import Path
|
|
8
8
|
from typing import Any
|
|
9
9
|
|
|
10
|
+
from runtime.asset_loader import resolve_asset
|
|
11
|
+
|
|
10
12
|
_MCP_IMPORT_ERROR: ModuleNotFoundError | None = None
|
|
11
13
|
|
|
12
14
|
try:
|
|
@@ -112,7 +114,7 @@ def _service() -> ControlPlaneService:
|
|
|
112
114
|
|
|
113
115
|
|
|
114
116
|
def _read_repo_text(rel_path: str) -> str:
|
|
115
|
-
return (
|
|
117
|
+
return resolve_asset(rel_path).read_text(encoding="utf-8")
|
|
116
118
|
|
|
117
119
|
|
|
118
120
|
@mcp.tool()
|
package/runtime/preflight.py
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"""Structured preflight routing for OMG."""
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
|
+
from runtime.delta_classifier import classify_project_changes
|
|
5
|
+
from runtime.tracebank import record_trace
|
|
4
6
|
from typing import Any
|
|
5
7
|
|
|
6
8
|
|
|
@@ -9,8 +11,15 @@ def run_preflight(project_dir: str, *, goal: str) -> dict[str, Any]:
|
|
|
9
11
|
task_class = "implementation"
|
|
10
12
|
risk_class = "medium"
|
|
11
13
|
route = "teams"
|
|
14
|
+
delta = classify_project_changes(project_dir, goal=goal)
|
|
15
|
+
categories = set(delta["categories"])
|
|
16
|
+
domain_packs = [category for category in delta["categories"] if category in {"robotics", "vision", "algorithms", "health"}]
|
|
12
17
|
|
|
13
|
-
if
|
|
18
|
+
if categories & {"auth", "payment", "health", "compliance"}:
|
|
19
|
+
task_class = "security"
|
|
20
|
+
risk_class = "high"
|
|
21
|
+
route = "security-check"
|
|
22
|
+
elif categories & {"api"} or any(token in lowered for token in ("openapi", "swagger", "postman", "contract", "fixture", "replay")):
|
|
14
23
|
task_class = "contract"
|
|
15
24
|
route = "api-twin"
|
|
16
25
|
elif any(token in lowered for token in ("auth", "secret", "security", "token", "injection")):
|
|
@@ -22,6 +31,15 @@ def run_preflight(project_dir: str, *, goal: str) -> dict[str, Any]:
|
|
|
22
31
|
risk_class = "high"
|
|
23
32
|
route = "crazy"
|
|
24
33
|
|
|
34
|
+
trace = record_trace(
|
|
35
|
+
project_dir,
|
|
36
|
+
trace_type="preflight",
|
|
37
|
+
route=route,
|
|
38
|
+
status="ok",
|
|
39
|
+
plan={"goal": goal, "delta_categories": delta["categories"]},
|
|
40
|
+
verify={"risk_class": risk_class},
|
|
41
|
+
)
|
|
42
|
+
|
|
25
43
|
return {
|
|
26
44
|
"schema": "PreflightResult",
|
|
27
45
|
"project_dir": project_dir,
|
|
@@ -33,6 +51,9 @@ def run_preflight(project_dir: str, *, goal: str) -> dict[str, Any]:
|
|
|
33
51
|
"required_mcps": ["omg-control"] if route in {"security-check", "api-twin", "crazy"} else [],
|
|
34
52
|
"missing_constraints": [],
|
|
35
53
|
"evidence_plan": _evidence_plan(route),
|
|
54
|
+
"delta_classification": delta,
|
|
55
|
+
"domain_packs": domain_packs,
|
|
56
|
+
"trace": {"trace_id": trace["trace_id"], "path": trace["path"]},
|
|
36
57
|
}
|
|
37
58
|
|
|
38
59
|
|