# =====================================================================
#  Decision Model Ontology  (dm:)
#  A vendor-neutral vocabulary for typed decisions and their execution.
#
#  Layer 1 of 3:
#      decision-making.ttl   general semantics of decisions   <- THIS FILE
#      jev.ttl               native Jev / System One vocabulary
#      inference.ttl         semantic bridge between the two
#
#  This file MUST NOT reference any provider vocabulary. It imports
#  nothing and is usable on its own.
# =====================================================================

@prefix dm:      <https://www.openlinksw.com/ontology/decision-model#> .
@prefix rdf:     <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs:    <http://www.w3.org/2000/01/rdf-schema#> .
@prefix owl:     <http://www.w3.org/2002/07/owl#> .
@prefix xsd:     <http://www.w3.org/2001/XMLSchema#> .
@prefix skos:    <http://www.w3.org/2004/02/skos/core#> .
@prefix dcterms: <http://purl.org/dc/terms/> .
@prefix prov:    <http://www.w3.org/ns/prov#> .
@prefix sh:      <http://www.w3.org/ns/shacl#> .
@prefix vann:    <http://purl.org/vocab/vann/> .

# ---------------------------------------------------------------------
# Ontology header
# ---------------------------------------------------------------------

<https://www.openlinksw.com/ontology/decision-model>
    a owl:Ontology ;
    owl:versionIRI <https://www.openlinksw.com/ontology/decision-model/1.0.0> ;
    dcterms:title "Decision Model Ontology"@en ;
    dcterms:description """A vendor-neutral RDF/OWL vocabulary for describing typed decisions -- classifications, ratings, determinations, estimates, extractions and rankings -- independently of the inference backend that executes them;

The ontology separates three things that are routinely conflated in AI/ML tooling:

  1. WHAT is being decided  (dm:Decision and its subclasses)
  2. HOW it is executed     (dm:DecisionBinding, dm:Executor, dm:Model, dm:Provider)
  3. WHAT was decided       (dm:DecisionResult)

Keeping these apart is what allows one decision definition to be executed by several backends, compared across them, and audited over time without rewriting the decision itself."""@en ;
    dcterms:creator <https://www.openlinksw.com#this>, <https://github.com/danielhmills#this> ;
    dcterms:created "2026-09-20"^^xsd:date ;    
    dcterms:license <https://creativecommons.org/licenses/by/4.0/> ;
    vann:preferredNamespacePrefix "dm" ;
    vann:preferredNamespaceUri "https://www.openlinksw.com/ontology/decision-model#" ;
    rdfs:seeAlso <https://www.openlinksw.com/ontology/jev/inference> ;
    skos:scopeNote """Design rules observed throughout this file:

  * rdfs:range is declared ONLY on properties whose values are structural
    nodes minted by this ontology. Properties that point into a user's own
    ontology (dm:option, dm:about, dm:selectedValue, dm:input, dm:context,
    dm:inputResource, dm:scoredValue, dm:generatedBy) carry NO range, so
    that dragging an arbitrary skos:Concept, owl:Class or owl:NamedIndividual
    into a decision never rewrites the semantics of the term being dragged.

  * rdfs:domain is declared where the subject is necessarily a class of this
    ontology, because that entailment is intended and additive.

  * Only three OWL 2 constructs beyond RDFS are used: owl:disjointWith,
    owl:propertyChainAxiom and owl:AnnotationProperty. Every mapping that
    carries reasoning weight stays inside the RDFS-Plus subset supported by
    mainstream stores, including OpenLink Virtuoso's rule sets."""@en .

# =====================================================================
# 1. DECISIONS -- what is being asked
# =====================================================================

