{
  "schema_version": "rustqec.envelope-support.v1",
  "purpose": "Executable, decoder-specific support matrix for the atom-loss envelope decoders. Verified by tools/check_envelope_support.py, which runs every declared control against a real rustqec binary. Per-decoder promotion out of Beta is decided by the release-readiness gate and published through the version-bound evidence bundle. published_support declares the intended release metadata but is not publication proof: consumers must also fetch the post-publication verification marker.",
  "applies_to": {
    "repository": "nzy1997/rust-qec",
    "source_revision": "359fc656d6a8d7150489fd6735fdf6f9f5e236c2",
    "release_line": "0.3 release line (v0.3.3 and later)",
    "note": "The promise applies to the recorded source revision and later revisions for which tools/check_envelope_support.py passes. A version string alone is not a synonym for stable support: per-decoder promotion is gated by the release-readiness check (issue #716) and is durable only through the release's published evidence bundle (issue #719). Releases without that bundle, such as v0.3.0, lack a verified promotion."
  },
  "decoders": {
    "envelope-matching": {
      "required_build_features": [],
      "current_maturity": "supported",
      "proposed_release_maturity": "supported-candidate",
      "published_support": {
        "maturity": "supported",
        "release": "v0.3.3",
        "release_url": "https://github.com/nzy1997/rust-qec/releases/tag/v0.3.3",
        "evidence_asset": "envelope-support-evidence-v0.3.3.tar.gz",
        "evidence_url": "https://github.com/nzy1997/rust-qec/releases/download/v0.3.3/envelope-support-evidence-v0.3.3.tar.gz",
        "verification_asset": "envelope-support-verification-v0.3.3.json",
        "verification_url": "https://api.github.com/repos/nzy1997/rust-qec/releases/tags/v0.3.3",
        "scope": "Flat loss-visible Mid-SWAP memory-Z circuits within the declared circuit contract and the measured operating envelope."
      },
      "objective": "Minimum-weight perfect matching on a loss-conditioned graph; an approximation to the declared fault-configuration objective. Ties are resolved by the matching implementation and are only checked against the explicitly allowed answer sets recorded by the independent correctness suite.",
      "known_limitations": [
        "Not the exact declared fault-configuration objective; no logical-class Bayes-optimality claim.",
        "--shot-timeout-ms is rejected as invalid_arguments; per-shot timeouts apply to the MLE solve path only."
      ],
      "numeric_operating_limits": "Measured operating envelope: benchmarks/atom_loss/readiness/resources/ (report.md + manifest.json, issue #715). Recommendations are workload/machine-specific; larger configurations are unmeasured, not promised."
    },
    "envelope-mle": {
      "required_build_features": ["ilp"],
      "current_maturity": "supported",
      "proposed_release_maturity": "supported-candidate",
      "published_support": {
        "maturity": "supported",
        "release": "v0.3.3",
        "release_url": "https://github.com/nzy1997/rust-qec/releases/tag/v0.3.3",
        "evidence_asset": "envelope-support-evidence-v0.3.3.tar.gz",
        "evidence_url": "https://github.com/nzy1997/rust-qec/releases/download/v0.3.3/envelope-support-evidence-v0.3.3.tar.gz",
        "verification_asset": "envelope-support-verification-v0.3.3.json",
        "verification_url": "https://api.github.com/repos/nzy1997/rust-qec/releases/tags/v0.3.3",
        "scope": "Exactly four measured Mid-SWAP workload points: d=3/r=2 at loss 0.002 and d=3/r=1 at loss 0.01, each with batches 1,024 and 16,384; no interpolation."
      },
      "objective": "Exact most-likely fault configuration for the declared envelope model, solved per conditioned loss pattern with ILP.",
      "known_limitations": [
        "Envelope candidate count is bounded (rejects with unsupported_circuit when the limit is exceeded, e.g. the conventional fixture).",
        "Per-shot solve time is unbounded without --shot-timeout-ms; timeout and infeasible outcomes are reported via distinct structured errors.",
        "Missing the ilp build feature removes the decoder from capabilities entirely; a supported MLE promise requires an ILP-capable archive."
      ],
      "numeric_operating_limits": "Measured operating envelope: benchmarks/atom_loss/readiness/resources/ (report.md + manifest.json, issue #715). Recommendations are workload/machine-specific; larger configurations are unmeasured, not promised."
    }
  },
  "circuit_contract": {
    "dataset_format": "rstim_decoder_dataset schema_version 1, mode measurements_blinded (public records only; private answers are never inputs)",
    "circuit_families": [
      {
        "id": "midswap",
        "description": "Flat loss-visible Mid-SWAP rotated surface-code memory-Z circuits as generated by rstim rotated_memory_z_midswap and pinned under rustqec-cli/tests/fixtures/current_rstim_atom_loss/midswap*.",
        "status": "supported-domain",
        "domain_note": "Each decoder's published support record defines its own verified subset of this family: Matching uses the declared circuit contract and measured operating envelope, while MLE is limited to the four exact loss/batch points in docs/envelope-mle-scope.json. The pinned fixtures are finite witnesses, not a promise that every family member has been exercised."
      },
      {
        "id": "conventional-stim-annotated",
        "description": "Conventional (non-Mid-SWAP) Stim-generated rotated memory-Z circuits annotated into the loss-visible subset by tools/annotate_loss_visible.py.",
        "status": "isolated-checked-examples",
        "domain_note": "Only the pinned fixtures are checked. Finite examples must not be read as support for every conventional circuit shape."
      }
    ],
    "readout_basis": "Z basis only: ML/MZL (terminal) and MRL/MRZL (resetting) loss-visible readouts; MXL/MYL/MRXL/MRYL are rejected",
    "allowed_instructions": ["QUBIT_COORDS", "SHIFT_COORDS", "TICK", "R", "RZ", "H", "CX", "CNOT", "ZCX", "X_ERROR", "DEPOLARIZE1", "DEPOLARIZE2", "LOSS", "ML", "MZL", "MRL", "MRZL", "DETECTOR", "OBSERVABLE_INCLUDE"],
    "restrictions": [
      "Flat circuits only: REPEAT blocks are rejected before flattening.",
      "No sweep bits; 1..=64 observables.",
      "ML/MZL must be terminal for each measured physical wire; MRL/MRZL closes the loss window and restores the wire.",
      "Loss-visible readouts require a LOSS opportunity since the last reset and carry no inline noise.",
      "CX/CNOT/ZCX pairs must be complete and qubit-disjoint within one instruction; CX is resolved as H-CZ-H.",
      "Every loss-visible readout must be loss-flag/value interleaved; detectors and observables must reference value bits, not loss flags.",
      "Pauli DEM probabilities must be finite and below 0.5; decomposed non-graphlike Pauli effects are rejected."
    ]
  },
  "release_candidate_scope": {
    "statement": "Supported-release candidate scope: envelope-matching on flat loss-visible Mid-SWAP memory-Z circuits inside the circuit contract above, within the numeric operating limits of the resource report; and envelope-mle at exactly the four measured workload points declared by docs/envelope-mle-scope.json, with no interpolation. Conventional circuits remain isolated checked examples for Matching and outside the MLE supported domain. Known accepted regression cases are retained without being promoted merely because they run.",
    "promotion_gate": "Issue #716 per-decoder release-readiness gate (correctness + resources + installed-artifact checks on both native platforms)"
  },
  "controls": [
    {
      "id": "mini-circuit-known-answer",
      "description": "Hand-computed two-wire loss circuit with a known logical answer per shot; pinned by rustqec-cli/tests/decode.rs.",
      "decoders": ["envelope-matching", "envelope-mle"],
      "dataset": {
        "type": "inline",
        "circuit": "QUBIT_COORDS(0,0) 0\nQUBIT_COORDS(1,0) 1\nR 0 1\nTICK[rstim:logical_flip_point]\nX_ERROR(0.1) 0\nX_ERROR(0.01) 1\nLOSS(0.1) 0\nH 0\nH 0\nCX 0 1\nX_ERROR(0.02) 0\nLOSS(0.1) 1\nML 0 1\nDETECTOR(0,0,0) rec[-3]\nDETECTOR(1,0,0) rec[-1]\nOBSERVABLE_INCLUDE(0) rec[-3]\n",
        "shots_hex": "02010100"
      },
      "expected": {
        "outcome": "success",
        "predictions_hex": "01010100",
        "known_answer": {
          "source": "hand-computed; pinned in rustqec-cli/tests/decode.rs (EXPECTED_PREDICTIONS)",
          "observables_per_shot": [[0], [0], [0], []]
        },
        "stats": {
          "shot_count": 4,
          "attempted_shot_count": 4,
          "distinct_loss_patterns": 2,
          "distinct_loss_patterns_exact": true,
          "timeout_count": 0,
          "infeasible_shot_count": 0
        },
        "decoder_stats": {
          "envelope-matching": {"matching_graph_builds": 2, "cache_hits": 2, "mle_model_builds": 0},
          "envelope-mle": {"mle_model_builds": 2, "matching_graph_builds": 0}
        }
      }
    },
    {
      "id": "lost-value-placeholder-invariance",
      "description": "Both shots herald the same loss with different placeholder value bits; predictions must be identical.",
      "decoders": ["envelope-matching", "envelope-mle"],
      "dataset": {
        "type": "inline",
        "circuit": "QUBIT_COORDS(0,0) 0\nR 0\nX_ERROR(0.1) 0\nLOSS(0.1) 0\nMRL 0\nX_ERROR(0.02) 0\nLOSS(0.1) 0\nML 0\nDETECTOR(0,0,0) rec[-3]\nDETECTOR(0,0,1) rec[-3] rec[-1]\nOBSERVABLE_INCLUDE(0) rec[-1]\n",
        "shots_hex": "090b"
      },
      "expected": {
        "outcome": "success",
        "predictions_hex": "0101",
        "known_answer": {
          "source": "placeholder bits are arbitrary by the loss-visible record contract; pinned in rustqec-cli/tests/decode.rs",
          "observables_per_shot": [[0], [0]]
        },
        "stats": {
          "shot_count": 2,
          "distinct_loss_patterns": 1,
          "distinct_loss_patterns_exact": true
        },
        "decoder_stats": {
          "envelope-matching": {"matching_graph_builds": 1},
          "envelope-mle": {"mle_model_builds": 1}
        }
      }
    },
    {
      "id": "midswap-fixture-matching-acceptance",
      "description": "Pinned d=5/r=15 Mid-SWAP regression fixture accepted by envelope-matching.",
      "decoders": ["envelope-matching"],
      "dataset": {"type": "fixture", "path": "rustqec-cli/tests/fixtures/current_rstim_atom_loss/midswap"},
      "expected": {
        "outcome": "success",
        "predictions_sha256": "96a296d224f285c67bee93c30f8a309157f0daa35dc5b87e410b78630a09cfc7",
        "stats": {
          "shot_count": 2,
          "attempted_shot_count": 2,
          "primitive_probe_count": 15345,
          "primitive_symptom_terms": 29128,
          "loss_envelope_candidate_count": 0,
          "timeout_count": 0,
          "infeasible_shot_count": 0
        },
        "decoder_stats": {
          "envelope-matching": {"matching_graph_builds": 2, "mle_model_builds": 0}
        }
      }
    },
    {
      "id": "midswap-fixture-mle-acceptance",
      "description": "The same Mid-SWAP fixture accepted by envelope-mle under a generous per-shot timeout. Retained as a regression case; does not promote MLE.",
      "decoders": ["envelope-mle"],
      "dataset": {"type": "fixture", "path": "rustqec-cli/tests/fixtures/current_rstim_atom_loss/midswap"},
      "arguments": {"shot_timeout_ms": 2000},
      "expected": {
        "outcome": "success",
        "predictions_sha256": "96a296d224f285c67bee93c30f8a309157f0daa35dc5b87e410b78630a09cfc7",
        "stats": {
          "shot_count": 2,
          "attempted_shot_count": 2,
          "loss_envelope_candidate_count": 30538,
          "timeout_count": 0,
          "infeasible_shot_count": 0
        },
        "decoder_stats": {
          "envelope-mle": {"mle_model_builds": 2, "mle_detector_rows": 360}
        }
      }
    },
    {
      "id": "midswap-canonical-mle-known-answer",
      "description": "Canonical-detector MLE control with an independently derived known answer (renvelope exact reference solver; issue #679 provenance).",
      "decoders": ["envelope-mle"],
      "dataset": {"type": "fixture", "path": "rustqec-cli/tests/fixtures/current_rstim_atom_loss/midswap_canonical_mle"},
      "expected": {
        "outcome": "success",
        "predictions_hex": "01",
        "known_answer": {
          "source": "rustqec-cli/tests/fixtures/current_rstim_atom_loss/midswap_canonical_mle/provenance.json (expected_observables, reference_cases.json.zst sha256 80d39123d29836059acf45575031b07d29ed919a46f63426bbc96ce7af9cd1a4) evaluated by the independent renvelope exact solver",
          "observables_per_shot": [[0]]
        },
        "stats": {
          "shot_count": 1,
          "attempted_shot_count": 1,
          "timeout_count": 0,
          "infeasible_shot_count": 0
        },
        "decoder_stats": {
          "envelope-mle": {"mle_model_builds": 1, "mle_detector_rows": 360}
        }
      }
    },
    {
      "id": "midswap-canonical-matching-acceptance",
      "description": "The canonical fixture also decodes under envelope-matching; retained as a regression control.",
      "decoders": ["envelope-matching"],
      "dataset": {"type": "fixture", "path": "rustqec-cli/tests/fixtures/current_rstim_atom_loss/midswap_canonical_mle"},
      "expected": {
        "outcome": "success",
        "predictions_sha256": "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a",
        "stats": {
          "shot_count": 1,
          "attempted_shot_count": 1,
          "loss_envelope_candidate_count": 0
        },
        "decoder_stats": {
          "envelope-matching": {"matching_graph_builds": 1}
        }
      }
    },
    {
      "id": "conventional-fixture-matching-acceptance",
      "description": "Pinned conventional Stim-annotated fixture accepted by envelope-matching (isolated checked example, not a family promise).",
      "decoders": ["envelope-matching"],
      "dataset": {"type": "fixture", "path": "rustqec-cli/tests/fixtures/current_rstim_atom_loss/conventional"},
      "expected": {
        "outcome": "success",
        "predictions_sha256": "47dc540c94ceb704a23875c11273e16bb0b8a87aed84de911f2133568115f254",
        "stats": {
          "shot_count": 2,
          "attempted_shot_count": 2,
          "primitive_probe_count": 15345,
          "primitive_symptom_terms": 29360,
          "loss_envelope_candidate_count": 0
        },
        "decoder_stats": {
          "envelope-matching": {"matching_graph_builds": 2}
        }
      }
    },
    {
      "id": "conventional-mle-candidate-explosion-rejection",
      "description": "The conventional fixture exceeds the MLE envelope candidate limit and must be rejected before any output file is published.",
      "decoders": ["envelope-mle"],
      "dataset": {"type": "fixture", "path": "rustqec-cli/tests/fixtures/current_rstim_atom_loss/conventional"},
      "arguments": {"shot_timeout_ms": 2000},
      "expected": {
        "outcome": "rejection",
        "error_code": "unsupported_circuit",
        "exit_code": 2,
        "message_contains": "exceeds candidate limit",
        "predictions_written": false,
        "stats_written": false
      }
    },
    {
      "id": "repeat-block-rejection",
      "description": "REPEAT blocks are outside the flat native subset and are rejected before flattening.",
      "decoders": ["envelope-matching", "envelope-mle"],
      "dataset": {
        "type": "inline",
        "circuit": "QUBIT_COORDS(0,0) 0\nR 0\nLOSS(0.1) 0\nREPEAT 2 {\nH 0\n}\nML 0\nDETECTOR(0,0,0) rec[-1]\nOBSERVABLE_INCLUDE(0) rec[-1]\n",
        "shots_hex": "00"
      },
      "expected": {
        "outcome": "rejection",
        "error_code": "unsupported_circuit",
        "exit_code": 2,
        "message_contains": "REPEAT",
        "predictions_written": false,
        "stats_written": false
      }
    },
    {
      "id": "unsupported-instruction-rejection",
      "description": "Gates/readouts outside the declared subset are rejected with unsupported_circuit; variants pinned by rustqec-cli/tests/decode.rs and external_fixtures.rs.",
      "decoders": ["envelope-matching"],
      "dataset": {
        "type": "variants",
        "base_circuit": "QUBIT_COORDS(0,0) 0\nQUBIT_COORDS(1,0) 1\nR 0 1\nTICK[rstim:logical_flip_point]\nX_ERROR(0.1) 0\nX_ERROR(0.01) 1\nLOSS(0.1) 0\nH 0\nH 0\nCX 0 1\nX_ERROR(0.02) 0\nLOSS(0.1) 1\nML 0 1\nDETECTOR(0,0,0) rec[-3]\nDETECTOR(1,0,0) rec[-1]\nOBSERVABLE_INCLUDE(0) rec[-3]\n",
        "shots_hex": "02010100",
        "variants": [
          {"id": "sqrt-x-gate", "replace": ["H 0\nH 0\n", "SQRT_X 0\n"]},
          {"id": "x-basis-loss-readout", "replace": ["ML 0 1\n", "MXL 0 1\n"]},
          {"id": "overlapping-cx-pairs", "replace": ["CX 0 1\n", "CX 0 1 1 0\n"]},
          {"id": "unresolved-cz", "replace": ["CX 0 1\n", "CZ 0 1\n"]},
          {"id": "non-terminal-ml", "replace": ["ML 0 1\n", "ML 0\nX_ERROR(0.1) 0\nML 1\n"]}
        ]
      },
      "expected": {
        "outcome": "rejection",
        "error_code": "unsupported_circuit",
        "exit_code": 2,
        "predictions_written": false,
        "stats_written": false
      }
    },
    {
      "id": "non-metadata-annotation-rejection",
      "description": "A circuit whose annotation carries non-metadata content (S gate) is rejected; pinned by external_fixtures.rs.",
      "decoders": ["envelope-matching"],
      "dataset": {
        "type": "derived",
        "circuit_file": "rustqec-cli/tests/fixtures/current_rstim_atom_loss/unsupported_non_metadata.stim",
        "shots": 1
      },
      "expected": {
        "outcome": "rejection",
        "error_code": "unsupported_circuit",
        "exit_code": 2,
        "message_contains": "S",
        "predictions_written": false,
        "stats_written": false
      }
    },
    {
      "id": "terminal-ml-rejection",
      "description": "Acting on a wire after its terminal ML violates the terminal-readout invariant.",
      "decoders": ["envelope-matching"],
      "dataset": {
        "type": "derived",
        "circuit_file": "rustqec-cli/tests/fixtures/stim_rotated_memory_z_d3_r2_loss_visible.stim",
        "append": "H 1\n",
        "shots": 1
      },
      "expected": {
        "outcome": "rejection",
        "error_code": "unsupported_circuit",
        "exit_code": 2,
        "message_contains": "ML must be terminal",
        "predictions_written": false,
        "stats_written": false
      }
    },
    {
      "id": "observable-limit-rejection",
      "description": "More than 64 observables exceeds the declared observable bound.",
      "decoders": ["envelope-matching"],
      "dataset": {
        "type": "inline",
        "circuit": "QUBIT_COORDS(0,0) 0\nR 0\nLOSS(0.1) 0\nML 0\nDETECTOR(0,0,0) rec[-1]\nOBSERVABLE_INCLUDE(0) rec[-1]\nOBSERVABLE_INCLUDE(1) rec[-1]\nOBSERVABLE_INCLUDE(2) rec[-1]\nOBSERVABLE_INCLUDE(3) rec[-1]\nOBSERVABLE_INCLUDE(4) rec[-1]\nOBSERVABLE_INCLUDE(5) rec[-1]\nOBSERVABLE_INCLUDE(6) rec[-1]\nOBSERVABLE_INCLUDE(7) rec[-1]\nOBSERVABLE_INCLUDE(8) rec[-1]\nOBSERVABLE_INCLUDE(9) rec[-1]\nOBSERVABLE_INCLUDE(10) rec[-1]\nOBSERVABLE_INCLUDE(11) rec[-1]\nOBSERVABLE_INCLUDE(12) rec[-1]\nOBSERVABLE_INCLUDE(13) rec[-1]\nOBSERVABLE_INCLUDE(14) rec[-1]\nOBSERVABLE_INCLUDE(15) rec[-1]\nOBSERVABLE_INCLUDE(16) rec[-1]\nOBSERVABLE_INCLUDE(17) rec[-1]\nOBSERVABLE_INCLUDE(18) rec[-1]\nOBSERVABLE_INCLUDE(19) rec[-1]\nOBSERVABLE_INCLUDE(20) rec[-1]\nOBSERVABLE_INCLUDE(21) rec[-1]\nOBSERVABLE_INCLUDE(22) rec[-1]\nOBSERVABLE_INCLUDE(23) rec[-1]\nOBSERVABLE_INCLUDE(24) rec[-1]\nOBSERVABLE_INCLUDE(25) rec[-1]\nOBSERVABLE_INCLUDE(26) rec[-1]\nOBSERVABLE_INCLUDE(27) rec[-1]\nOBSERVABLE_INCLUDE(28) rec[-1]\nOBSERVABLE_INCLUDE(29) rec[-1]\nOBSERVABLE_INCLUDE(30) rec[-1]\nOBSERVABLE_INCLUDE(31) rec[-1]\nOBSERVABLE_INCLUDE(32) rec[-1]\nOBSERVABLE_INCLUDE(33) rec[-1]\nOBSERVABLE_INCLUDE(34) rec[-1]\nOBSERVABLE_INCLUDE(35) rec[-1]\nOBSERVABLE_INCLUDE(36) rec[-1]\nOBSERVABLE_INCLUDE(37) rec[-1]\nOBSERVABLE_INCLUDE(38) rec[-1]\nOBSERVABLE_INCLUDE(39) rec[-1]\nOBSERVABLE_INCLUDE(40) rec[-1]\nOBSERVABLE_INCLUDE(41) rec[-1]\nOBSERVABLE_INCLUDE(42) rec[-1]\nOBSERVABLE_INCLUDE(43) rec[-1]\nOBSERVABLE_INCLUDE(44) rec[-1]\nOBSERVABLE_INCLUDE(45) rec[-1]\nOBSERVABLE_INCLUDE(46) rec[-1]\nOBSERVABLE_INCLUDE(47) rec[-1]\nOBSERVABLE_INCLUDE(48) rec[-1]\nOBSERVABLE_INCLUDE(49) rec[-1]\nOBSERVABLE_INCLUDE(50) rec[-1]\nOBSERVABLE_INCLUDE(51) rec[-1]\nOBSERVABLE_INCLUDE(52) rec[-1]\nOBSERVABLE_INCLUDE(53) rec[-1]\nOBSERVABLE_INCLUDE(54) rec[-1]\nOBSERVABLE_INCLUDE(55) rec[-1]\nOBSERVABLE_INCLUDE(56) rec[-1]\nOBSERVABLE_INCLUDE(57) rec[-1]\nOBSERVABLE_INCLUDE(58) rec[-1]\nOBSERVABLE_INCLUDE(59) rec[-1]\nOBSERVABLE_INCLUDE(60) rec[-1]\nOBSERVABLE_INCLUDE(61) rec[-1]\nOBSERVABLE_INCLUDE(62) rec[-1]\nOBSERVABLE_INCLUDE(63) rec[-1]\nOBSERVABLE_INCLUDE(64) rec[-1]\n",
        "shots_hex": "00"
      },
      "expected": {
        "outcome": "rejection",
        "error_code": "unsupported_circuit",
        "exit_code": 2,
        "message_contains": "1..=64 observables",
        "predictions_written": false,
        "stats_written": false
      }
    },
    {
      "id": "mle-solve-timeout",
      "description": "A zero per-shot timeout stops the MLE solve path after one attempted shot: diagnostic statistics are written, predictions are withheld, exit code 3.",
      "decoders": ["envelope-mle"],
      "dataset": {
        "type": "inline",
        "circuit": "QUBIT_COORDS(0,0) 0\nQUBIT_COORDS(1,0) 1\nR 0 1\nTICK[rstim:logical_flip_point]\nX_ERROR(0.1) 0\nX_ERROR(0.01) 1\nLOSS(0.1) 0\nH 0\nH 0\nCX 0 1\nX_ERROR(0.02) 0\nLOSS(0.1) 1\nML 0 1\nDETECTOR(0,0,0) rec[-3]\nDETECTOR(1,0,0) rec[-1]\nOBSERVABLE_INCLUDE(0) rec[-3]\n",
        "shots_hex": "02010100"
      },
      "arguments": {"shot_timeout_ms": 0},
      "expected": {
        "outcome": "rejection",
        "error_code": "decode_timeout",
        "exit_code": 3,
        "message_contains": "timed out",
        "predictions_written": false,
        "stats_written": true,
        "stats": {
          "shot_count": 4,
          "attempted_shot_count": 1,
          "timeout_count": 1,
          "infeasible_shot_count": 0
        }
      }
    },
    {
      "id": "mle-infeasible-shot",
      "description": "A shot with no feasible correction stops the batch: diagnostic statistics are written, predictions are withheld, exit code 3. Distinct from timeout and unsupported input.",
      "decoders": ["envelope-mle"],
      "dataset": {
        "type": "inline",
        "circuit": "QUBIT_COORDS(0,0) 0\nQUBIT_COORDS(1,0) 1\nR 0 1\nTICK[rstim:logical_flip_point]\nX_ERROR(0.1) 0\nLOSS(0.1) 0\nH 0\nH 0\nCX 0 1\nLOSS(0.1) 1\nML 0 1\nDETECTOR(0,0,0) rec[-3]\nDETECTOR(1,0,0) rec[-1]\nOBSERVABLE_INCLUDE(0) rec[-3]\n",
        "shots_hex": "08"
      },
      "expected": {
        "outcome": "rejection",
        "error_code": "decode_infeasible",
        "exit_code": 3,
        "message_contains": "no feasible correction",
        "predictions_written": false,
        "stats_written": true,
        "stats": {
          "shot_count": 1,
          "attempted_shot_count": 1,
          "timeout_count": 0,
          "infeasible_shot_count": 1
        }
      }
    },
    {
      "id": "mle-unsupported-detector-layout",
      "description": "A detector layout the MLE conditioning cannot represent is unsupported input, not a decode failure.",
      "decoders": ["envelope-mle"],
      "dataset": {
        "type": "inline",
        "circuit": "QUBIT_COORDS(0,0) 0\nQUBIT_COORDS(1,0) 1\nR 0 1\nTICK[rstim:logical_flip_point]\nX_ERROR(0.1) 0\nX_ERROR(0.01) 1\nLOSS(0.1) 0\nH 0\nH 0\nCX 0 1\nX_ERROR(0.02) 0\nLOSS(0.1) 1\nML 0 1\nDETECTOR(0,0,0) rec[-4]\nDETECTOR(1,0,0) rec[-1]\nOBSERVABLE_INCLUDE(0) rec[-3]\n",
        "shots_hex": "02010100"
      },
      "expected": {
        "outcome": "rejection",
        "error_code": "unsupported_circuit",
        "exit_code": 2,
        "predictions_written": false,
        "stats_written": false
      }
    },
    {
      "id": "matching-rejects-shot-timeout-argument",
      "description": "--shot-timeout-ms applies to the MLE solve path only; envelope-matching rejects it as invalid arguments.",
      "decoders": ["envelope-matching"],
      "dataset": {
        "type": "inline",
        "circuit": "QUBIT_COORDS(0,0) 0\nQUBIT_COORDS(1,0) 1\nR 0 1\nTICK[rstim:logical_flip_point]\nX_ERROR(0.1) 0\nX_ERROR(0.01) 1\nLOSS(0.1) 0\nH 0\nH 0\nCX 0 1\nX_ERROR(0.02) 0\nLOSS(0.1) 1\nML 0 1\nDETECTOR(0,0,0) rec[-3]\nDETECTOR(1,0,0) rec[-1]\nOBSERVABLE_INCLUDE(0) rec[-3]\n",
        "shots_hex": "02010100"
      },
      "arguments": {"shot_timeout_ms": 1},
      "expected": {
        "outcome": "rejection",
        "error_code": "invalid_arguments",
        "exit_code": 2,
        "message_contains": "--shot-timeout-ms",
        "predictions_written": false,
        "stats_written": false
      }
    },
    {
      "id": "detector-mode-dataset-rejection",
      "description": "Detector-mode datasets are outside the public measurement-row contract.",
      "decoders": ["envelope-matching"],
      "dataset": {
        "type": "inline",
        "circuit": "QUBIT_COORDS(0,0) 0\nQUBIT_COORDS(1,0) 1\nR 0 1\nTICK[rstim:logical_flip_point]\nX_ERROR(0.1) 0\nX_ERROR(0.01) 1\nLOSS(0.1) 0\nH 0\nH 0\nCX 0 1\nX_ERROR(0.02) 0\nLOSS(0.1) 1\nML 0 1\nDETECTOR(0,0,0) rec[-3]\nDETECTOR(1,0,0) rec[-1]\nOBSERVABLE_INCLUDE(0) rec[-3]\n",
        "shots_hex": "02010100",
        "manifest_patch": {"mode": "detectors", "row.kind": "detectors"}
      },
      "expected": {
        "outcome": "rejection",
        "error_code": "unsupported_dataset_mode",
        "exit_code": 2,
        "predictions_written": false,
        "stats_written": false
      }
    },
    {
      "id": "padding-violation-rejection",
      "description": "Non-zero padding bits in the final measurement byte are invalid input.",
      "decoders": ["envelope-matching"],
      "dataset": {
        "type": "inline",
        "circuit": "QUBIT_COORDS(0,0) 0\nQUBIT_COORDS(1,0) 1\nR 0 1\nTICK[rstim:logical_flip_point]\nX_ERROR(0.1) 0\nX_ERROR(0.01) 1\nLOSS(0.1) 0\nH 0\nH 0\nCX 0 1\nX_ERROR(0.02) 0\nLOSS(0.1) 1\nML 0 1\nDETECTOR(0,0,0) rec[-3]\nDETECTOR(1,0,0) rec[-1]\nOBSERVABLE_INCLUDE(0) rec[-3]\n",
        "shots_hex": "82"
      },
      "expected": {
        "outcome": "rejection",
        "error_code": "invalid_dataset",
        "exit_code": 2,
        "message_contains": "padding",
        "predictions_written": false,
        "stats_written": false
      }
    }
  ]
}
