sopvm 0.2.0__tar.gz

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 (133) hide show
  1. sopvm-0.2.0/.api-baseline.json +22 -0
  2. sopvm-0.2.0/.coverage +0 -0
  3. sopvm-0.2.0/.github/workflows/adversarial.yml +31 -0
  4. sopvm-0.2.0/.github/workflows/release.yml +50 -0
  5. sopvm-0.2.0/.mimocode/.cron-lock +1 -0
  6. sopvm-0.2.0/.pre-commit-hooks.yaml +7 -0
  7. sopvm-0.2.0/ARCHITECTURE.md +81 -0
  8. sopvm-0.2.0/CHANGELOG.md +55 -0
  9. sopvm-0.2.0/CONTRIBUTING.md +83 -0
  10. sopvm-0.2.0/INTERFACES.md +231 -0
  11. sopvm-0.2.0/PKG-INFO +115 -0
  12. sopvm-0.2.0/README.md +96 -0
  13. sopvm-0.2.0/VERSIONING_POLICY.md +49 -0
  14. sopvm-0.2.0/examples/adversarial_walkthrough.md +121 -0
  15. sopvm-0.2.0/examples/compile-deny.ir.json +1 -0
  16. sopvm-0.2.0/examples/compiled-refund.ir.json +67 -0
  17. sopvm-0.2.0/examples/langgraph_integration.py +47 -0
  18. sopvm-0.2.0/examples/providers/mock_db.py +34 -0
  19. sopvm-0.2.0/examples/providers/mock_payments.py +25 -0
  20. sopvm-0.2.0/examples/sops/data-export.sop.yaml +42 -0
  21. sopvm-0.2.0/examples/sops/incident-response.sop.yaml +54 -0
  22. sopvm-0.2.0/examples/violating.ir.json +1 -0
  23. sopvm-0.2.0/policies/data-export.policy.yaml +9 -0
  24. sopvm-0.2.0/policies/incident-response.policy.yaml +14 -0
  25. sopvm-0.2.0/policies/support-agent.policy.yaml +7 -0
  26. sopvm-0.2.0/pyproject.toml +40 -0
  27. sopvm-0.2.0/schemas/capability.schema.json +25 -0
  28. sopvm-0.2.0/schemas/sop.schema.json +78 -0
  29. sopvm-0.2.0/scripts/check_api_compat.py +147 -0
  30. sopvm-0.2.0/sopvm-milestone-1/sopvm/.github/workflows/ci.yml +30 -0
  31. sopvm-0.2.0/sopvm-milestone-1/sopvm/README.md +124 -0
  32. sopvm-0.2.0/sopvm-milestone-1/sopvm/cli/__init__.py +1 -0
  33. sopvm-0.2.0/sopvm-milestone-1/sopvm/cli/main.py +92 -0
  34. sopvm-0.2.0/sopvm-milestone-1/sopvm/docs/architecture.md +432 -0
  35. sopvm-0.2.0/sopvm-milestone-1/sopvm/examples/hello-sop/hello.md +10 -0
  36. sopvm-0.2.0/sopvm-milestone-1/sopvm/pyproject.toml +30 -0
  37. sopvm-0.2.0/sopvm-milestone-1/sopvm/sopvm/__init__.py +10 -0
  38. sopvm-0.2.0/sopvm-milestone-1/sopvm/sopvm/api.py +37 -0
  39. sopvm-0.2.0/sopvm-milestone-1/sopvm/sopvm/compiler/__init__.py +11 -0
  40. sopvm-0.2.0/sopvm-milestone-1/sopvm/sopvm/compiler/ast.py +80 -0
  41. sopvm-0.2.0/sopvm-milestone-1/sopvm/sopvm/compiler/errors.py +56 -0
  42. sopvm-0.2.0/sopvm-milestone-1/sopvm/sopvm/compiler/parser.py +283 -0
  43. sopvm-0.2.0/sopvm-milestone-1/sopvm/tests/__init__.py +0 -0
  44. sopvm-0.2.0/sopvm-milestone-1/sopvm/tests/unit/__init__.py +0 -0
  45. sopvm-0.2.0/sopvm-milestone-1/sopvm/tests/unit/fixtures/missing_frontmatter.md +3 -0
  46. sopvm-0.2.0/sopvm-milestone-1/sopvm/tests/unit/fixtures/valid.md +11 -0
  47. sopvm-0.2.0/sopvm-milestone-1/sopvm/tests/unit/test_cli.py +117 -0
  48. sopvm-0.2.0/sopvm-milestone-1/sopvm/tests/unit/test_parser.py +338 -0
  49. sopvm-0.2.0/src/sopvm/__init__.py +92 -0
  50. sopvm-0.2.0/src/sopvm/capability/__init__.py +12 -0
  51. sopvm-0.2.0/src/sopvm/capability/policy.py +94 -0
  52. sopvm-0.2.0/src/sopvm/capability/token.py +159 -0
  53. sopvm-0.2.0/src/sopvm/checker/__init__.py +5 -0
  54. sopvm-0.2.0/src/sopvm/checker/check.py +176 -0
  55. sopvm-0.2.0/src/sopvm/cli/__init__.py +1 -0
  56. sopvm-0.2.0/src/sopvm/cli/main.py +177 -0
  57. sopvm-0.2.0/src/sopvm/compiler/__init__.py +8 -0
  58. sopvm-0.2.0/src/sopvm/compiler/errors.py +16 -0
  59. sopvm-0.2.0/src/sopvm/compiler/lower.py +61 -0
  60. sopvm-0.2.0/src/sopvm/compiler/page.py +53 -0
  61. sopvm-0.2.0/src/sopvm/compiler/pipeline.py +37 -0
  62. sopvm-0.2.0/src/sopvm/integrations/__init__.py +1 -0
  63. sopvm-0.2.0/src/sopvm/integrations/langgraph/__init__.py +1 -0
  64. sopvm-0.2.0/src/sopvm/integrations/langgraph/node.py +83 -0
  65. sopvm-0.2.0/src/sopvm/ir/__init__.py +5 -0
  66. sopvm-0.2.0/src/sopvm/ir/model.py +94 -0
  67. sopvm-0.2.0/src/sopvm/ir/versions.py +7 -0
  68. sopvm-0.2.0/src/sopvm/parser/__init__.py +15 -0
  69. sopvm-0.2.0/src/sopvm/parser/ast.py +62 -0
  70. sopvm-0.2.0/src/sopvm/parser/errors.py +53 -0
  71. sopvm-0.2.0/src/sopvm/parser/parse.py +151 -0
  72. sopvm-0.2.0/src/sopvm/plugins/__init__.py +13 -0
  73. sopvm-0.2.0/src/sopvm/plugins/base.py +53 -0
  74. sopvm-0.2.0/src/sopvm/plugins/registry.py +43 -0
  75. sopvm-0.2.0/src/sopvm/plugins/sandbox.py +63 -0
  76. sopvm-0.2.0/src/sopvm/runtime/__init__.py +18 -0
  77. sopvm-0.2.0/src/sopvm/runtime/events.py +30 -0
  78. sopvm-0.2.0/src/sopvm/runtime/executor.py +288 -0
  79. sopvm-0.2.0/src/sopvm/runtime/gate.py +75 -0
  80. sopvm-0.2.0/src/sopvm/runtime/state.py +27 -0
  81. sopvm-0.2.0/src/sopvm/runtime/violations.py +29 -0
  82. sopvm-0.2.0/src/sopvm/schemas/capability.schema.json +25 -0
  83. sopvm-0.2.0/src/sopvm/schemas/sop.schema.json +78 -0
  84. sopvm-0.2.0/src/sopvm/telemetry/__init__.py +12 -0
  85. sopvm-0.2.0/src/sopvm/telemetry/events.py +89 -0
  86. sopvm-0.2.0/src/sopvm/telemetry/sink.py +58 -0
  87. sopvm-0.2.0/src/sopvm.egg-info/PKG-INFO +14 -0
  88. sopvm-0.2.0/src/sopvm.egg-info/SOURCES.txt +12 -0
  89. sopvm-0.2.0/src/sopvm.egg-info/dependency_links.txt +1 -0
  90. sopvm-0.2.0/src/sopvm.egg-info/entry_points.txt +2 -0
  91. sopvm-0.2.0/src/sopvm.egg-info/requires.txt +7 -0
  92. sopvm-0.2.0/src/sopvm.egg-info/top_level.txt +1 -0
  93. sopvm-0.2.0/tests/__init__.py +0 -0
  94. sopvm-0.2.0/tests/adversarial/README.md +74 -0
  95. sopvm-0.2.0/tests/adversarial/__init__.py +0 -0
  96. sopvm-0.2.0/tests/adversarial/test_escalation.py +115 -0
  97. sopvm-0.2.0/tests/adversarial/test_gate.py +166 -0
  98. sopvm-0.2.0/tests/adversarial/test_grammar_exploits.py +83 -0
  99. sopvm-0.2.0/tests/adversarial/test_ir_tampering.py +128 -0
  100. sopvm-0.2.0/tests/adversarial/test_provider_integrity.py +107 -0
  101. sopvm-0.2.0/tests/fixtures/refund-request-handling.sop.yaml +35 -0
  102. sopvm-0.2.0/tests/golden/__init__.py +0 -0
  103. sopvm-0.2.0/tests/golden/refund-request-handling.ast.json +79 -0
  104. sopvm-0.2.0/tests/golden/refund-request-handling.ir.json +67 -0
  105. sopvm-0.2.0/tests/golden/test_golden.py +47 -0
  106. sopvm-0.2.0/tests/golden/test_ir_golden.py +49 -0
  107. sopvm-0.2.0/tests/integration/__init__.py +0 -0
  108. sopvm-0.2.0/tests/integration/test_checker.py +32 -0
  109. sopvm-0.2.0/tests/integration/test_langgraph.py +54 -0
  110. sopvm-0.2.0/tests/integration/test_parse.py +65 -0
  111. sopvm-0.2.0/tests/integration/test_telemetry.py +114 -0
  112. sopvm-0.2.0/tests/property/__init__.py +0 -0
  113. sopvm-0.2.0/tests/property/test_executor_property.py +91 -0
  114. sopvm-0.2.0/tests/property/test_gate_property.py +95 -0
  115. sopvm-0.2.0/tests/property/test_ir_property.py +114 -0
  116. sopvm-0.2.0/tests/property/test_page_property.py +84 -0
  117. sopvm-0.2.0/tests/property/test_property.py +103 -0
  118. sopvm-0.2.0/tests/test_import_boundaries.py +78 -0
  119. sopvm-0.2.0/tests/unit/__init__.py +0 -0
  120. sopvm-0.2.0/tests/unit/test_capability_grammar.py +67 -0
  121. sopvm-0.2.0/tests/unit/test_capability_token.py +78 -0
  122. sopvm-0.2.0/tests/unit/test_checker.py +142 -0
  123. sopvm-0.2.0/tests/unit/test_cli.py +137 -0
  124. sopvm-0.2.0/tests/unit/test_executor.py +184 -0
  125. sopvm-0.2.0/tests/unit/test_gate.py +76 -0
  126. sopvm-0.2.0/tests/unit/test_lower.py +124 -0
  127. sopvm-0.2.0/tests/unit/test_page.py +124 -0
  128. sopvm-0.2.0/tests/unit/test_pipeline.py +36 -0
  129. sopvm-0.2.0/tests/unit/test_plugins.py +99 -0
  130. sopvm-0.2.0/tests/unit/test_policy.py +67 -0
  131. sopvm-0.2.0/tests/unit/test_public_api.py +89 -0
  132. sopvm-0.2.0/tests/unit/test_schema.py +85 -0
  133. sopvm-0.2.0/tests/unit/test_telemetry.py +110 -0