dm:Decision
    a owl:Class ;
    rdfs:label "Decision"@en ;
    skos:altLabel "decision task"@en ;
    rdfs:comment """A bounded decision, prediction, classification, estimate or determination that an inference system is asked to make.

An instance of dm:Decision is the SPECIFICATION of a question -- not the act of answering it and not the answer. The specification is stable, citable and reusable: it is the unit that is version-controlled, reviewed by domain experts, bound to one or more backends, and compared across them."""@en ;
    skos:scopeNote "Instances are usually named IRIs in an application namespace, e.g. :ticketCategory. The answer is a dm:DecisionResult, which is disjoint from dm:Decision."@en ;
    owl:disjointWith dm:DecisionResult .

dm:CategoricalDecision
    a owl:Class ;
    rdfs:subClassOf dm:Decision ;
    rdfs:label "Categorical Decision"@en ;
    rdfs:comment "A decision that selects exactly one item from a finite set of unordered alternatives. The admissible alternatives are given by dm:option, or by dm:optionsFrom when the set is large enough to be better named than enumerated."@en .

dm:OrdinalDecision
    a owl:Class ;
    rdfs:subClassOf dm:Decision ;
    rdfs:label "Ordinal Decision"@en ;
    rdfs:comment "A decision that places its subject on an ordered scale, by selecting a scale value or by estimating a position between scale values. The scale is given by dm:scale (reusable) or by dm:scalePoint (inline)."@en ;
    skos:scopeNote "Ordinal is not categorical-with-an-order: the answer space of an ordinal decision is an interval of the scale's index line, so a result may legitimately fall between two named levels (e.g. 1.43 on a 0..2 scale). That is why the two classes are declared disjoint."@en .

dm:BinaryDecision
    a owl:Class ;
    rdfs:subClassOf dm:Decision ;
    rdfs:label "Binary Decision"@en ;
    rdfs:comment "A decision that determines a two-valued proposition, preferably reported with the probability that the proposition holds rather than as a bare boolean."@en ;
    skos:scopeNote """Deliberately NOT modelled as a categorical decision over {true,false}. The useful output of a binary decision is the probability mass on 'true' (dm:probability on the result), which the consuming application thresholds according to the cost of each kind of error. A categorical decision's useful output is the identity of the selected resource."""@en .

dm:NumericDecision
    a owl:Class ;
    rdfs:subClassOf dm:Decision ;
    rdfs:label "Numeric Decision"@en ;
    rdfs:comment "A decision that produces a numeric value, optionally bounded by dm:minValue and dm:maxValue and typed by dm:outputDatatype."@en .

dm:MultiLabelDecision
    a owl:Class ;
    rdfs:subClassOf dm:Decision ;
    rdfs:label "Multi-Label Decision"@en ;
    rdfs:comment "A decision that selects zero or more values from a set of alternatives. Its result may carry several dm:selectedValue statements."@en .

dm:RankingDecision
    a owl:Class ;
    rdfs:subClassOf dm:Decision ;
    rdfs:label "Ranking Decision"@en ;
    rdfs:comment "A decision that orders candidate values. Its result is normally expressed as a set of dm:outcomeScore nodes whose dm:probability or score induces the order."@en .

dm:ExtractionDecision
    a owl:Class ;
    rdfs:subClassOf dm:Decision ;
    rdfs:label "Extraction Decision"@en ;
    rdfs:comment "A decision that identifies or extracts entities or values from the input data rather than choosing among alternatives supplied in advance."@en .

# Disjointness is asserted only among the three core types, whose answer
# spaces are provably different in kind. MultiLabel, Ranking, Extraction and
# Numeric are left non-disjoint on purpose: real tasks legitimately straddle
# them (an extraction that is also multi-label; a ranking scored numerically).
dm:CategoricalDecision owl:disjointWith dm:OrdinalDecision, dm:BinaryDecision .
dm:OrdinalDecision     owl:disjointWith dm:BinaryDecision .

# ---------------------------------------------------------------------
# 1.1 Decision specification properties
# ---------------------------------------------------------------------

