@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.
Files changed (206) hide show
  1. package/.agents/skills/omg/AGENTS.fragment.md +1 -1
  2. package/.agents/skills/omg/algorithms/SKILL.md +11 -0
  3. package/.agents/skills/omg/algorithms/openai.yaml +11 -0
  4. package/.agents/skills/omg/api-twin/SKILL.md +11 -0
  5. package/.agents/skills/omg/api-twin/openai.yaml +12 -0
  6. package/.agents/skills/omg/control-plane/SKILL.md +1 -1
  7. package/.agents/skills/omg/control-plane/openai.yaml +1 -1
  8. package/.agents/skills/omg/data-lineage/SKILL.md +11 -0
  9. package/.agents/skills/omg/data-lineage/openai.yaml +12 -0
  10. package/.agents/skills/omg/delta-classifier/SKILL.md +11 -0
  11. package/.agents/skills/omg/delta-classifier/openai.yaml +12 -0
  12. package/.agents/skills/omg/eval-gate/SKILL.md +11 -0
  13. package/.agents/skills/omg/eval-gate/openai.yaml +12 -0
  14. package/.agents/skills/omg/health/SKILL.md +11 -0
  15. package/.agents/skills/omg/health/openai.yaml +11 -0
  16. package/.agents/skills/omg/hook-governor/SKILL.md +1 -1
  17. package/.agents/skills/omg/hook-governor/openai.yaml +1 -1
  18. package/.agents/skills/omg/incident-replay/SKILL.md +11 -0
  19. package/.agents/skills/omg/incident-replay/openai.yaml +12 -0
  20. package/.agents/skills/omg/lsp-pack/SKILL.md +1 -1
  21. package/.agents/skills/omg/lsp-pack/openai.yaml +1 -1
  22. package/.agents/skills/omg/mcp-fabric/SKILL.md +1 -1
  23. package/.agents/skills/omg/mcp-fabric/openai.yaml +1 -1
  24. package/.agents/skills/omg/preflight/SKILL.md +11 -0
  25. package/.agents/skills/omg/preflight/openai.yaml +12 -0
  26. package/.agents/skills/omg/remote-supervisor/SKILL.md +11 -0
  27. package/.agents/skills/omg/remote-supervisor/openai.yaml +12 -0
  28. package/.agents/skills/omg/robotics/SKILL.md +11 -0
  29. package/.agents/skills/omg/robotics/openai.yaml +11 -0
  30. package/.agents/skills/omg/secure-worktree-pipeline/SKILL.md +1 -1
  31. package/.agents/skills/omg/secure-worktree-pipeline/openai.yaml +1 -1
  32. package/.agents/skills/omg/security-check/SKILL.md +11 -0
  33. package/.agents/skills/omg/security-check/openai.yaml +13 -0
  34. package/.agents/skills/omg/tracebank/SKILL.md +11 -0
  35. package/.agents/skills/omg/tracebank/openai.yaml +12 -0
  36. package/.agents/skills/omg/vision/SKILL.md +11 -0
  37. package/.agents/skills/omg/vision/openai.yaml +11 -0
  38. package/.claude-plugin/marketplace.json +3 -3
  39. package/.claude-plugin/plugin.json +1 -1
  40. package/.claude-plugin/scripts/uninstall.sh +2 -2
  41. package/OMG-setup.sh +1 -1
  42. package/OMG_COMPAT_CONTRACT.md +1 -1
  43. package/README.md +2 -2
  44. package/commands/__init__.py +1 -0
  45. package/control_plane/__init__.py +2 -0
  46. package/control_plane/openapi.yaml +228 -0
  47. package/control_plane/server.py +123 -0
  48. package/control_plane/service.py +185 -0
  49. package/dist/enterprise/bundle/.agents/skills/omg/algorithms/SKILL.md +11 -0
  50. package/dist/enterprise/bundle/.agents/skills/omg/algorithms/openai.yaml +11 -0
  51. package/dist/enterprise/bundle/.agents/skills/omg/api-twin/SKILL.md +11 -0
  52. package/dist/enterprise/bundle/.agents/skills/omg/api-twin/openai.yaml +12 -0
  53. package/dist/enterprise/bundle/.agents/skills/omg/data-lineage/SKILL.md +11 -0
  54. package/dist/enterprise/bundle/.agents/skills/omg/data-lineage/openai.yaml +12 -0
  55. package/dist/enterprise/bundle/.agents/skills/omg/delta-classifier/SKILL.md +11 -0
  56. package/dist/enterprise/bundle/.agents/skills/omg/delta-classifier/openai.yaml +12 -0
  57. package/dist/enterprise/bundle/.agents/skills/omg/eval-gate/SKILL.md +11 -0
  58. package/dist/enterprise/bundle/.agents/skills/omg/eval-gate/openai.yaml +12 -0
  59. package/dist/enterprise/bundle/.agents/skills/omg/health/SKILL.md +11 -0
  60. package/dist/enterprise/bundle/.agents/skills/omg/health/openai.yaml +11 -0
  61. package/dist/enterprise/bundle/.agents/skills/omg/incident-replay/SKILL.md +11 -0
  62. package/dist/enterprise/bundle/.agents/skills/omg/incident-replay/openai.yaml +12 -0
  63. package/dist/enterprise/bundle/.agents/skills/omg/preflight/SKILL.md +11 -0
  64. package/dist/enterprise/bundle/.agents/skills/omg/preflight/openai.yaml +12 -0
  65. package/dist/enterprise/bundle/.agents/skills/omg/remote-supervisor/SKILL.md +11 -0
  66. package/dist/enterprise/bundle/.agents/skills/omg/remote-supervisor/openai.yaml +12 -0
  67. package/dist/enterprise/bundle/.agents/skills/omg/robotics/SKILL.md +11 -0
  68. package/dist/enterprise/bundle/.agents/skills/omg/robotics/openai.yaml +11 -0
  69. package/dist/enterprise/bundle/.agents/skills/omg/security-check/SKILL.md +11 -0
  70. package/dist/enterprise/bundle/.agents/skills/omg/security-check/openai.yaml +13 -0
  71. package/dist/enterprise/bundle/.agents/skills/omg/tracebank/SKILL.md +11 -0
  72. package/dist/enterprise/bundle/.agents/skills/omg/tracebank/openai.yaml +12 -0
  73. package/dist/enterprise/bundle/.agents/skills/omg/vision/SKILL.md +11 -0
  74. package/dist/enterprise/bundle/.agents/skills/omg/vision/openai.yaml +11 -0
  75. package/dist/enterprise/bundle/.claude-plugin/marketplace.json +3 -3
  76. package/dist/enterprise/bundle/.claude-plugin/plugin.json +1 -1
  77. package/dist/enterprise/bundle/OMG_COMPAT_CONTRACT.md +1 -1
  78. package/dist/enterprise/bundle/registry/bundles/algorithms.yaml +45 -0
  79. package/dist/enterprise/bundle/registry/bundles/api-twin.yaml +48 -0
  80. package/dist/enterprise/bundle/registry/bundles/control-plane.yaml +61 -0
  81. package/dist/enterprise/bundle/registry/bundles/data-lineage.yaml +47 -0
  82. package/dist/enterprise/bundle/registry/bundles/delta-classifier.yaml +47 -0
  83. package/dist/enterprise/bundle/registry/bundles/eval-gate.yaml +47 -0
  84. package/dist/enterprise/bundle/registry/bundles/health.yaml +45 -0
  85. package/dist/enterprise/bundle/registry/bundles/hook-governor.yaml +97 -0
  86. package/dist/enterprise/bundle/registry/bundles/incident-replay.yaml +47 -0
  87. package/dist/enterprise/bundle/registry/bundles/lsp-pack.yaml +48 -0
  88. package/dist/enterprise/bundle/registry/bundles/mcp-fabric.yaml +53 -0
  89. package/dist/enterprise/bundle/registry/bundles/preflight.yaml +48 -0
  90. package/dist/enterprise/bundle/registry/bundles/remote-supervisor.yaml +49 -0
  91. package/dist/enterprise/bundle/registry/bundles/robotics.yaml +45 -0
  92. package/dist/enterprise/bundle/registry/bundles/secure-worktree-pipeline.yaml +54 -0
  93. package/dist/enterprise/bundle/registry/bundles/security-check.yaml +50 -0
  94. package/dist/enterprise/bundle/registry/bundles/tracebank.yaml +47 -0
  95. package/dist/enterprise/bundle/registry/bundles/vision.yaml +45 -0
  96. package/dist/enterprise/bundle/registry/omg-capability.schema.json +80 -0
  97. package/dist/enterprise/bundle/settings.json +21 -6
  98. package/dist/enterprise/manifest.json +167 -11
  99. package/dist/public/bundle/.agents/skills/omg/algorithms/SKILL.md +11 -0
  100. package/dist/public/bundle/.agents/skills/omg/algorithms/openai.yaml +11 -0
  101. package/dist/public/bundle/.agents/skills/omg/api-twin/SKILL.md +11 -0
  102. package/dist/public/bundle/.agents/skills/omg/api-twin/openai.yaml +12 -0
  103. package/dist/public/bundle/.agents/skills/omg/data-lineage/SKILL.md +11 -0
  104. package/dist/public/bundle/.agents/skills/omg/data-lineage/openai.yaml +12 -0
  105. package/dist/public/bundle/.agents/skills/omg/delta-classifier/SKILL.md +11 -0
  106. package/dist/public/bundle/.agents/skills/omg/delta-classifier/openai.yaml +12 -0
  107. package/dist/public/bundle/.agents/skills/omg/eval-gate/SKILL.md +11 -0
  108. package/dist/public/bundle/.agents/skills/omg/eval-gate/openai.yaml +12 -0
  109. package/dist/public/bundle/.agents/skills/omg/health/SKILL.md +11 -0
  110. package/dist/public/bundle/.agents/skills/omg/health/openai.yaml +11 -0
  111. package/dist/public/bundle/.agents/skills/omg/incident-replay/SKILL.md +11 -0
  112. package/dist/public/bundle/.agents/skills/omg/incident-replay/openai.yaml +12 -0
  113. package/dist/public/bundle/.agents/skills/omg/preflight/SKILL.md +11 -0
  114. package/dist/public/bundle/.agents/skills/omg/preflight/openai.yaml +12 -0
  115. package/dist/public/bundle/.agents/skills/omg/remote-supervisor/SKILL.md +11 -0
  116. package/dist/public/bundle/.agents/skills/omg/remote-supervisor/openai.yaml +12 -0
  117. package/dist/public/bundle/.agents/skills/omg/robotics/SKILL.md +11 -0
  118. package/dist/public/bundle/.agents/skills/omg/robotics/openai.yaml +11 -0
  119. package/dist/public/bundle/.agents/skills/omg/security-check/SKILL.md +11 -0
  120. package/dist/public/bundle/.agents/skills/omg/security-check/openai.yaml +13 -0
  121. package/dist/public/bundle/.agents/skills/omg/tracebank/SKILL.md +11 -0
  122. package/dist/public/bundle/.agents/skills/omg/tracebank/openai.yaml +12 -0
  123. package/dist/public/bundle/.agents/skills/omg/vision/SKILL.md +11 -0
  124. package/dist/public/bundle/.agents/skills/omg/vision/openai.yaml +11 -0
  125. package/dist/public/bundle/.claude-plugin/marketplace.json +3 -3
  126. package/dist/public/bundle/.claude-plugin/plugin.json +1 -1
  127. package/dist/public/bundle/OMG_COMPAT_CONTRACT.md +1 -1
  128. package/dist/public/bundle/registry/bundles/algorithms.yaml +45 -0
  129. package/dist/public/bundle/registry/bundles/api-twin.yaml +48 -0
  130. package/dist/public/bundle/registry/bundles/control-plane.yaml +61 -0
  131. package/dist/public/bundle/registry/bundles/data-lineage.yaml +47 -0
  132. package/dist/public/bundle/registry/bundles/delta-classifier.yaml +47 -0
  133. package/dist/public/bundle/registry/bundles/eval-gate.yaml +47 -0
  134. package/dist/public/bundle/registry/bundles/health.yaml +45 -0
  135. package/dist/public/bundle/registry/bundles/hook-governor.yaml +97 -0
  136. package/dist/public/bundle/registry/bundles/incident-replay.yaml +47 -0
  137. package/dist/public/bundle/registry/bundles/lsp-pack.yaml +48 -0
  138. package/dist/public/bundle/registry/bundles/mcp-fabric.yaml +53 -0
  139. package/dist/public/bundle/registry/bundles/preflight.yaml +48 -0
  140. package/dist/public/bundle/registry/bundles/remote-supervisor.yaml +49 -0
  141. package/dist/public/bundle/registry/bundles/robotics.yaml +45 -0
  142. package/dist/public/bundle/registry/bundles/secure-worktree-pipeline.yaml +54 -0
  143. package/dist/public/bundle/registry/bundles/security-check.yaml +50 -0
  144. package/dist/public/bundle/registry/bundles/tracebank.yaml +47 -0
  145. package/dist/public/bundle/registry/bundles/vision.yaml +45 -0
  146. package/dist/public/bundle/registry/omg-capability.schema.json +80 -0
  147. package/dist/public/bundle/settings.json +17 -4
  148. package/dist/public/manifest.json +167 -11
  149. package/docs/assets/omg-hud.svg +32 -0
  150. package/docs/install/claude-code.md +31 -0
  151. package/docs/install/codex.md +29 -0
  152. package/docs/migration/native-adoption.md +57 -0
  153. package/docs/proof.md +55 -0
  154. package/docs/release-checklist.md +38 -0
  155. package/docs/transcripts/crazy.md +17 -0
  156. package/docs/transcripts/setup.md +25 -0
  157. package/hooks/shadow_manager.py +6 -0
  158. package/package.json +1 -1
  159. package/plugins/__init__.py +1 -0
  160. package/plugins/core/plugin.json +1 -1
  161. package/pyproject.toml +38 -2
  162. package/registry/__init__.py +1 -0
  163. package/registry/bundles/algorithms.yaml +45 -0
  164. package/registry/bundles/api-twin.yaml +48 -0
  165. package/registry/bundles/control-plane.yaml +61 -0
  166. package/registry/bundles/data-lineage.yaml +47 -0
  167. package/registry/bundles/delta-classifier.yaml +47 -0
  168. package/registry/bundles/eval-gate.yaml +47 -0
  169. package/registry/bundles/health.yaml +45 -0
  170. package/registry/bundles/hook-governor.yaml +97 -0
  171. package/registry/bundles/incident-replay.yaml +47 -0
  172. package/registry/bundles/lsp-pack.yaml +48 -0
  173. package/registry/bundles/mcp-fabric.yaml +53 -0
  174. package/registry/bundles/preflight.yaml +48 -0
  175. package/registry/bundles/remote-supervisor.yaml +49 -0
  176. package/registry/bundles/robotics.yaml +45 -0
  177. package/registry/bundles/secure-worktree-pipeline.yaml +54 -0
  178. package/registry/bundles/security-check.yaml +50 -0
  179. package/registry/bundles/tracebank.yaml +47 -0
  180. package/registry/bundles/vision.yaml +45 -0
  181. package/registry/omg-capability.schema.json +80 -0
  182. package/registry/verify_artifact.py +90 -0
  183. package/runtime/adapters/claude.py +3 -0
  184. package/runtime/adapters/gpt.py +3 -0
  185. package/runtime/adapters/local.py +3 -0
  186. package/runtime/adoption.py +1 -1
  187. package/runtime/api_twin.py +60 -11
  188. package/runtime/asset_loader.py +62 -0
  189. package/runtime/compat.py +3 -2
  190. package/runtime/contract_compiler.py +171 -22
  191. package/runtime/data_lineage.py +73 -0
  192. package/runtime/delta_classifier.py +81 -0
  193. package/runtime/domain_packs.py +12 -0
  194. package/runtime/ecosystem.py +1 -1
  195. package/runtime/eval_gate.py +50 -0
  196. package/runtime/incident_replay.py +47 -0
  197. package/runtime/mcp_memory_server.py +1 -1
  198. package/runtime/omg_compat_contract_snapshot.json +1 -1
  199. package/runtime/omg_contract_snapshot.json +1 -1
  200. package/runtime/omg_mcp_server.py +3 -1
  201. package/runtime/preflight.py +22 -1
  202. package/runtime/remote_supervisor.py +64 -0
  203. package/runtime/security_check.py +119 -2
  204. package/runtime/tracebank.py +53 -0
  205. package/scripts/omg.py +187 -2
  206. package/settings.json +21 -6
