{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "Transferability Rule",
  "description": "What must happen, to which artifacts, under which envelope conditions.\n- `appliesTo` \u2014 the artifacts this rule governs, by `id`. Omit it for a rule about the workflow\n  as a whole.\n- `when` \u2014 the conditions, each citing an envelope constraint by `id` and how the target is\n  tested against it (`inside`/`outside`). **Conjunctive: all of them must hold.** A disjunction\n  is written as two rules.\n- `actions` \u2014 an **OR-set**: any one resolves the rule. Never a sequence, never a combination to\n  apply together.\n- `affects` \u2014 which parts of the annotated document stop working, or work differently, if this\n  rule's outcome is the terminal one.\n- `mandatory` \u2014 whether applying one is required once the conditions hold.\n**Rules are exceptions, not a complete decision table.** A statement's rules say what has to change; anything they do not mention is reusable unchanged. So an artifact with no rule firing for a given target is reused as-is, and a rule stating `reuse-as-is` explicitly is documenting the default rather than adding to it (worth doing where it makes a cascade legible, as UP-WF2's inside/outside pair does, and redundant otherwise). See bblocks://ogc.focal.transferability.transferabilityStatement for why that default is safe here: a statement cannot omit `rules`, so writing none is a deliberate act rather than an oversight.\n**`when` is what makes a rule evaluable rather than merely readable.** `triggeredBy: different-geographic-coverage` is a string a person reads; `when: [{constraint: clms-coverage, test: outside}]` names the boundary, and if that constraint's dimension is spatial its value is a `geo:asWKT` geometry, so a GeoSPARQL engine can settle the question with `geof:sfWithin`. Where the cited constraint is prose-valued (a comparable ecological range), the same structure records a judgement a person still has to make \u2014 which is honest about where automation stops rather than pretending the whole model is machine-decidable.\n`triggeredBy` is kept, but optional: not every evidenced FOCAL case has a constraint precise enough to cite, and forcing one would mean inventing boundaries. At least one of `when` or `triggeredBy` is required, so no rule is left with no stated condition at all. Prefer `when`.\n",
  "allOf": [
    {
      "$ref": "https://ogcincubator.github.io/bblocks-focal/build/annotated/focal/transferability/notes/schema.json"
    },
    {
      "type": "object",
      "required": [
        "actions"
      ],
      "anyOf": [
        {
          "required": [
            "when"
          ]
        },
        {
          "required": [
            "triggeredBy"
          ]
        }
      ],
      "properties": {
        "appliesTo": {
          "type": "array",
          "minItems": 1,
          "items": {
            "$ref": "https://opengeospatial.github.io/bblocks/annotated-schemas/ogc-utils/iri-or-curie/schema.json"
          },
          "description": "Identifiers of the artifacts this rule governs, from the enclosing statement's `artifacts`. Several artifacts may share one rule \u2014 FP-WF2's four Czechia-specific reference files do. Omit for a rule that applies to the workflow as a whole.\n",
          "x-jsonld-id": "https://w3id.org/ogc/hosted/focal/transferability/properties/appliesTo",
          "x-jsonld-type": "@id",
          "x-jsonld-container": "@set"
        },
        "when": {
          "type": "array",
          "minItems": 1,
          "items": {
            "$ref": "#/$defs/condition"
          },
          "description": "The conditions under which this rule fires, ANDed together. Two conditions here mean both must hold; two rules mean either suffices.\n",
          "x-jsonld-id": "https://w3id.org/ogc/hosted/focal/transferability/properties/when",
          "x-jsonld-container": "@set"
        },
        "triggeredBy": {
          "type": "string",
          "examples": [
            "different-geographic-coverage",
            "different-ecological-range",
            "different-climate-regime",
            "different-dataset"
          ],
          "description": "A coarse statement of what fires this rule, for cases where no envelope constraint can be cited precisely. Open vocabulary \u2014 see bblocks://ogc.focal.transferability.vocab.\n",
          "x-jsonld-id": "https://w3id.org/ogc/hosted/focal/transferability/properties/triggeredBy",
          "x-jsonld-type": "@id",
          "x-jsonld-base": "https://w3id.org/ogc/hosted/focal/transferability/triggers/"
        },
        "affects": {
          "type": "array",
          "minItems": 1,
          "items": {
            "type": "string",
            "format": "json-pointer",
            "pattern": "^/"
          },
          "description": "JSON Pointers (RFC 6901) to the parts of the annotated document this rule's outcome bears on \u2014 for the CWL profile in bblocks://ogc.focal.transferability.workflow, typically `/steps/<id>` or `/outputs/<id>`, though the pointer carries no CWL assumption.\n**This is what `component-not-executable` needs to be actionable.** Without it the action names no component, so \"hot-spot characterization cannot run\" and \"the workflow cannot run\" are the same statement, and per-artifact answers cannot be assembled into the per-workflow verdict (*runs as is* / *runs in part* / *cannot run here*) that a deployment platform actually has to produce. UP-WF2 is the evidenced case: losing CLMS costs it one step, not the run.\nOptional, and meaningful for any action: a `replace-with-local-equivalent` rule may equally name the outputs whose quality changes. Omit it rather than guess, the same way `artifactRef` is omitted where no real Application Package exists to point into.\n",
          "x-jsonld-id": "https://w3id.org/ogc/hosted/focal/transferability/properties/affects",
          "x-jsonld-container": "@set"
        },
        "actions": {
          "type": "array",
          "minItems": 1,
          "items": {
            "type": "string",
            "examples": [
              "reuse-as-is",
              "replace-with-local-equivalent",
              "retrain",
              "replace-with-alternative-published-model",
              "component-not-executable"
            ]
          },
          "description": "One or more actions, any of which resolves the rule \u2014 an OR-set, not a required combination. No evidenced case needs two applied simultaneously.\n",
          "x-jsonld-id": "https://w3id.org/ogc/hosted/focal/transferability/properties/actions",
          "x-jsonld-type": "@id",
          "x-jsonld-container": "@set",
          "x-jsonld-base": "https://w3id.org/ogc/hosted/focal/transferability/actions/"
        },
        "mandatory": {
          "type": "boolean",
          "default": true,
          "description": "Whether applying one of `actions` is mandatory once the conditions hold. Defaults to true. When false, skipping is allowed but degrades result quality or trust rather than blocking execution \u2014 say what degrades in `transferabilityNotes`.\nSays nothing on a rule whose only action is `reuse-as-is`: \"you must do nothing\" and \"you may do nothing\" are the same instruction. Omit it there.\nNamed `mandatory` rather than `required`: a data property spelled like a JSON Schema keyword reads as the keyword, and this one sits beside real `required` lists.\n",
          "x-jsonld-id": "https://w3id.org/ogc/hosted/focal/transferability/properties/mandatory"
        }
      }
    }
  ],
  "$defs": {
    "condition": {
      "title": "Envelope condition",
      "type": "object",
      "required": [
        "constraint",
        "test"
      ],
      "additionalProperties": false,
      "properties": {
        "constraint": {
          "$ref": "https://opengeospatial.github.io/bblocks/annotated-schemas/ogc-utils/iri-or-curie/schema.json",
          "description": "The `id` of an envelope constraint in the enclosing statement's `envelope`, written the same way it is declared there \u2014 an IRI, a CURIE, or a bare local name. Whichever form is used, the constraint must still be declared in the statement, so a reader has the extent in front of them rather than a bare identifier: a global identifier makes a boundary shareable, it does not make it fetchable.\n",
          "x-jsonld-id": "https://w3id.org/ogc/hosted/focal/transferability/properties/constraint",
          "x-jsonld-type": "@id"
        },
        "test": {
          "type": "string",
          "examples": [
            "outside",
            "inside"
          ],
          "description": "How the target is compared against that constraint. `outside` is the condition under which almost every evidenced FOCAL rule fires; `inside` states the complementary case, which is what lets a cascade be written without losing its connecting condition \u2014 UP-WF2's \"reuse within the domain, otherwise substitute or fail\" is two rules over the same constraint. Open vocabulary \u2014 see bblocks://ogc.focal.transferability.vocab, where each term also records how it is evaluated per dimension.\n",
          "x-jsonld-id": "https://w3id.org/ogc/hosted/focal/transferability/properties/test",
          "x-jsonld-type": "@id",
          "x-jsonld-base": "https://w3id.org/ogc/hosted/focal/transferability/tests/"
        }
      }
    }
  },
  "x-jsonld-prefixes": {
    "focal-transf-prop": "https://w3id.org/ogc/hosted/focal/transferability/properties/"
  }
}