dm:question
    a owl:DatatypeProperty ;
    rdfs:label "question"@en ;
    rdfs:domain dm:Decision ;
    rdfs:comment "The human-readable question or instruction put to the inference system."@en ;
    skos:scopeNote "The value is a literal. A plain or language-tagged string suits most decisions; a backend that accepts structured instructions may carry an rdf:JSON literal here."@en .

dm:about
    a owl:ObjectProperty ;
    rdfs:label "about"@en ;
    rdfs:domain dm:Decision ;
    rdfs:comment "The RDF class, property or concept that the decision is about -- the subject matter, not the input instance."@en ;
    skos:scopeNote "No rdfs:range: the value is a term from the user's own ontology (an owl:Class, an rdf:Property, a skos:Concept or any other resource) and must not acquire a type from being referenced here."@en .

dm:option
    a owl:ObjectProperty ;
    rdfs:label "option"@en ;
    rdfs:domain dm:Decision ;
    rdfs:comment "An admissible alternative for this decision, identified by IRI so that the answer keeps its RDF identity."@en ;
    skos:scopeNote """No rdfs:range, by design. A dm:option may point at a skos:Concept, an owl:NamedIndividual, an owl:Class or any other resource, which is what lets a user drag terms out of an arbitrary loaded ontology and drop them into a decision.

The label and definition of an option are NOT restated here: they are read from the option's own annotations (skos:prefLabel / skos:definition / rdfs:label / rdfs:comment) in whichever ontology governs it. One curated definition therefore serves every decision, every backend and every request built from it."""@en .

dm:optionsFrom
    a owl:ObjectProperty ;
    rdfs:label "options from"@en ;
    rdfs:domain dm:Decision ;
    rdfs:comment "A resource that denotes the set from which the options are drawn -- typically a skos:ConceptScheme, a skos:Collection or an owl:Class -- used when enumerating each option with dm:option would be impractical."@en ;
    skos:scopeNote "A client expands this to concrete options with a SPARQL query before execution. Enumeration (dm:option) and intension (dm:optionsFrom) may be combined."@en .

dm:scale
    a owl:ObjectProperty ;
    rdfs:label "scale"@en ;
    rdfs:domain dm:Decision ;
    rdfs:range dm:Scale ;
    rdfs:comment "The reusable ordered scale that this decision is measured against."@en .

dm:scalePoint
    a owl:ObjectProperty ;
    rdfs:label "scale point"@en ;
    rdfs:domain dm:Decision ;
    rdfs:range dm:ScaleValue ;
    rdfs:comment "A scale value admissible for this decision, stated directly on the decision."@en ;
    owl:propertyChainAxiom ( dm:scale dm:scaleValue ) ;
    skos:scopeNote """Shortcut. The property chain makes every value of a referenced dm:Scale a dm:scalePoint of the decision, so the reusable form (dm:scale) and the inline form (dm:scalePoint) answer the same query.

The chain is the only inference in this ontology that needs an OWL 2 reasoner; it is a convenience, not a load-bearing mapping. Stores limited to RDFS-Plus can materialise it with:
  CONSTRUCT { ?d dm:scalePoint ?v } WHERE { ?d dm:scale/dm:scaleValue ?v }"""@en .

dm:minValue
    a owl:DatatypeProperty ;
    rdfs:label "minimum value"@en ;
    rdfs:domain dm:Decision ;
    rdfs:comment "The lowest value the decision may produce, inclusive."@en .

dm:maxValue
    a owl:DatatypeProperty ;
    rdfs:label "maximum value"@en ;
    rdfs:domain dm:Decision ;
    rdfs:comment "The highest value the decision may produce, inclusive."@en .

dm:outputDatatype
    a owl:ObjectProperty ;
    rdfs:label "output datatype"@en ;
    rdfs:domain dm:Decision ;
    rdfs:range rdfs:Datatype ;
    rdfs:comment "The datatype expected of a literal-valued result, e.g. xsd:decimal or xsd:integer."@en ;
    skos:scopeNote "Applies to decisions whose answer is a literal (numeric, ordinal position). Decisions whose answer is a resource describe their output semantics with dm:option, dm:optionsFrom or dm:scale instead."@en .