@@ -0,0 +1,90 @@
1
+ """OMG v1 supply-chain verification with Warn-and-Run semantics."""
2
+ from __future__ import annotations
3
+
4
+ from dataclasses import dataclass
5
+ from typing import Any
6
+
7
+
8
+ @dataclass
9
+ class SupplyArtifact:
10
+ id: str
11
+ signer: str | None
12
+ checksum: str | None
13
+ permissions: list[str]
14
+ static_scan: list[dict[str, Any]]
15
+ risk_level: str = "low"
16
+
17
+
18
+ def _normalize(artifact: dict[str, Any]) -> SupplyArtifact:
19
+ return SupplyArtifact(
20
+ id=str(artifact.get("id", "unknown")),
21
+ signer=artifact.get("signer"),
22
+ checksum=artifact.get("checksum"),
23
+ permissions=[str(p) for p in artifact.get("permissions", [])],
24
+ static_scan=[f for f in artifact.get("static_scan", []) if isinstance(f, dict)],
25
+ risk_level=str(artifact.get("risk_level", "low")).lower(),
26
+ )
27
+
28
+
29
+ def verify_artifact(artifact: dict[str, Any], mode: str = "warn_and_run") -> dict[str, Any]:
30
+ a = _normalize(artifact)
31
+ reasons: list[str] = []
32
+ controls: list[str] = []
33
+
34
+ for finding in a.static_scan:
35
+ sev = str(finding.get("severity", "")).lower()
36
+ if sev == "critical":
37
+ reasons.append("critical static scan finding")
38
+ return {
39
+ "action": "deny",
40
+ "risk_level": "critical",
41
+ "reason": "; ".join(reasons),
42
+ "controls": ["block-execution"],
43
+ "trusted": False,
44
+ }
45
+
46
+ perm_blob = " ".join(a.permissions).lower()
47
+ if any(token in perm_blob for token in ["sudo", "rm -rf", "--privileged", "curl |", "wget |"]):
48
+ return {
49
+ "action": "deny",
50
+ "risk_level": "critical",
51
+ "reason": "critical permission profile",
52
+ "controls": ["block-execution"],
53
+ "trusted": False,
54
+ }
55
+
56
+ if not a.signer or not a.checksum:
57
+ reasons.append("missing signer/checksum")
58
+ controls.extend(["isolate-network", "read-only-fs", "manual-approval"])
59
+ if mode == "warn_and_run":
60
+ return {
61
+ "action": "ask",
62
+ "risk_level": "high",
63
+ "reason": "; ".join(reasons),
64
+ "controls": controls,
65
+ "trusted": False,
66
+ }
67
+ return {
68
+ "action": "deny",
69
+ "risk_level": "high",
70
+ "reason": "; ".join(reasons),
71
+ "controls": ["block-execution"],
72
+ "trusted": False,
73
+ }
74
+
75
+ if any(str(f.get("severity", "")).lower() == "high" for f in a.static_scan):
76
+ return {
77
+ "action": "ask",
78
+ "risk_level": "high",
79
+ "reason": "high severity findings present",
80
+ "controls": ["manual-approval"],
81
+ "trusted": False,
82
+ }
83
+
84
+ return {
85
+ "action": "allow",
86
+ "risk_level": a.risk_level if a.risk_level in {"low", "med", "high", "critical"} else "low",
87
+ "reason": "artifact verified",
88
+ "controls": [],
89
+ "trusted": True,
90
+ }
@@ -27,6 +27,7 @@ class ClaudeAdapter:
27
27
  "runtime": self.runtime,