@@ -0,0 +1,22 @@
1
+ {
2
+ "compile": [
3
+ "sop_path",
4
+ "policy_path"
5
+ ],
6
+ "check": [
7
+ "compiled"
8
+ ],
9
+ "Runtime": [
10
+ "compiled",
11
+ "providers"
12
+ ],
13
+ "__init__": [
14
+ "compiled",
15
+ "providers"
16
+ ],
17
+ "run": [],
18
+ "execute": [
19
+ "node",
20
+ "request_tool"
21
+ ]
22
+ }
sopvm-0.2.0/.coverage ADDED
Binary file
@@ -0,0 +1,31 @@
1
+ name: Adversarial Security Tests
2
+
3
+ on:
4
+ pull_request:
5
+ branches: [main]
6
+ push:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ adversarial:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ python-version: ["3.10", "3.11", "3.12"]
15
+
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - name: Set up Python ${{ matrix.python-version }}
20
+ uses: actions/setup-python@v5
21
+ with:
22
+ python-version: ${{ matrix.python-version }}
23
+
24
+ - name: Install dependencies
25
+ run: |
26
+ python -m pip install --upgrade pip
27
+ pip install -e ".[dev]"
28
+
29
+ - name: Run adversarial test suite
30
+ run: |
31
+ pytest tests/adversarial/ -v --tb=short
@@ -0,0 +1,50 @@
1
+ name: Release to PyPI
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ build:
13
+ runs-on: ubuntu-latest
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+
17
+ - name: Set up Python
18
+ uses: actions/setup-python@v5
19
+ with:
20
+ python-version: "3.12"
21
+
22
+ - name: Install build dependencies
23
+ run: python -m pip install --upgrade pip build
24
+
25
+ - name: Build package
26
+ run: python -m build
27
+
28
+ - name: Upload build artifacts
29
+ uses: actions/upload-artifact@v4
30
+ with:
31
+ name: dist
32
+ path: dist/
33
+
34
+ publish:
35
+ needs: build
36
+ runs-on: ubuntu-latest
37
+ environment: release
38
+ permissions:
39
+ id-token: write # Required for PyPI trusted publishing (OIDC)
40
+ steps:
41
+ - name: Download build artifacts
42
+ uses: actions/download-artifact@v4
43
+ with:
44
+ name: dist
45
+ path: dist/
46
+
47
+ - name: Publish to PyPI
48
+ uses: pypa/gh-action-pypi-publish@release/v1
49
+ # Uses trusted publishing (OIDC) — no API token needed
50
+ # Configure at https://pypi.org/manage/project/sopvm/settings/publishing/
@@ -0,0 +1 @@
1
+ {"pid":19240,"startedAt":1784354811742}
@@ -0,0 +1,7 @@
1
+ - id: sopvm-check
2
+ name: SOPVM policy check
3
+ description: Check a compiled SOP IR against a policy file
4
+ entry: sopvm check
5
+ language: python
6
+ types: [json]
7
+ args: ["--policy", "policy.yaml"]
@@ -0,0 +1,81 @@
1
+ # ARCHITECTURE.md
2
+
3
+ SOPVM's architecture is built on one core principle: **the compiler never talks to a model, and the runtime never talks to raw SOP prose.** Everything the LLM sees at execution time came out of the compiler as a validated artifact.
4
+
5
+ ## Parser (`sopvm.parser`)
6
+
7
+ **Milestone:** M2
8
+
9
+ Parses strict YAML SOP files into a typed AST. Validates against a JSON Schema (`schemas/sop.schema.json`) and performs semantic checks (edge target resolution, reachability, capability grammar).
10
+
11
+ **Design rationale:** YAML was chosen over Markdown (which M1 used) because it's unambiguous to parse and doesn't require a hand-written grammar. The JSON Schema provides machine-readable validation; the semantic checks catch logic errors that the schema can't express.
12
+
13
+ **What the parser does NOT do:** It doesn't know about policies, capabilities, or execution semantics. It only validates structure.
14
+
15
+ See [INTERFACES.md §1](INTERFACES.md#1-sop-source-format-owned-by-m2) for the exact YAML format.
16
+
17
+ ## AST (`sopvm.parser.ast`)
18
+
19
+ **Milestone:** M2
20
+
21
+ Frozen dataclasses representing the parsed SOP: `SopDocument`, `StepNode`, `CapabilityRequest`. Immutable — the AST is a build artifact, not a mutable state object.
22
+
23
+ See [INTERFACES.md §2](INTERFACES.md#2-ast-owned-by-m2) for the exact shape.
24
+
25
+ ## Compiler (`sopvm.compiler`)
26
+
27
+ **Milestones:** M3 (lowering), M5 (paging)
28
+
29
+ Two passes:
30
+ 1. **Lower** (`lower.py`): Converts AST → IR (`CompiledProgram`). Direct 1:1 mapping, no optimization.
31
+ 2. **Page** (`page.py`): Computes `capabilities_paged` by intersecting declared capabilities against the policy via `satisfies()`. This is where the policy ceiling is applied.
32
+
33
+ **Design rationale:** Separating lowering from paging makes each pass independently testable and keeps the IR clean. The IR is the *only* artifact the runtime reads — it never sees the AST or source YAML.
34
+
35
+ **Why compilation is deterministic:** No LLM calls, no randomness, no side effects. Same input → same output, always. This is what makes the compiler testable without an API key.
36
+
37
+ See [INTERFACES.md §4](INTERFACES.md#4-ir--program-graph-owned-by-m3-capability-annotated-by-m5) for the IR shape.
38
+
39
+ ## Capability Model (`sopvm.capability`, `sopvm.checker`)
40
+
41
+ **Milestone:** M4
42
+
43
+ Two layers:
44
+ 1. **Capability tokens** (`capability/token.py`): Parse `namespace:action(params)` strings into structured tokens. Comparator params (`<=`, `>=`) express ceilings in policy entries.
45
+ 2. **Static checker** (`checker/check.py`): Validates that every declared capability in the IR satisfies at least one policy entry. This is compile-time enforcement.
46
+
47
+ **Design rationale:** Capabilities are unforgeable references, not strings the model can produce. The LLM never sees or manipulates a capability token directly — it only requests an action, and the gate decides whether the currently-held capability covers that action.
48
+
49
+ See [INTERFACES.md §3](INTERFACES.md#3-capability-token-schema-owned-by-m4) for the token schema and policy format.
50
+
51
+ ## Runtime (`sopvm.runtime`)
52
+
53
+ **Milestones:** M6 (executor), M7 (gate), M8 (providers)
54
+
55
+ The runtime is a stack machine with an explicit program counter:
56
+ 1. **Executor** (`executor.py`): Drives the state machine. Fixed transition table, not ad hoc branching.
57
+ 2. **Capability gate** (`gate.py`): Intercepts every tool-call request. Checks against `capabilities_paged` for the current step. DENIED is a terminal fact — no retry, no recovery.
58
+ 3. **Provider registry + sandbox** (`plugins/`): Routes approved calls to registered providers. The sandbox catches providers lying about their own scope (defense in depth).
59
+
60
+ **Design rationale:** The LLM is *never* trusted to control the program counter. It proposes ("I believe the precondition for branch B holds"), the engine decides. Every state transition is auditable via the telemetry system.
61
+
62
+ **IR version check:** The executor rejects IR files with unsupported versions before execution begins, preventing confusing mid-execution failures.
63
+
64
+ ## Telemetry (`sopvm.telemetry`)
65
+
66
+ **Milestone:** M10
67
+
68
+ Every state transition and gate decision is emitted to a `TelemetrySink`. The `JsonlSink` produces append-only JSONL traces. Telemetry failures are silently degraded — a broken sink never crashes the run it's observing.
69
+
70
+ See [INTERFACES.md §7](INTERFACES.md#7-event--telemetry-schema-owned-by-m10) for the event schema.
71
+
72
+ ## CLI (`sopvm.cli`)
73
+
74
+ **Milestone:** M11
75
+
76
+ Four subcommands: `compile`, `check`, `run`, `trace`. The `check` command is designed for pre-commit hooks (exit 1 on violations). No subcommand ever prints a raw Python traceback for expected failures.
77
+
78
+ ## Integration Points
79
+
80
+ - **LangGraph** (`sopvm.integrations.langgraph`): Wraps a compiled SOP as a StateGraph node. This is the ONLY module allowed to import langgraph (enforced by AST-based import boundary test).
81
+ - **Pre-commit** (`.pre-commit-hooks.yaml`): `sopvm-check` hook for CI/CD pipelines.
@@ -0,0 +1,55 @@
1
+ # Changelog
2
+
3
+ All notable changes to SOPVM will be documented in this file.
4
+
5
+ Format based on [Keep a Changelog](https://keepachangelog.com/).
6
+
7
+ ## [Unreleased]
8
+
9
+ ## [0.2.0] - 2026-07-18
10
+
11
+ > **Versioning note:** Per pre-1.0 SemVer convention, MINOR bumps (0.x)
12
+ > may signal breaking changes. This release replaces the M1 public API
13
+ > (`compile_check()`, Markdown input, argparse CLI) with the M2-M13
14
+ > public API (`compile()`, `check()`, `Runtime`, YAML input, click CLI).
15
+ > See VERSIONING_POLICY.md for details.
16
+
17
+ ### Added
18
+ - **M2**: SOP YAML parser with JSON Schema validation and semantic checks
19
+ - **M3**: AST→IR lowering pass (`CompiledProgram`, `IrNode`)
20
+ - **M4**: Capability token parser, policy loader, static checker (`satisfies()`, `check()`)
21
+ - **M5**: Paging pass (`apply_paging()`) and compile pipeline (`compile_sop()`)
22
+ - **M6**: Runtime executor with state machine, event hooks, infinite-loop detection
23
+ - **M7**: Capability gate — runtime tool-call interception and enforcement
24
+ - **M8**: Plugin architecture — `ToolProvider`, `ProviderRegistry`, sandbox wrapper
25
+ - **M9**: LangGraph integration adapter (`as_langgraph_node()`)
26
+ - **M10**: Telemetry — `Event`, `EventType` enum, `TelemetrySink`, `JsonlSink`
27
+ - **M11**: CLI with `compile`, `check`, `run`, `trace` subcommands (click)
28
+ - **M12**: Adversarial security test suite (25+ test cases, CI-gated)
29
+ - **M13**: Public API stabilization — `sopvm.compile()`, `sopvm.check()`, `sopvm.Runtime`
30
+ - Pre-commit hook (`sopvm-check`)
31
+ - IR version validation — `UnsupportedIrVersionError` for unsupported IR files
32
+ - API compatibility CI check script
33
+ - JSON Schema for SOP YAML format (`schemas/sop.schema.json`)
34
+ - JSON Schema for capability tokens (`schemas/capability.schema.json`)
35
+
36
+ ### Security
37
+ - **M4**: Static capability checker catches policy ceiling violations at compile time
38
+ - **M7**: Runtime capability gate denies out-of-scope tool calls
39
+ - **M8**: Provider sandbox catches lying providers (defense in depth)
40
+ - **M12**: IR tampering detection — runtime re-validates `capabilities_paged` against policy
41
+ - **M12**: Adversarial test corpus covers 25+ escalation techniques
42
+
43
+ ### Changed
44
+ - **M5**: Replaced M3's placeholder `capabilities_paged = capabilities_declared` with real policy-resolved subset
45
+ - **M10**: Extended M6's stub `Event` with full telemetry schema (EventType enum, timestamps, run_id)
46
+
47
+ ### Fixed
48
+ - **M12**: Fixed IR tampering gap — executor now re-validates capabilities_paged at run time when policy is provided
49
+
50
+ ## [0.1.0] - 2026-07-18
51
+
52
+ ### Added
53
+ - Initial release: Markdown+frontmatter SOP parser (M1)
54
+ - `sopvm compile --check` CLI command
55
+ - Unit tests for parser
@@ -0,0 +1,83 @@
1
+ # Contributing to SOPVM
2
+
3
+ ## Development Setup
4
+
5
+ ```bash
6
+ # Clone the repo
7
+ git clone https://github.com/your-org/sopvm.git
8
+ cd sopvm
9
+
10
+ # Install in editable mode with dev dependencies
11
+ pip install -e ".[dev]"
12
+
13
+ # Run the full test suite
14
+ make ci
15
+ # or equivalently:
16
+ pytest tests/ -v
17
+ ```
18
+
19
+ ## Project Structure
20
+
21
+ ```
22
+ src/sopvm/
23
+ ├── parser/ # M2: YAML parser + AST
24
+ ├── ir/ # M3: IR data model
25
+ ├── compiler/ # M3+M5: lowering + paging
26
+ ├── capability/ # M4: token parser + policy loader
27
+ ├── checker/ # M4: static capability checker
28
+ ├── runtime/ # M6-M8: executor, gate, violations
29
+ ├── plugins/ # M8: tool providers + registry + sandbox
30
+ ├── integrations/ # M9: LangGraph adapter (ONLY place langgraph is imported)
31
+ ├── telemetry/ # M10: events + sinks
32
+ ├── cli/ # M11: click CLI
33
+ tests/
34
+ ├── unit/ # Compiler + capability + scheduler unit tests
35
+ ├── integration/ # Runtime + pipeline integration tests
36
+ ├── adversarial/ # M12: security test corpus (CI-gated)
37
+ ├── golden/ # AST/IR JSON snapshot tests
38
+ ├── property/ # Hypothesis property-based tests
39
+ ```
40
+
41
+ ## Running Tests
42
+
43
+ ```bash
44
+ # Full suite
45
+ pytest tests/ -v
46
+
47
+ # Just adversarial tests (CI-gated)
48
+ pytest tests/adversarial/ -v
49
+
50
+ # With coverage
51
+ pytest tests/ --cov=src/sopvm --cov-report=term-missing
52
+
53
+ # Property-based tests only
54
+ pytest tests/property/ -v
55
+ ```
56
+
57
+ ## Test Taxonomy
58
+
59
+ | Category | Purpose | When to add |
60
+ |---|---|---|
61
+ | `unit/` | Isolated component tests | Any new function/class |
62
+ | `integration/` | End-to-end pipeline tests | New milestone or cross-component feature |
63
+ | `adversarial/` | Security boundary tests | New escalation technique or enforcement layer |
64
+ | `golden/` | Snapshot regression tests | Any change to AST/IR output format |
65
+ | `property/` | Hypothesis fuzz tests | Any invariant that holds for all valid inputs |
66
+
67
+ ## Proposing INTERFACES.md Changes
68
+
69
+ INTERFACES.md is the single source of truth for cross-milestone schemas. Changes require:
70
+
71
+ 1. **Read the Change Control section** at the bottom of INTERFACES.md
72
+ 2. **Version bump** to the affected schema
73
+ 3. **Explicit call-out** in the milestone prompt listing which section changed and why
74
+ 4. **Update all consuming modules** — a schema change in §2 (AST) must propagate to §3 (capability), §4 (IR), etc.
75
+
76
+ Do NOT silently redefine a schema. If you need a new field, add it (MINOR bump). If you need to remove or rename a field, that's a breaking change (MAJOR bump).
77
+
78
+ ## Code Style
79
+
80
+ - Python 3.10+ only
81
+ - No comments unless the WHY is non-obvious
82
+ - Frozen dataclasses for all data structures
83
+ - No runtime state mutations in the compiler/checker path
@@ -0,0 +1,231 @@
1
+ # SOPVM — INTERFACES.md (canonical contract doc)
2
+
3
+ Status: DRAFT skeleton. Every later milestone prompt must conform to this or explicitly call out a proposed change here first. This file is the single source of truth for cross-milestone shapes — no milestone may silently redefine a schema it doesn't own.
4
+
5
+ Ownership: each section names the milestone that *defines* it. Other milestones may *consume* it but not *redefine* it.
6
+
7
+ ---
8
+
9
+ ## 1. SOP Source Format (owned by M2)
10
+
11
+ Strict YAML, validated against a JSON Schema (`schemas/sop.schema.json`). No free-form prose fields feed the compiler — only structured fields do.
12
+
13
+ ```yaml
14
+ sop_version: "0.1"
15
+ name: "refund-request-handling"
16
+ policy: "policies/support-agent.policy.yaml" # top-level capability ceiling (see §3)
17
+
18
+ steps:
19
+ - id: verify_identity
20
+ description: "Confirm requester identity via order lookup"
21
+ requires:
22
+ capabilities: ["db:read(orders)"]
23
+ on_success: check_eligibility
24
+ on_failure: escalate_human
25
+
26
+ - id: check_eligibility
27
+ description: "Check refund policy eligibility"
28
+ requires:
29
+ capabilities: ["db:read(orders)", "db:read(policy_rules)"]
30
+ on_success: issue_refund
31
+ on_failure: escalate_human
32
+
33
+ - id: issue_refund
34
+ description: "Issue refund via payment provider"
35
+ requires:
36
+ capabilities: ["payments:refund(max_amount=100.00)"]
37
+ on_success: notify_user
38
+ on_failure: escalate_human
39
+
40
+ - id: notify_user
41
+ terminal: true
42
+ requires:
43
+ capabilities: ["notify:email"]
44
+
45
+ - id: escalate_human
46
+ terminal: true
47
+ requires:
48
+ capabilities: ["notify:slack(channel=support-escalations)"]
49
+ ```
50
+
51
+ Rules M2 must enforce at parse time:
52
+ - every `on_success`/`on_failure` target must resolve to a declared step id
53
+ - every non-terminal step must declare at least one outgoing edge
54
+ - `capabilities` entries must match the capability grammar in §3
55
+ - unreferenced steps (unreachable from the declared entry point) are a parse error, not a warning
56
+
57
+ ---
58
+
59
+ ## 2. AST (owned by M2)
60
+
61
+ Internal only — not a public contract, but shape is fixed here so M3 doesn't guess:
62
+
63
+ ```
64
+ SopDocument
65
+ ├── version: str
66
+ ├── policy_ref: str
67
+ └── steps: list[StepNode]
68
+ StepNode
69
+ ├── id: str
70
+ ├── description: str
71
+ ├── requires: CapabilityRequest[]
72
+ ├── edges: {on_success: str|None, on_failure: str|None}
73
+ └── terminal: bool
74
+ ```
75
+
76
+ ---
77
+
78
+ ## 3. Capability Token Schema (owned by M4)
79
+
80
+ Auditable outside Python — `schemas/capability.schema.json`.
81
+
82
+ Grammar: `namespace:action(param=value, ...)` — parameterized, not a flat string enum (see MILESTONES.md risk #2).
83
+
84
+ ```json
85
+ {
86
+ "$schema": "http://json-schema.org/draft-07/schema#",
87
+ "title": "CapabilityToken",
88
+ "type": "object",
89
+ "required": ["namespace", "action", "params"],
90
+ "properties": {
91
+ "namespace": { "type": "string", "examples": ["fs", "net", "db", "payments", "notify"] },
92
+ "action": { "type": "string", "examples": ["read", "write", "http", "refund"] },
93
+ "params": { "type": "object", "additionalProperties": true },
94
+ "raw": { "type": "string", "description": "original string form, e.g. payments:refund(max_amount=100.00)" }
95
+ }
96
+ }
97
+ ```
98
+
99
+ Policy file (top-level ceiling a SOP may never exceed):
100
+
101
+ ```yaml
102
+ # policies/support-agent.policy.yaml
103
+ policy_version: "0.1"
104
+ allowed_capabilities:
105
+ - "db:read(orders)"
106
+ - "db:read(policy_rules)"
107
+ - "payments:refund(max_amount<=100.00)"
108
+ - "notify:email"
109
+ - "notify:slack(channel=support-escalations)"
110
+ ```
111
+
112
+ Static checker (M4) contract: given a compiled IR + a policy file, return a `CheckResult`:
113
+
114
+ ```json
115
+ {
116
+ "passed": false,
117
+ "violations": [
118
+ {
119
+ "step_id": "issue_refund",
120
+ "requested": "payments:refund(max_amount=250.00)",
121
+ "reason": "exceeds policy ceiling payments:refund(max_amount<=100.00)"
122
+ }
123
+ ]
124
+ }
125
+ ```
126
+
127
+ This must be checkable **without running the runtime** — pure static analysis, usable as a pre-commit hook (M11).
128
+
129
+ ---
130
+
131
+ ## 4. IR / Program Graph (owned by M3, capability-annotated by M5)
132
+
133
+ ```json
134
+ {
135
+ "ir_version": "0.1",
136
+ "entry": "verify_identity",
137
+ "nodes": {
138
+ "verify_identity": {
139
+ "capabilities_declared": ["db:read(orders)"],
140
+ "capabilities_paged": ["db:read(orders)"],
141
+ "edges": { "on_success": "check_eligibility", "on_failure": "escalate_human" },
142
+ "terminal": false
143
+ }
144
+ }
145
+ }
146
+ ```
147
+
148
+ - `capabilities_declared`: what the source SOP asked for at this step (from AST, unchanged by compiler).
149
+ - `capabilities_paged`: what M5's paging-plan pass actually resolves as grantable *at this step* (may be a subset after policy intersection — never a superset of `capabilities_declared`).
150
+ - IR is the only artifact the runtime (M6) ever reads. The runtime never sees the AST or source YAML directly.
151
+
152
+ ---
153
+
154
+ ## 5. Runtime State Machine (owned by M6)
155
+
156
+ States per step: `PENDING → RUNNING → {DONE, FAILED, DENIED}`.
157
+
158
+ - `DENIED` is a distinct terminal state from `FAILED` — a capability violation is not an execution error, it's a policy event, and M10's telemetry must be able to tell them apart.
159
+ - Transition rules are a fixed table, not branching logic scattered through the executor — this table is what M12's adversarial tests assert against.
160
+
161
+ ---
162
+
163
+ ## 6. Tool Provider / Plugin Interface (owned by M8)
164
+
165
+ ```python
166
+ class ToolProvider(Protocol):
167
+ def declared_capabilities(self) -> list[str]: ...
168
+ def invoke(self, capability: CapabilityToken, args: dict) -> ToolResult: ...
169
+ ```
170
+
171
+ - `invoke` is never called directly by step logic — only by the gate (M7), which checks `capability` against `capabilities_paged` for the current step first.
172
+ - A provider that lies about `declared_capabilities()` (requests more at invoke time than it declared) is itself a `DENIED` event, not a crash.
173
+
174
+ ---
175
+
176
+ ## 7. Event / Telemetry Schema (owned by M10)
177
+
178
+ ```json
179
+ {
180
+ "event": "capability_denied",
181
+ "step_id": "issue_refund",
182
+ "requested": "payments:refund(max_amount=250.00)",
183
+ "paged": ["payments:refund(max_amount<=100.00)"],
184
+ "timestamp": "2026-07-18T12:00:00Z",
185
+ "run_id": "..."
186
+ }
187
+ ```
188
+
189
+ Event types (fixed enum, extend via versioning policy only): `step_started`, `step_completed`, `step_failed`, `capability_granted`, `capability_denied`, `run_started`, `run_completed`.
190
+
191
+ ---
192
+
193
+ ## 8. Public API (owned by M13, staged incrementally by M3/M6/M11)
194
+
195
+ ```python
196
+ sopvm.compile(sop_path: str, policy_path: str) -> CompiledProgram
197
+ sopvm.check(compiled: CompiledProgram) -> CheckResult
198
+ sopvm.Runtime(compiled: CompiledProgram, providers: list[ToolProvider])
199
+ .run() -> RunResult
200
+ ```
201
+
202
+ Anything not in this list is internal and may change without a version bump.
203
+
204
+ ---
205
+
206
+ ## 9. CLI Contract (owned by M11)
207
+
208
+ ```
209
+ sopvm compile <sop.yaml> --policy <policy.yaml> -o <out.ir.json>
210
+ sopvm check <out.ir.json> --policy <policy.yaml> # exit 1 on violation, for pre-commit
211
+ sopvm run <out.ir.json> --providers <providers.yaml>
212
+ sopvm trace <run_id> # replay telemetry
213
+ ```
214
+
215
+ ---
216
+
217
+ ## 10. LangGraph Adapter (owned by M9)
218
+
219
+ Adapter module only — must not leak LangGraph types into M1–M8. Exposes SOPVM as a single callable node:
220
+
221
+ ```python
222
+ def as_langgraph_node(compiled: CompiledProgram, providers: list[ToolProvider]) -> Callable
223
+ ```
224
+
225
+ ---
226
+
227
+ ## Change control
228
+
229
+ Any edit to a section above after its owning milestone has merged requires:
230
+ 1. A version bump to the affected schema (see VERSIONING_POLICY.md)
231
+ 2. An explicit call-out in the milestone prompt that changes it, listing exactly which section of this file is being modified and why
sopvm-0.2.0/PKG-INFO ADDED
@@ -0,0 +1,115 @@
1
+ Metadata-Version: 2.4
2
+ Name: sopvm
3
+ Version: 0.2.0
4
+ Summary: A compiler and capability-gated runtime for Standard Operating Procedures executed by LLM agents.
5
+ Author: Sushit
6
+ License: Apache-2.0
7
+ Requires-Python: >=3.10
8
+ Requires-Dist: click>=8.0
9
+ Requires-Dist: jsonschema>=4.20
10
+ Requires-Dist: pyyaml>=6.0
11
+ Provides-Extra: dev
12
+ Requires-Dist: build; extra == 'dev'
13
+ Requires-Dist: hypothesis>=6.90; extra == 'dev'
14
+ Requires-Dist: pytest-cov>=5.0; extra == 'dev'
15
+ Requires-Dist: pytest>=8.0; extra == 'dev'
16
+ Provides-Extra: langgraph
17
+ Requires-Dist: langgraph>=0.2.0; extra == 'langgraph'
18
+ Description-Content-Type: text/markdown
19
+
20
+ # SOPVM
21
+
22
+ **A runtime layer beneath agent frameworks — not another one.**
23
+
24
+ SOPVM compiles Standard Operating Procedures into executable programs and runs them on a capability-gated stack machine. It sits *underneath* LangGraph, CrewAI, or any tool-calling loop — the same way LLVM sits underneath Clang, Rust, and Swift rather than competing with any single language.
25
+
26
+ ## Why Compilation Matters
27
+
28
+ Most "follow the SOP" approaches today mean "paste the SOP text into a system prompt and hope." The LLM re-plans every step, inconsistently. SOPVM compiles the SOP once into a deterministic, auditable program. The LLM only does the semantic work (interpreting what a step means); the runtime handles state tracking, capability enforcement, and execution flow. This is measurably better — the research paper behind SOPVM shows up to 16-point accuracy gains over raw prose.
29
+
30
+ ## Quickstart
31
+
32
+ ```bash
33
+ # Install
34
+ pip install -e ".[dev]"
35
+
36
+ # Compile a SOP against a policy
37
+ sopvm compile examples/sops/refund-request-handling.sop.yaml \
38
+ --policy policies/support-agent.policy.yaml \
39
+ -o compiled.ir.json
40
+
41
+ # Check for policy violations (pre-commit mode)
42
+ sopvm check compiled.ir.json --policy policies/support-agent.policy.yaml
43
+ # exit 0 = all capabilities within policy
44
+ # exit 1 = violations found
45
+
46
+ # Execute the compiled SOP
47
+ sopvm run compiled.ir.json
48
+ ```
49
+
50
+ ### Example: compile and check
51
+
52
+ ```
53
+ $ sopvm compile tests/fixtures/refund-request-handling.sop.yaml \
54
+ --policy policies/support-agent.policy.yaml \
55
+ -o compiled.ir.json
56
+ Compiled to compiled.ir.json
57
+
58
+ $ sopvm check compiled.ir.json --policy policies/support-agent.policy.yaml
59
+ All capabilities within policy.
60
+ ```
61
+
62
+ ### Example: policy violation caught at check time
63
+
64
+ ```
65
+ $ sopvm check violating.ir.json --policy policies/support-agent.policy.yaml
66
+ VIOLATION step=a requested='payments:refund(max_amount=250.00)' reason=exceeds policy ceiling payments:refund(max_amount<=100.00)
67
+ ```
68
+
69
+ ### Example: runtime execution
70
+
71
+ ```
72
+ $ sopvm run compiled.ir.json
73
+ Final state: DONE
74
+ Path: verify_identity -> check_eligibility -> issue_refund -> notify_user
75
+ ```
76
+
77
+ ## Architecture
78
+
79
+ ```
80
+ SOP YAML ──► Parser ──► AST ──► Compiler ──► IR ──► Runtime ──► Result
81
+ (M2) (M2) (M3+M5) (M3) (M6-M8)
82
+ ┌──────┴──────┐
83
+ │ Gate │Plugins│
84
+ │ (M7) │ (M8) │
85
+ └──────┴──────┘
86
+ ```
87
+
88
+ | Box | Module | Milestone |
89
+ |---|---|---|
90
+ | Parser | `sopvm.parser` | M2 |
91
+ | AST | `sopvm.parser.ast` | M2 |
92
+ | Compiler (lower+page) | `sopvm.compiler` | M3, M5 |
93
+ | IR | `sopvm.ir` | M3 |
94
+ | Runtime (gate+providers) | `sopvm.runtime` | M6-M8 |
95
+ | Static Checker | `sopvm.checker` | M4 |
96
+ | Telemetry | `sopvm.telemetry` | M10 |
97
+ | CLI | `sopvm.cli` | M11 |
98
+ | LangGraph Adapter | `sopvm.integrations.langgraph` | M9 |
99
+
100
+ See [ARCHITECTURE.md](ARCHITECTURE.md) for detailed design rationale.
101
+
102
+ ## Documentation
103
+
104
+ - [ARCHITECTURE.md](ARCHITECTURE.md) — component design and rationale
105
+ - [INTERFACES.md](INTERFACES.md) — canonical contract schemas (M2-M13)
106
+ - [VERSIONING_POLICY.md](VERSIONING_POLICY.md) — SemVer rules
107
+ - [CHANGELOG.md](CHANGELOG.md) — release history
108
+ - [CONTRIBUTING.md](CONTRIBUTING.md) — development setup
109
+
110
+ ## Examples
111
+
112
+ - `examples/sops/` — example SOP YAML files
113
+ - `examples/providers/` — reference tool provider implementations
114
+ - `examples/langgraph_integration.py` — LangGraph StateGraph wrapper
115
+ - `examples/adversarial_walkthrough.md` — step-by-step escalation attempt