dm:input
    a owl:ObjectProperty ;
    rdfs:label "input"@en ;
    rdfs:domain dm:Decision ;
    rdfs:comment "A description of an input the decision requires: typically an owl:Class whose instances are admissible inputs, or a SHACL node shape they must satisfy."@en ;
    skos:scopeNote "This is the input TYPE required by the specification. The individual input actually judged is recorded on the result with dm:inputResource."@en .

dm:context
    a owl:ObjectProperty ;
    rdfs:label "context"@en ;
    rdfs:domain dm:Decision ;
    rdfs:comment "An additional resource -- a policy, a named graph, a reference dataset -- that must be supplied alongside the input for the decision to be answerable."@en .

# =====================================================================
# 2. ORDERED SCALES -- reusable ordinal semantics
# =====================================================================

dm:Scale
    a owl:Class ;
    rdfs:label "Scale"@en ;
    rdfs:comment "An ordered set of named values against which ordinal decisions are measured, e.g. Informational < Low < Medium < High < Critical."@en ;
    skos:scopeNote "A scale is a first-class, citable resource so that one organisational definition of severity, risk or priority can be shared by every decision, every backend and every report that uses it."@en .

dm:ScaleValue
    a owl:Class ;
    rdfs:label "Scale Value"@en ;
    rdfs:comment "One named point on a dm:Scale, carrying its rank via dm:position."@en ;
    skos:scopeNote "A scale value is frequently also a skos:Concept in a domain scheme; nothing here prevents that, and the two typings are complementary."@en .

dm:scaleValue
    a owl:ObjectProperty ;
    rdfs:label "scale value"@en ;
    rdfs:domain dm:Scale ;
    rdfs:range dm:ScaleValue ;
    rdfs:comment "A value belonging to this scale."@en .

dm:position
    a owl:DatatypeProperty ;
    rdfs:label "position"@en ;
    rdfs:domain dm:ScaleValue ;
    rdfs:range xsd:integer ;
    rdfs:comment "The rank of this value within its scale, ascending, conventionally zero-based."@en ;
    skos:scopeNote "The integer is the ordering key, not a measurement. It is what lets a client serialise the scale in the right order for a backend, and what lets SPARQL sort, threshold and compare results across backends."@en .

# =====================================================================
# 3. EXECUTION -- how a decision is carried out
#    Nothing in this section is part of the intrinsic semantics of a
#    decision. A decision may have zero, one or many bindings.
# =====================================================================

dm:DecisionBinding
    a owl:Class ;
    rdfs:label "Decision Binding"@en ;
    rdfs:comment "An association between one dm:Decision and one execution backend able to answer it. A decision may have any number of alternative bindings; adding, removing or re-pointing a binding leaves the decision definition untouched."@en ;
    skos:scopeNote "Bindings are the unit of comparison in an evaluation harness: the same decision executed through two bindings yields two dm:DecisionResult sets over the same dm:inputResource, which is exactly the shape a benchmark, a canary rollout or a cost/quality trade-off needs."@en .

dm:Executor
    a owl:Class ;
    rdfs:subClassOf prov:SoftwareAgent ;
    rdfs:label "Executor"@en ;
    rdfs:comment "A system that can execute decisions: a hosted inference API, a local runtime, a SPARQL endpoint, a rules engine or any future backend."@en .

dm:Model
    a owl:Class ;
    rdfs:label "Model"@en ;
    rdfs:comment "A concrete model artefact or endpoint-addressable model version used by an executor."@en ;
    skos:scopeNote "A model is an artefact, not an agent: it is the thing an executor uses. Local files and remote endpoints are equally first-class -- see dm:modelLocation."@en .