28
28
  "phase": "execute",
29
29
  "status": "executed",
30
+ "worker_mode": "stub",
30
31
  "operations": [
31
32
  "apply-plan",
32
33
  "collect-diff",
@@ -43,6 +44,7 @@ class ClaudeAdapter:
43
44
  "checks": [
44
45
  {"name": "tests", "passed": True},
45
46
  {"name": "security", "passed": True},
47
+ {"name": "worker_implementation", "passed": False, "mode": "stub"},
46
48
  ],
47
49
  }
48
50
 
@@ -57,4 +59,5 @@ class ClaudeAdapter:
57
59
  "diff_summary": {},
58
60
  "reproducibility": {"seed": "deterministic"},
59
61
  "unresolved_risks": [],
62
+ "worker_mode": "stub",
60
63
  }
@@ -23,6 +23,7 @@ class GPTAdapter:
23
23
  "runtime": self.runtime,
24
24
  "phase": "execute",
25
25
  "status": "executed",
26
+ "worker_mode": "stub",
26
27
  "operations": ["apply-plan", "collect-diff"],
27
28
  "errors": [],
28
29
  }
@@ -36,6 +37,7 @@ class GPTAdapter:
36
37
  "checks": [
37
38
  {"name": "tests", "passed": True},
38
39
  {"name": "security", "passed": True},
40
+ {"name": "worker_implementation", "passed": False, "mode": "stub"},
39
41
  ],
