{
  "format": "vercy-enterprise-allocation-fixtures/v1",
  "candidateName": "Reusable Training Recipe",
  "cases": [
    {
      "id": "shared-recipe-two-runs",
      "kind": "positive",
      "conditional": true,
      "blockedBy": "allocationState=unassigned",
      "gatesProfile": false,
      "invariants": [
        3,
        4,
        5,
        6
      ],
      "pins": {
        "recipeRevision": "rev-1",
        "runs": [
          "R1",
          "R2"
        ],
        "effectiveConfigDigests": [
          "sha256:cf01",
          "sha256:cf02"
        ]
      },
      "input": "Runs R1 and R2 cite future recipe revision rev-1 with distinct resolved effective-configuration digests.",
      "expect": "Accepted only after allocation; rev-1 is shared while R1 and R2 remain distinct runs and no run fact becomes a recipe property."
    },
    {
      "id": "recipe-is-run",
      "kind": "negative",
      "conditional": true,
      "blockedBy": "allocationState=unassigned",
      "gatesProfile": false,
      "invariants": [
        5,
        6
      ],
      "pins": {
        "recipeRevision": "rev-1",
        "run": "R1"
      },
      "input": "Recipe revision rev-1 and execution R1 are assigned one identifier and lifecycle.",
      "expect": "Rejected; reusable specification and execution occurrence cannot share identity or lifecycle."
    },
    {
      "id": "recipe-identifier-assumed",
      "kind": "negative",
      "invariants": [
        16
      ],
      "conditional": false,
      "gatesProfile": false,
      "pins": {
        "recipeRef": "any",
        "allocationState": "unassigned"
      },
      "input": "An artifact references Reusable Training Recipe as an identified object while allocationState is 'unassigned'.",
      "expect": "The reference is rejected. The candidate has no assigned identifier and no present independent identity; no identifier may be guessed or minted."
    },
    {
      "id": "recipe-revision-draft-without-effective-from",
      "kind": "positive",
      "conditional": true,
      "blockedBy": "allocationState=unassigned",
      "gatesProfile": false,
      "invariants": [
        16
      ],
      "pins": {
        "revisionStatus": "draft",
        "effectiveFrom": null,
        "currentRevisionRef": null
      },
      "input": "A recipe in status 'draft' holds a revision in status 'draft' with no effectiveFrom and no currentRevisionRef.",
      "expect": "Accepted once the candidate is allocated. effectiveFrom is required only from status 'effective' onward and currentRevisionRef only when some revision is effective."
    },
    {
      "id": "recipe-superseded-without-successor",
      "kind": "negative",
      "conditional": true,
      "blockedBy": "allocationState=unassigned",
      "gatesProfile": false,
      "invariants": [
        16
      ],
      "pins": {
        "revisionStatus": "superseded",
        "successorRef": null,
        "effectiveTo": null
      },
      "input": "A recipe revision is set to 'superseded' with no successorRef and no effectiveTo.",
      "expect": "Rejected. successorRef and effectiveTo are required when status is 'superseded'; a retired or superseded revision identifier remains resolvable and is never recycled."
    },
    {
      "id": "recipe-inherits-run-outcome",
      "kind": "negative",
      "conditional": true,
      "blockedBy": "allocationState=unassigned",
      "gatesProfile": false,
      "invariants": [
        16
      ],
      "pins": {
        "recipeRevision": "rev-1",
        "runs": [
          "R1",
          "R2"
        ],
        "metricSource": "R1"
      },
      "input": "R1's metrics, checkpoints and compute actuals are recorded against recipe revision rev-1 and read as properties of R2.",
      "expect": "Rejected. Recipe identity never inherits execution outcomes, checkpoints, metrics or resource actuals; two executions of one recipe remain two independently identified runs."
    }
  ]
}