dm:Provider
    a owl:Class ;
    rdfs:subClassOf prov:Agent ;
    rdfs:label "Provider"@en ;
    rdfs:comment "The organisation or service that supplies an executor or a model."@en ;
    skos:scopeNote "This ontology contains NO enumeration of providers. Providers are ordinary individuals minted in whatever namespace their describer controls."@en .

dm:DecisionPrimitive
    a owl:Class ;
    rdfs:label "Decision Primitive"@en ;
    rdfs:comment "A named native operation of a backend that realises a kind of decision, e.g. a constrained-choice head, a regression head, a SPARQL ASK."@en ;
    skos:scopeNote "Declared for providers who wish to model their primitives as individuals. Providers whose primitives are already classes (as Jev's are) annotate those classes with dm:implementsDecisionType instead; both styles are queryable."@en .

dm:decision
    a owl:ObjectProperty ;
    rdfs:label "decision"@en ;
    rdfs:domain dm:DecisionBinding ;
    rdfs:range dm:Decision ;
    rdfs:comment "The decision this binding can execute."@en .

dm:executor
    a owl:ObjectProperty ;
    rdfs:label "executor"@en ;
    rdfs:domain dm:DecisionBinding ;
    rdfs:range dm:Executor ;
    rdfs:comment "The system that performs the execution."@en .

dm:model
    a owl:ObjectProperty ;
    rdfs:label "model"@en ;
    rdfs:domain dm:DecisionBinding ;
    rdfs:range dm:Model ;
    rdfs:comment "The model the executor uses for this binding."@en .

dm:provider
    a owl:ObjectProperty ;
    rdfs:label "provider"@en ;
    rdfs:range dm:Provider ;
    rdfs:comment "The provider responsible for the subject binding, executor or model."@en ;
    skos:scopeNote "No rdfs:domain: provenance of supply is legitimately stated on a binding, an executor or a model, and declaring one domain would mistype the other two."@en .

dm:modelIdentifier
    a owl:DatatypeProperty ;
    rdfs:label "model identifier"@en ;
    rdfs:domain dm:Model ;
    rdfs:range xsd:string ;
    rdfs:comment "The identifier by which the provider names this model, e.g. a version string or repository id."@en .

dm:modelLocation
    a owl:DatatypeProperty ;
    rdfs:label "model location"@en ;
    rdfs:domain dm:Model ;
    rdfs:range xsd:anyURI ;
    rdfs:comment "Where the model is to be found: an absolute URI for a remote endpoint or a relative reference for a file on the executing host."@en ;
    skos:scopeNote "Relative references such as ./models/ticket-classifier.onnx are valid xsd:anyURI values. Local execution is a first-class case, not a degenerate remote one."@en .

dm:framework
    a owl:DatatypeProperty ;
    rdfs:label "framework"@en ;
    rdfs:domain dm:Model ;
    rdfs:range xsd:string ;
    rdfs:comment "The runtime format or framework of the model, e.g. \"ONNX\", \"GGUF\", \"PyTorch\"."@en ;
    skos:scopeNote "A literal, to stay open-ended. Where a controlled resource is preferred, use dcterms:conformsTo on the model in addition."@en .

dm:primitive
    a owl:AnnotationProperty ;
    rdfs:label "primitive"@en ;
    rdfs:comment "The backend primitive this binding uses, given as the IRI of the term that names it."@en ;
    skos:scopeNote "An annotation property because its object is frequently a vocabulary TERM (a class such as jev:Choice) rather than an individual. This keeps the ontology inside OWL 2 DL without punning and guarantees no class-membership entailment follows from naming a primitive. SPARQL sees the triple normally."@en .

dm:implementsDecisionType
    a owl:AnnotationProperty ;
    rdfs:label "implements decision type"@en ;
    rdfs:comment "Declares that the subject -- a backend primitive, whether modelled as an individual or as a class -- is capable of realising decisions of the named dm:Decision subclass."@en ;
    skos:scopeNote """A CAPABILITY claim, and deliberately distinct from rdfs:subClassOf.

  rdfs:subClassOf answers: what kind of decision is this instance?
  dm:implementsDecisionType answers: what kinds of decision can this backend execute?

The first is instance-level and is what a reasoner acts on. The second is term-level and is what binding validators, capability planners and UIs act on when they offer a user the backends that can run a given decision. Asserting one never implies the other."""@en .