40
42
  }
41
43
 
@@ -50,4 +52,5 @@ class GPTAdapter:
50
52
  "diff_summary": {},
51
53
  "reproducibility": {"seed": "deterministic"},
52
54
  "unresolved_risks": [],
55
+ "worker_mode": "stub",
53
56
  }
@@ -23,6 +23,7 @@ class LocalAdapter:
23
23
  "runtime": self.runtime,
24
24
  "phase": "execute",
25
25
  "status": "executed",
26
+ "worker_mode": "stub",
26
27
  "operations": ["apply-plan", "collect-diff"],
27
28
  "errors": [],
28
29
  }
@@ -36,6 +37,7 @@ class LocalAdapter:
36
37
  "checks": [
37
38
  {"name": "tests", "passed": True},
38
39
  {"name": "security", "passed": True},
40
+ {"name": "worker_implementation", "passed": False, "mode": "stub"},
39
41
  ],
40
42
  }
41
43
 
@@ -50,4 +52,5 @@ class LocalAdapter:
50
52
  "diff_summary": {},
51
53
  "reproducibility": {"seed": "deterministic"},
52
54
  "unresolved_risks": [],
55
+ "worker_mode": "stub",
53
56
  }
@@ -11,7 +11,7 @@ CANONICAL_REPO_URL = "https://github.com/trac3er00/OMG"
11
11
  CANONICAL_PACKAGE_NAME = "@trac3er/oh-my-god"
