diff --git a/research/cm-language/runtime/CM-EXECUTION-MODEL.md b/research/cm-language/runtime/CM-EXECUTION-MODEL.md
new file mode 100644
index 0000000..56678be
--- /dev/null
+++ b/research/cm-language/runtime/CM-EXECUTION-MODEL.md
@@ -0,0 +1,757 @@
+# CM Execution Model: JSON Property Graph and `coh` Runtime
+
+Status: proposed foundational design; exact wire details may be clarified by implementation evidence before ABI freeze
+Scope: executable JSON IR, linking, execution, evidence, and verification
+Deferred: `.cm` surface syntax and compiler implementation
+Authority: review candidate until accepted through the project-native review path
+
+## Decision
+
+TSC will make a JSON execution model work before extending the `.cm` compiler.
+The compiler will later lower `.cm` into this model; it will not define a second
+runtime ontology.
+
+The executable heart of a CM is a finite typed directed acyclic graph of
+property checkers. Every checker has the same logical execution envelope, while
+its input, output, configuration, evidence, capability, and bound contracts are
+parametric and explicit.
+
+A complete CM is slightly more than that graph:
+
+> A CM is a governing question, a typed property-checker graph, a declarative
+> result derivation, an evidence-bearing receipt contract, and any warrant
+> obligations required to support its claims.
+
+This distinction is load-bearing. Checkers produce local facts and evidence.
+They do not set the CM's final result. `coh` executes the graph, evaluates the
+CM-owned result rule, and emits a `MeasurementReceipt` that a verifier can
+recompute and accept or refuse.
+
+## Why this is the next boundary
+
+The repository contains two useful but incomplete execution witnesses:
+
+- `coh-min` loads a hand-authored `NormalizedCMIR`, links a small plan, executes
+ an input-sensitive `file.exists` check, and emits a verified receipt. Its
+ result derivation and checker dispatch are still CM-specific OCaml.
+- Ascent-0 executes a seven-provider graph with retained alternatives, phase
+ ordering, oracle evidence, and result-specific receipt constraints. Its
+ runtime contracts are local to that CM family.
+
+Those witnesses constrain the general model from opposite directions. An
+ordinary repository check proves that the model is usable. Ascent-0 proves that
+it can retain alternatives, respect phase barriers, carry stronger evidence,
+and discharge Core-grounded warrant obligations. Neither witness alone is a
+sufficient ABI-freeze test.
+
+The immediate engineering objective is therefore:
+
+```text
+JSON CM -> generic linker -> generic checker DAG runtime
+ -> declarative result evaluator -> MeasurementReceipt -> verifier
+```
+
+A CM must be addable as data plus provider implementations. Adding a second CM
+must not require a `cm_id` branch, a new CM-specific classifier, or a hand-wired
+execution pipeline in `coh`.
+
+## First principles
+
+### 1. Measurement, not mutation
+
+A CM may observe, calculate, judge, invoke a child CM, consult a bounded oracle,
+or apply a pure transform. It does not admit, merge, release, repair, mutate the
+measured project, or exercise project authority. Actors, CNOS, and CDS retain
+those powers.
+
+### 2. Explicit dataflow
+
+Edges carry typed values or artifact references. Dependencies are derived from
+input bindings, not hidden provider calls or shared mutable state. A node becomes
+ready only when its required inputs are available.
+
+### 3. Bounded execution
+
+The v0 graph is finite and acyclic. Every checker declares its resource and
+capability envelope. Mutation, unbounded loops, and unrestricted general-purpose
+control flow are absent. Bounded collection operators may be added only with
+explicit size and depth limits.
+
+**Unbounded recursion is forbidden; bounded `invoke_cm` recursion is permitted.** A
+CM may invoke a child CM, and a child may do the same, but only under the limits the
+`RunRequest` names — `child_cm_depth`, `child_cm_calls`, and a digest stack that
+refuses direct or indirect cycles. Exceeding any limit, or re-entering a digest
+already on the stack, refuses fail-closed. Recursion with no named ceiling is not
+expressible: the ceiling is part of the request, not a runtime default.
+
+### 4. Evidence before claim strength
+
+A result class may require warrant obligations. Providers must produce the
+declared evidence; the runtime must retain it; the verifier must refuse a result
+whose evidence does not discharge the required obligations. TSC Core supplies
+the semantic and warrant foundation. It is not a separate runtime stage.
+
+### 5. Recomputable results
+
+The final result is a pure, total, bounded function of named run facts and
+retained evidence predicates. Narrative derivation text is documentation, not
+execution.
+
+### 6. Fail closed
+
+Missing bindings, undeclared capabilities, schema mismatches, digest mismatches,
+unsupported strong claims, and non-recomputable derivations are refusal or
+verification failure. They are never silently downgraded into success.
+
+## Core ontology
+
+| Concept | Owns | Does not own |
+|---|---|---|
+| Property | The local question or fact to be established | Provider selection |
+| Checker requirement | Typed interface, capability class, schemas, evidence, bounds | Concrete implementation identity |
+| Provider | One implementation of a checker capability | CM result authority |
+| `NormalizedStep` | The check the normalized CM requires | Host-specific grants or paths |
+| `SandboxPlanStep` | The selected provider, adapters, grants, and discharge proof | New methodology semantics |
+| `RunRequest` | Exact CM, subject artifacts, parameters, profile, and ceilings | Host-local artifact identity |
+| `CheckOutcome` | Local status, outputs, evidence, provenance, diagnostics | Final CM result |
+| Result rule | Exactly one declared CM result from run facts | Provider effects |
+| `MeasurementReceipt` | Request/IR/plan binding, trace, evidence, derivation witness, obligations | Mutation or admission authority |
+
+“Property checker” is the unifying runtime role. Mechanical tools, LLM-backed
+judges, child CMs, oracle adapters, and pure transforms implement different
+checker classes through one envelope; they are not forced to share one concrete
+payload type.
+
+## Artifact pipeline
+
+```text
+.cm source (later)
+ |
+ v
+NormalizedCMIR JSON <---- authored directly during JSON-first phase
+ |
+ | + RunRequest + provider registry + host policy
+ v
+linker
+ |
+ v
+SandboxExecutionPlan
+ |
+ v
+checker DAG runtime
+ |
+ v
+pure result evaluator
+ |
+ v
+MeasurementReceipt
+ |
+ v
+receipt verifier
+```
+
+Normalization and linking are deliberately different moments:
+
+- normalization resolves source syntax, defaults, symbols, imports, and schemas
+ into concrete methodology requirements;
+- linking selects concrete providers and host adapters for one `RunRequest`, then
+ proves that the plan satisfies the normalized requirements without granting
+ more authority than was declared.
+
+`CompiledCM` is not a second execution ontology. It is deferred. If later useful,
+it may be a content-addressed cache/envelope around normalized IR and reusable
+link metadata. The per-run `SandboxExecutionPlan` remains the only concrete
+execution binding.
+
+## JSON document family
+
+The names and semantics below are the backbone for `tsc-cm-ir/0.2`. Field
+spelling may be refined when the CUE schema and two-sided fixtures are built, but
+implementations must preserve the boundaries between these objects.
+
+### `NormalizedCMIR`
+
+The normalized IR is closed, concrete, portable, and content-addressed. It
+contains no measurement result and no concrete provider binding.
+
+```json
+{
+ "format": "tsc-cm-ir/0.2",
+ "cm": {
+ "id": "example.repo-basics",
+ "version": "0.1.0",
+ "source_digest": "sha256:..."
+ },
+ "question": "Does the repository expose the required entry documentation?",
+ "inputs": {},
+ "steps": [],
+ "result": {},
+ "receipt": {},
+ "permissions": {}
+}
+```
+
+Required top-level semantics:
+
+- `cm` identifies the methodology and its source lineage.
+- `question` is the governing measurement question.
+- `inputs` declares named, typed subject inputs.
+- `steps` is the finite checker DAG.
+- `result` declares the result vocabulary, executable rules, and warrant
+ obligations.
+- `receipt` selects one closed receipt family and its evidence contract.
+- `permissions` is the maximum capability/resource envelope normalization allows
+ the linker to request.
+
+### `NormalizedStep`
+
+```json
+{
+ "id": "readme_exists",
+ "kind": "mechanical",
+ "checker": {
+ "capability": "fs.file-exists",
+ "interface": "tsc-checker/0.1"
+ },
+ "inputs": {
+ "root": {
+ "from": { "input": "repository" },
+ "schema": "tsc://schema/directory-artifact/0.1"
+ }
+ },
+ "outputs": {
+ "present": { "schema": "tsc://schema/boolean/0.1", "required": true }
+ },
+ "config": { "relative_path": "README.md" },
+ "evidence": {
+ "schema": "tsc://schema/file-observation/0.1",
+ "required": true
+ },
+ "capabilities": {
+ "request": ["subject.fs.read"]
+ },
+ "bounds": {
+ "wall_time_ms": 1000,
+ "output_bytes": 4096
+ },
+ "failure_policy": {
+ "refused": "fact_unavailable",
+ "incomplete": "fact_unavailable",
+ "failed": "run_failed"
+ }
+}
+```
+
+Rules:
+
+- `id` is unique within the CM.
+- `kind` is one of `mechanical`, `semantic_judgment`, `invoke_cm`, `oracle`, or
+ `transform`.
+- `checker.capability` identifies what must be implemented; it does not select
+ who implements it.
+- Every input binds either a CM input or one named output port of another step.
+ Those bindings define the graph edges.
+- Every input, output, and evidence payload has an explicit schema.
+- `config` is methodology-owned and portable. Host locators, credentials, and
+ concrete provider configuration do not belong here.
+- Requested capabilities and bounds are ceilings, not grants.
+- `failure_policy` maps a checker outcome into run-fact availability or run
+ status. It must not directly select a CM result class.
+
+The existing `#TypedStep` and runtime-private step shapes mix requirement and
+binding. They are migration sources, not the final executable step contract.
+The existing `failure -> ResultClass` shortcut is superseded because it gives a
+node hidden authority over the CM result.
+
+### `RunRequest`
+
+The run request is a first-class, canonical, content-addressed artifact.
+
+```json
+{
+ "format": "tsc-run-request/0.1",
+ "cm_ir": { "kind": "normalized_cm_ir", "digest": "sha256:..." },
+ "subject": {
+ "repository": {
+ "kind": "directory_snapshot",
+ "scheme": "directory-merkle/0.1",
+ "digest": "sha256:..."
+ }
+ },
+ "profile": "default",
+ "parameters": {},
+ "capability_ceiling": ["subject.fs.read"],
+ "bounds": {
+ "wall_time_ms": 10000,
+ "child_cm_depth": 4,
+ "child_cm_calls": 32,
+ "evidence_bytes": 10485760
+ }
+}
+```
+
+Artifact digests identify inputs. Local paths, mount points, URLs, credentials,
+and process handles are locators supplied by the host during linking; they do
+not replace content identity. Repeating the same request may create a new
+execution id, but must not change the canonical request digest.
+
+Every subject entry MUST name a versioned `scheme` alongside its `kind` and
+`digest`. The scheme fixes how the snapshot is constructed and digested, so a
+verifier can recompute identity rather than trust it. An absent or unrecognized
+scheme refuses fail-closed — see §Deferred decisions, which defers the catalog of
+schemes, not the requirement to name one.
+
+### `SandboxExecutionPlan`
+
+The plan is the linker's closed, concrete answer for one request.
+
+```json
+{
+ "format": "tsc-sandbox-plan/0.1",
+ "request_digest": "sha256:...",
+ "cm_ir_digest": "sha256:...",
+ "steps": [
+ {
+ "step_id": "readme_exists",
+ "provider": {
+ "id": "coh.fs.file-exists",
+ "version": "0.1.0",
+ "digest": "sha256:..."
+ },
+ "adapters": {
+ "root": { "kind": "readonly_directory", "handle": "subject:repository" }
+ },
+ "grants": ["subject.fs.read"],
+ "limits": { "wall_time_ms": 1000, "output_bytes": 4096 },
+ "discharge": {
+ "checker_interface": true,
+ "input_schemas": true,
+ "output_schemas": true,
+ "evidence_schema": true,
+ "capability_subset": true,
+ "bounds_within_request": true
+ }
+ }
+ ]
+}
+```
+
+The linker must establish all of the following before execution:
+
+1. every normalized step has exactly one binding;
+2. the provider implements the required checker interface and capability class;
+3. input, output, and evidence schemas are compatible;
+4. grants are sufficient and do not exceed both the step request and the
+ `RunRequest` ceiling;
+5. plan limits are within normalized and request bounds;
+6. every artifact adapter is confined to its declared subject surface;
+7. provider identities and executable artifacts are pinned by version and
+ digest.
+
+Any unproved obligation refuses linking. Missing capability and excess
+capability are both errors.
+
+## Uniform checker contract
+
+The logical interface is:
+
+```text
+execute(CheckRequest) -> CheckOutcome