# =====================================================================
# 4. RESULTS -- what was decided
# =====================================================================

dm:DecisionResult
    a owl:Class ;
    rdfs:subClassOf prov:Entity ;
    rdfs:label "Decision Result"@en ;
    rdfs:comment "The outcome of executing a decision against one input on one occasion."@en ;
    skos:scopeNote "Results are modelled separately from decisions so that a decision definition stays stable while an unbounded, timestamped, attributable series of results accumulates against it. That series is the evidence base for calibration studies, drift monitoring and audit."@en .

dm:OutcomeScore
    a owl:Class ;
    rdfs:label "Outcome Score"@en ;
    rdfs:comment "One entry of a per-alternative score or probability distribution attached to a result."@en ;
    skos:scopeNote "Retaining the whole distribution, not only the winner, is what makes a result re-interpretable later: thresholds can be re-tuned, second choices recovered and abstention rates computed without re-running the model."@en .

dm:resultOf
    a owl:ObjectProperty ;
    rdfs:label "result of"@en ;
    rdfs:domain dm:DecisionResult ;
    rdfs:range dm:Decision ;
    rdfs:comment "The decision that was executed to produce this result."@en .

dm:inputResource
    a owl:ObjectProperty ;
    rdfs:label "input resource"@en ;
    rdfs:domain dm:DecisionResult ;
    rdfs:comment "The resource the result concerns -- the individual that was judged."@en ;
    skos:scopeNote "No rdfs:range: the judged resource belongs to the user's own ontology."@en .

dm:selectedValue
    a owl:ObjectProperty ;
    rdfs:label "selected value"@en ;
    rdfs:domain dm:DecisionResult ;
    rdfs:comment "A value chosen by this result, identified by IRI."@en ;
    skos:scopeNote "Points at the option itself (e.g. ex:Technical), never at a copy of its label. The result therefore joins directly to everything the domain ontology already says about that term -- broader concepts, owners, SLAs, translations -- with no string matching anywhere. Repeatable, for multi-label results."@en .

dm:numericValue
    a owl:DatatypeProperty ;
    rdfs:label "numeric value"@en ;
    rdfs:domain dm:DecisionResult ;
    rdfs:comment "The numeric value carried by this result, typically xsd:decimal, xsd:double or xsd:integer."@en ;
    skos:scopeNote "For an ordinal result this is a position on the scale's index line and may fall between two named scale values; the scale itself is reachable through dm:resultOf/dm:scale."@en .

dm:confidence
    a owl:DatatypeProperty ;
    rdfs:label "confidence"@en ;
    rdfs:domain dm:DecisionResult ;
    rdfs:range xsd:decimal ;
    rdfs:comment "The executor's certainty in this result, on a 0 to 1 scale where 1 is maximal certainty."@en ;
    skos:scopeNote """Confidence is NOT the probability of the selected value. It summarises the shape of the distribution -- how peaked it is -- and is used to decide whether to act at all. Probability mass belongs on dm:probability.

Keeping the two apart is what supports a three-way policy: act automatically, seek confirmation, or route to a human. Collapsing them is the most common and most expensive modelling error in this area."""@en .

dm:probability
    a owl:DatatypeProperty ;
    rdfs:label "probability"@en ;
    rdfs:range xsd:decimal ;
    rdfs:comment "The probability mass assigned to the outcome described by the subject, from 0 to 1."@en ;
    skos:scopeNote "No rdfs:domain: used on a dm:OutcomeScore for the mass on one alternative, and directly on a dm:DecisionResult of a binary decision for the probability that the proposition is true."@en .