12
12
  CANONICAL_PLUGIN_ID = "omg"
13
13
  CANONICAL_MARKETPLACE_ID = "omg"
14
- CANONICAL_VERSION = "2.0.4"
14
+ CANONICAL_VERSION = "2.0.5"
15
15
 
16
16
  VALID_ADOPTION_MODES = ("omg-only", "coexist")
17
17
  VALID_PRESETS = ("safe", "balanced", "interop", "labs")
@@ -3,6 +3,7 @@ from __future__ import annotations
3
3
 
4
4
  import json
5
5
  from pathlib import Path
6
+ import time
6
7
  from typing import Any
7
8
 
8
9
 
@@ -32,23 +33,32 @@ def record_fixture(
32
33
  project_dir: str,
33
34
  *,
34
35
  name: str,
36
+ endpoint: str = "default",
37
+ cassette_version: str = "v1",
35
38
  request: dict[str, Any],
36
39
  response: dict[str, Any],
37
40
  validated: bool,
41
+ redactions: dict[str, str] | None = None,
38
42
  ) -> dict[str, Any]:
39
43
  state = _load_state(project_dir)
40
44
  fixture = {
41
45
  "name": name,
46
+ "endpoint": endpoint,
47
+ "cassette_version": cassette_version,
42
48
  "request": request,
43
49
  "response": response,
44
50
  "fidelity": "recorded-validated" if validated else "recorded",
45
51
  "validated": validated,
52
+ "redactions": redactions or {},
46
53
  }
47
- state.setdefault("fixtures", {})[name] = fixture
54
+ fixtures = state.setdefault("fixtures", {}).setdefault(name, {})
55
+ fixtures[_fixture_key(endpoint, cassette_version)] = fixture
48
56
  _save_state(project_dir, state)
49
57
  return {
50
58
  "schema": "ApiTwinFixture",
51
59
  "name": name,
60
+ "endpoint": endpoint,
61
+ "cassette_version": cassette_version,
52
62
  "fidelity": fixture["fidelity"],
53
63
  }
54
64
 
@@ -57,14 +67,18 @@ def serve_fixture(
57
67
  project_dir: str,
58
68
  *,
59
69
  name: str,
70
+ endpoint: str = "default",
71
+ cassette_version: str = "v1",
60
72
  latency_ms: int = 0,
61
73
  failure_mode: str = "",
62
74
  schema_drift: bool = False,
63
75
  ) -> dict[str, Any]:
64
76
  state = _load_state(project_dir)
65
- fixture = _fixture(state, name)
77
+ fixture = _fixture(state, name, endpoint=endpoint, cassette_version=cassette_version)
66
78
  response = dict(fixture.get("response", {}))
67
79
  fidelity = str(fixture.get("fidelity", "recorded"))
80
+ if latency_ms > 0:
81
+ time.sleep(max(latency_ms, 0) / 1000)
68
82
  if schema_drift:
69
83
  response["__schema_drift__"] = True
70
84
  fidelity = "stale"