dm:outcomeScore
    a owl:ObjectProperty ;
    rdfs:label "outcome score"@en ;
    rdfs:domain dm:DecisionResult ;
    rdfs:range dm:OutcomeScore ;
    rdfs:comment "An entry of the per-alternative distribution behind this result."@en .

dm:scoredValue
    a owl:ObjectProperty ;
    rdfs:label "scored value"@en ;
    rdfs:domain dm:OutcomeScore ;
    rdfs:comment "The alternative -- an option or a scale value -- that this entry scores."@en ;
    skos:scopeNote "No rdfs:range: the alternative is a term from the user's own ontology."@en .

dm:generatedBy
    a owl:ObjectProperty ;
    rdfs:label "generated by"@en ;
    rdfs:domain dm:DecisionResult ;
    rdfs:comment "The executor or model that produced this result."@en ;
    skos:scopeNote """No rdfs:range, because the value is legitimately either a dm:Executor (a prov:SoftwareAgent) or a dm:Model (an artefact), and forcing one would mistype the other.

Full provenance reuses PROV-O directly rather than duplicating it: dm:DecisionResult is a prov:Entity, so prov:generatedAtTime records when, prov:wasAttributedTo records the responsible agent, and prov:wasGeneratedBy records the activity when a request-level activity is modelled."""@en .

# =====================================================================
# 5. SHACL -- optional validation, no reasoning required
#    Shapes are named (no blank nodes) so they can be cited and reused.
# =====================================================================

dm:DecisionResultShape
    a sh:NodeShape ;
    rdfs:label "Decision result shape"@en ;
    sh:targetClass dm:DecisionResult ;
    sh:property dm:ResultOfConstraint, dm:ConfidenceConstraint .

dm:ResultOfConstraint
    a sh:PropertyShape ;
    sh:path dm:resultOf ;
    sh:nodeKind sh:IRI ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
    sh:message "A decision result must cite exactly one decision, by IRI."@en .

dm:ConfidenceConstraint
    a sh:PropertyShape ;
    sh:path dm:confidence ;
    sh:datatype xsd:decimal ;
    sh:minInclusive 0.0 ;
    sh:maxInclusive 1.0 ;
    sh:maxCount 1 ;
    sh:message "Confidence must be a single decimal between 0 and 1."@en .

dm:OutcomeScoreShape
    a sh:NodeShape ;
    rdfs:label "Outcome score shape"@en ;
    sh:targetClass dm:OutcomeScore ;
    sh:property dm:ScoredValueConstraint, dm:ProbabilityConstraint .

dm:ScoredValueConstraint
    a sh:PropertyShape ;
    sh:path dm:scoredValue ;
    sh:nodeKind sh:IRI ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
    sh:message "An outcome score must score exactly one alternative, by IRI."@en .

dm:ProbabilityConstraint
    a sh:PropertyShape ;
    sh:path dm:probability ;
    sh:datatype xsd:decimal ;
    sh:minInclusive 0.0 ;
    sh:maxInclusive 1.0 ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
    sh:message "An outcome score must carry exactly one probability between 0 and 1."@en .

dm:ScaleValueShape
    a sh:NodeShape ;
    rdfs:label "Scale value shape"@en ;
    sh:targetClass dm:ScaleValue ;
    sh:property dm:PositionConstraint .

dm:PositionConstraint
    a sh:PropertyShape ;
    sh:path dm:position ;
    sh:datatype xsd:integer ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
    sh:message "Every scale value must carry exactly one integer position."@en .

dm:DecisionBindingShape
    a sh:NodeShape ;
    rdfs:label "Decision binding shape"@en ;
    sh:targetClass dm:DecisionBinding ;
    sh:property dm:BoundDecisionConstraint .

dm:BoundDecisionConstraint
    a sh:PropertyShape ;
    sh:path dm:decision ;
    sh:nodeKind sh:IRI ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
    sh:message "A binding must bind exactly one decision, by IRI."@en .