@@ -74,25 +88,41 @@ def serve_fixture(
74
88
  return {
75
89
  "schema": "ApiTwinServeResult",
76
90
  "name": name,
91
+ "endpoint": endpoint,
92
+ "cassette_version": cassette_version,
77
93
  "latency_ms": latency_ms,
78
94
  "failure_mode": failure_mode,
79
95
  "fidelity": fidelity,
80
96
  "response": response,
97
+ "report": {
98
+ "saved_live_calls": 1,
99
+ "saved_cost_estimate": round(len(json.dumps(response)) * 0.0001, 4),
100
+ "redactions": fixture.get("redactions", {}),
101
+ },
81
102
  }
82
103
 
83
104
 
84
- def verify_fixture(project_dir: str, *, name: str, live_response: dict[str, Any]) -> dict[str, Any]:
105
+ def verify_fixture(
106
+ project_dir: str,
107
+ *,
108
+ name: str,
109
+ endpoint: str = "default",
110
+ cassette_version: str = "v1",
111
+ live_response: dict[str, Any],
112
+ ) -> dict[str, Any]:
85
113
  state = _load_state(project_dir)
86
- fixture = _fixture(state, name)
114
+ fixture = _fixture(state, name, endpoint=endpoint, cassette_version=cassette_version)
87
115
  matches_live = fixture.get("response", {}) == live_response
88
116
  fidelity = "recorded-validated" if matches_live else "stale"
89
117
  fixture["fidelity"] = fidelity
90
118
  fixture["validated"] = matches_live
91
- state.setdefault("fixtures", {})[name] = fixture
119
+ state.setdefault("fixtures", {}).setdefault(name, {})[_fixture_key(endpoint, cassette_version)] = fixture
92
120
  _save_state(project_dir, state)
93
121
  return {
94
122
  "schema": "ApiTwinVerifyResult",
95
123
  "name": name,
124
+ "endpoint": endpoint,
125
+ "cassette_version": cassette_version,
96
126
  "fidelity": fidelity,
97
127
  "matches_live": matches_live,
98
128
  "live_verification_required": True,
@@ -106,12 +136,18 @@ def _state_path(project_dir: str) -> Path:
106
136
  def _load_state(project_dir: str) -> dict[str, Any]:
107
137
  path = _state_path(project_dir)
108
138
  if not path.exists():
109
- return {"contracts": {}, "fixtures": {}}
139
+ return {"schema": "ApiTwinState", "version": "2.0.5", "contracts": {}, "fixtures": {}}
110
140
  try:
111
141
  payload = json.loads(path.read_text(encoding="utf-8"))
112
142
  except (OSError, json.JSONDecodeError):
113
- return {"contracts": {}, "fixtures": {}}
114
- return payload if isinstance(payload, dict) else {"contracts": {}, "fixtures": {}}
143
+ return {"schema": "ApiTwinState", "version": "2.0.5", "contracts": {}, "fixtures": {}}
144
+ if not isinstance(payload, dict):
145
+ return {"schema": "ApiTwinState", "version": "2.0.5", "contracts": {}, "fixtures": {}}
146
+ payload.setdefault("schema", "ApiTwinState")
147
+ payload.setdefault("version", "2.0.5")
148
+ payload.setdefault("contracts", {})
149
+ payload.setdefault("fixtures", {})
150
+ return payload
115
151
 
116
152
 
117
153
  def _save_state(project_dir: str, payload: dict[str, Any]) -> None:
@@ -120,11 +156,24 @@ def _save_state(project_dir: str, payload: dict[str, Any]) -> None:
120
156
  path.write_text(json.dumps(payload, indent=2, ensure_ascii=True) + "\n", encoding="utf-8")
121
157
 
122
158
 
123
- def _fixture(state: dict[str, Any], name: str) -> dict[str, Any]:
159
+ def _fixture_key(endpoint: str, cassette_version: str) -> str:
160
+ return f"{endpoint}::{cassette_version}"
161
+
162
+
163
+ def _fixture(
164
+ state: dict[str, Any],
165
+ name: str,
166
+ *,
167
+ endpoint: str = "default",
168
+ cassette_version: str = "v1",
169
+ ) -> dict[str, Any]:
124
170
  fixtures = state.get("fixtures", {})
125
171
  if not isinstance(fixtures, dict) or name not in fixtures:
126
172
  raise ValueError(f"Unknown API twin fixture: {name}")
127
- fixture = fixtures[name]
128
- if not isinstance(fixture, dict):
173
+ fixture_group = fixtures[name]
174
+ if not isinstance(fixture_group, dict):
129
175
  raise ValueError(f"Invalid API twin fixture: {name}")
176
+ fixture = fixture_group.get(_fixture_key(endpoint, cassette_version))
177
+ if not isinstance(fixture, dict):
178
+ raise ValueError(f"Unknown API twin cassette: {name} {endpoint} {cassette_version}")
130
179
  return fixture
@@ -0,0 +1,62 @@
1
+ """Helpers for locating packaged OMG assets from source checkouts or installs."""
2
+ from __future__ import annotations
3
+
4
+ from importlib import metadata
5
+ from pathlib import Path
6
+
7
+
8
+ _DIST_NAMES = ("oh-my-god", "oh_my_god")
9
+
10
+
11
+ def project_root() -> Path:
12
+ return Path(__file__).resolve().parents[1]
13
+
14
+
15
+ def resolve_asset(rel_path: str | Path) -> Path:
16
+ rel = Path(rel_path)
17
+ candidate = project_root() / rel
18
+ if candidate.exists():
19
+ return candidate
20
+
21
+ rel_posix = rel.as_posix()
22
+ for dist_name in _DIST_NAMES:
23
+ try:
24
+ dist = metadata.distribution(dist_name)
25
+ except metadata.PackageNotFoundError:
26
+ continue
27
+ for file in dist.files or []:
28
+ if str(file).endswith(rel_posix):
29
+ located = Path(dist.locate_file(file))
30
+ if located.exists():
31
+ return located
32
+
33
+ raise FileNotFoundError(f"Unable to resolve packaged OMG asset: {rel_posix}")
34
+
35
+
36
+ def resolve_assets(prefix: str | Path, suffix: str = "") -> list[Path]:
37
+ prefix_path = Path(prefix)
38
+ root_candidate = project_root() / prefix_path
39
+ if root_candidate.exists():
40
+ if root_candidate.is_dir():
41
+ if suffix:
42
+ return sorted(path for path in root_candidate.rglob("*") if path.is_file() and path.name.endswith(suffix))
43
+ return sorted(path for path in root_candidate.rglob("*") if path.is_file())
44
+ return [root_candidate]
45
+
46
+ prefix_posix = prefix_path.as_posix().rstrip("/")
47
+ matched: list[Path] = []
48
+ for dist_name in _DIST_NAMES:
49
+ try:
50
+ dist = metadata.distribution(dist_name)
51
+ except metadata.PackageNotFoundError:
52
+ continue
53
+ for file in dist.files or []:
54
+ file_text = str(file)
55
+ if not file_text.startswith(prefix_posix):
56
+ continue
57
+ if suffix and not file_text.endswith(suffix):
58
+ continue
59
+ located = Path(dist.locate_file(file))
60
+ if located.exists() and located.is_file():
61
+ matched.append(located)
62
+ return sorted(matched)
package/runtime/compat.py CHANGED
@@ -15,13 +15,14 @@ from typing import Any
15
15
 
16
16
  from hooks.policy_engine import evaluate_bash_command
17
17
  from lab.pipeline import run_pipeline
18
+ from runtime.adoption import CANONICAL_VERSION
18
19
  from runtime.dispatcher import dispatch_runtime
19
20
  from runtime.security_check import run_security_check
20
21
  from runtime.team_router import TeamDispatchRequest, dispatch_team
21
22
 
22
23
  CONTRACT_SNAPSHOT_SCHEMA = "OmgCompatContractSnapshot"
23
24
  LEGACY_CONTRACT_SNAPSHOT_SCHEMA = "OmgCompatContractSnapshot"
24
- CONTRACT_SNAPSHOT_VERSION = "1.0.0"
25
+ CONTRACT_SNAPSHOT_VERSION = CANONICAL_VERSION
25
26
  LEGACY_SNAPSHOT_VERSION = "0.9.0"
26
27
  GAP_REPORT_SCHEMA = "OmgCompatGapReport"
27
28
  LEGACY_GAP_REPORT_SCHEMA = "OmgCompatGapReport"
@@ -359,7 +360,7 @@ def migrate_contract_snapshot_payload(payload: dict[str, Any]) -> tuple[dict[str
359
360
  # v0.9.0 lacked explicit schema/version constraints.
360
361
  migrated["schema"] = CONTRACT_SNAPSHOT_SCHEMA
361
362
  migrated["contract_version"] = CONTRACT_SNAPSHOT_VERSION
362
- migrations.append("migrate-0.9.0-to-1.0.0")
363
+ migrations.append(f"migrate-0.9.0-to-{CONTRACT_SNAPSHOT_VERSION}")
363
364
 
364
365
  return migrated, migrations
365
366