@prefix sh:    <http://www.w3.org/ns/shacl#> .
@prefix rdf:   <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs:  <http://www.w3.org/2000/01/rdf-schema#> .
@prefix xsd:   <http://www.w3.org/2001/XMLSchema#> .
@prefix owl:   <http://www.w3.org/2002/07/owl#> .
@prefix top:   <https://top.scientix.ai/onto/clinical/v1#> .
@prefix topc:  <https://top.scientix.ai/onto/commons/v1#> .
@prefix fhir:  <http://hl7.org/fhir/> .
@prefix usdm:  <https://www.cdisc.org/standards/usdm/v3#> .
@prefix cdash: <https://www.cdisc.org/standards/cdashig/v2-1#> .
@prefix ngsi:  <https://uri.etsi.org/ngsi-ld/> .

# TOP SHACL shapes · source version: 0.1.4-strawman
# Generated by build_shacl.py · do not edit by hand
# Validates NGSI-LD entity instances against the reference graph's structural constraints

# === Top-level classes ===

top:SponsorShape a sh:NodeShape ;
    sh:targetClass top:Sponsor ;
    sh:name "Sponsor" ;
    sh:description "A Sponsor is an entity responsible for some aspect of a clinical trial: initiation, regulatory accountability, financing, or operational conduct. A study has at least one sponsor of record, who carries regulatory responsibility, and may have additional Sponsor entities for operational delegation (a CRO acting as proxy), financial backing (an academic medical center on an investigator-initiated trial), or co-sponsorship. The Sponsor entity is what the site, the study team, and the regulator interact with day to day. Corporate identity and parent/child structure live on the linked Organization." ;
    sh:property [
        sh:path top:sponsorId ;
        sh:datatype xsd:anyURI ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:description "Globally unique NGSI-LD identifier." ;
        sh:name "sponsorId" ;
    ] ;
    sh:property [
        sh:path top:sponsorName ;
        sh:datatype xsd:string ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:description "Display name." ;
        sh:name "sponsorName" ;
    ] ;
    sh:property [
        sh:path top:legalName ;
        sh:datatype xsd:string ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:description "Registered legal name." ;
        sh:name "legalName" ;
    ] ;
    sh:property [
        sh:path top:sponsorType ;
        sh:datatype xsd:string ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:in ( "PHARMACEUTICAL" "BIOTECH" "ACADEMIC" "GOVERNMENT" "INVESTIGATOR_SPONSOR" "CRO_AS_SPONSOR" "OTHER" ) ;
        sh:description "Sponsor type taxonomy. CRO_AS_SPONSOR covers CROs acting as legal sponsor. INVESTIGATOR_SPONSOR covers academic single-PI sponsorship." ;
        sh:name "sponsorType" ;
    ] ;
    sh:property [
        sh:path top:duns ;
        sh:datatype xsd:string ;
        sh:minCount 0 ;
        sh:maxCount 1 ;
        sh:description "Dun & Bradstreet DUNS number." ;
        sh:name "duns" ;
    ] ;
    sh:property [
        sh:path top:orcid ;
        sh:datatype xsd:string ;
        sh:minCount 0 ;
        sh:maxCount 1 ;
        sh:description "ORCID identifier when sponsor is an individual investigator-sponsor." ;
        sh:name "orcid" ;
    ] ;
    sh:property [
        sh:path top:address ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:description "Headquarters address (line1, line2, city, region, postalCode)." ;
        sh:name "address" ;
    ] ;
    sh:property [
        sh:path top:country ;
        sh:datatype xsd:string ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:description "ISO 3166-1 alpha-3 country code of registration." ;
        sh:name "country" ;
    ] ;
    sh:property [
        sh:path top:phone ;
        sh:datatype xsd:string ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:description "Primary phone." ;
        sh:name "phone" ;
    ] ;
    sh:property [
        sh:path top:email ;
        sh:datatype xsd:string ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:description "Primary email." ;
        sh:name "email" ;
    ] ;
    sh:property [
        sh:path top:website ;
        sh:datatype xsd:anyURI ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:description "Sponsor website." ;
        sh:name "website" ;
    ] ;
    sh:property [
        sh:path top:status ;
        sh:datatype xsd:string ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:in ( "ACTIVE" "INACTIVE" "MERGED" "ACQUIRED" ) ;
        sh:description "Lifecycle state." ;
        sh:name "status" ;
    ] ;
    sh:property [
        sh:path top:isSponsorOfRecord ;
        sh:datatype xsd:boolean ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:description "True if this Sponsor entity holds regulatory accountability per 21 CFR 312 (or equivalent in other jurisdictions). Exactly one Sponsor entity per (Study × RegulatoryAuthorityScope) carries this flag set true; SHACL constraint enforces the rule per jurisdiction." ;
        sh:name "isSponsorOfRecord" ;
    ] ;
    sh:property [
        sh:path top:hasRegulatoryResponsibility ;
        sh:datatype xsd:boolean ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:description "True if this Sponsor entity carries regulatory accountability for the Study. Often the same as isSponsorOfRecord but separated for the rare case where regulatory submission is delegated without transfer of legal sponsor-of-record status." ;
        sh:name "hasRegulatoryResponsibility" ;
    ] ;
    sh:property [
        sh:path top:hasFinancialResponsibility ;
        sh:datatype xsd:boolean ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:description "True if this Sponsor entity provides funding for the Study. Distinct from sponsor-of-record: an academic medical center may be the financial sponsor while a PI is the legal sponsor of an IIT." ;
        sh:name "hasFinancialResponsibility" ;
    ] ;
    sh:property [
        sh:path top:hasOperationalResponsibility ;
        sh:datatype xsd:boolean ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:description "True if this Sponsor entity manages day-to-day operational conduct of the Study. Most common case where a CRO acts as proxy for the sponsor of record." ;
        sh:name "hasOperationalResponsibility" ;
    ] ;
    sh:property [
        sh:path top:isInitiator ;
        sh:datatype xsd:boolean ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:description "True if this Sponsor entity initiated the Study (the entity that triggered the trial). Often the same as the financial sponsor for IITs; often the same as sponsor of record for industry-sponsored trials." ;
        sh:name "isInitiator" ;
    ] ;
    sh:property [
        sh:path top:validFrom ;
        sh:datatype xsd:dateTime ;
        sh:minCount 0 ;
        sh:maxCount 1 ;
        sh:description "Datetime from which this Sponsor relationship is operationally valid. Empty for the initial Sponsor record on a Study. Populated when sponsor transfers occur (M&A, divestiture, asset sale). Uses NGSI-LD's validFrom temporal property semantics." ;
        sh:name "validFrom" ;
    ] ;
    sh:property [
        sh:path top:validUntil ;
        sh:datatype xsd:dateTime ;
        sh:minCount 0 ;
        sh:maxCount 1 ;
        sh:description "Datetime through which this Sponsor relationship was operationally valid. Empty for currently-active Sponsor records. Populated when this Sponsor record is superseded by a new one (M&A transfer, sponsor handoff). Acquiring entities in M&A often do not move all studies at once; the temporal bounds are how this gets modeled honestly." ;
        sh:name "validUntil" ;
    ] ;
    sh:property [
        sh:path top:primaryTherapeuticArea ;
        sh:datatype xsd:string ;
        sh:minCount 0 ;
        sh:maxCount 1 ;
        sh:description "The therapeutic area this Sponsor entity's role on this Study is focused on (oncology, cardiology, infectious disease, rare disease, etc.). Per-Study lens: the Sponsor entity's TA focus for this specific Study, which may be a single area even when the parent Organization's portfolio spans many. The corporate TA spread lives on Organization.primaryTherapeuticAreas. Optional because not every Study has a single dominant TA (basket trials, platform trials)." ;
        sh:name "primaryTherapeuticArea" ;
    ] ;
    sh:property [
        sh:path top:belongsToOrganization ;
        sh:class topc:Organization ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:nodeKind sh:IRI ;
        sh:description "The Organization (corporate / legal entity) this Sponsor entity is backed by. USDM models Sponsor as a type on Organization; TOP keeps Sponsor as an operator-grade top-level and uses this relationship to anchor it to the corporate truth. J&J Innovative Medicine (Sponsor) → belongsToOrganization → Organization{name: 'J&J Innovative Medicine', parentOrganization: J&J}." ;
        sh:name "belongsToOrganization" ;
    ] ;
    sh:property [
        sh:path top:actsOnBehalfOf ;
        sh:class top:Sponsor ;
        sh:minCount 0 ;
        sh:maxCount 1 ;
        sh:nodeKind sh:IRI ;
        sh:description "Self-ref: this Sponsor entity acts as proxy for another Sponsor entity (typically a CRO operational sponsor pointing to the sponsor of record). Empty when this Sponsor is itself the principal. The IQVIA-Sponsor entity that operationally runs Pfizer's Study X has actsOnBehalfOf → Pfizer-Sponsor entity. Distinct from parentSponsor: actsOnBehalfOf is operational delegation, parentSponsor is structural lineage." ;
        sh:name "actsOnBehalfOf" ;
    ] ;
    sh:property [
        sh:path top:parentSponsor ;
        sh:class top:Sponsor ;
        sh:minCount 0 ;
        sh:maxCount 1 ;
        sh:nodeKind sh:IRI ;
        sh:description "Self-ref: this Sponsor entity has a structural parent Sponsor entity. Use only when the per-Org-per-Study lineage is meaningfully different from what Organization.parentOrganization expresses. Three operational cases: (1) M&A successor lineage, where Sponsor B supersedes Sponsor A on the same Study after corporate transfer, B.parentSponsor → A captures the lineage handoff alongside validFrom/validUntil. (2) Lead/co-sponsor in co-development, where the co-sponsor entity points at the lead-sponsor entity. (3) EU CTR Article 74 legal representatives, where a non-EU principal Sponsor designates an EU Sponsor entity as their legal rep, with the EU rep's parentSponsor → principal. Distinct from belongsToOrganization (corporate identity) and actsOnBehalfOf (operational proxy)." ;
        sh:name "parentSponsor" ;
    ] ;
    sh:property [
        sh:path top:regulatoryAuthorityScope ;
        sh:minCount 0 ;
        sh:nodeKind sh:IRI ;
        sh:description "Regulatory authorities this Sponsor entity is the sponsor of record under. Used for multi-jurisdictional studies where different legal entities act as sponsor of record in different jurisdictions (Pfizer Inc under FDA, Pfizer Ireland under EMA, on the same trial). Empty for single-jurisdiction studies; the SHACL 'exactly one sponsor of record per Study' constraint relaxes to per-jurisdiction." ;
        rdfs:comment "TARGET-MISSING: RegulatoryAuthority is a flagged-missing object in OOUX v0.2 (#60)." ;
        sh:name "regulatoryAuthorityScope" ;
    ] ;
    sh:property [
        sh:path top:runs ;
        sh:class top:Study ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:nodeKind sh:IRI ;
        sh:description "The Study this Sponsor entity is scoped to. Each Sponsor entity is per-Organization-per-Study. A company that sponsors many studies has many Sponsor entities, one per study, all linked through belongsToOrganization to the same Organization. The enterprise-level 'all studies Pfizer sponsors' view is served from Organization.playsSponsorRole, not from a single Sponsor entity." ;
        sh:name "runs" ;
    ] ;
    sh:property [
        sh:path top:employs ;
        sh:class top:Person ;
        sh:minCount 0 ;
        sh:nodeKind sh:IRI ;
        sh:description "Employees and contractors. 0..N at the per-Study Sponsor scope: a Sponsor record may be created before staffing assignments are linked. Enterprise-level employee counts roll up via Organization." ;
        sh:name "employs" ;
    ] ;
    sh:property [
        sh:path top:engages ;
        sh:class top:Site ;
        sh:minCount 0 ;
        sh:nodeKind sh:IRI ;
        sh:description "Sites the sponsor has contracted on this Study. 0..N because Sponsor records exist before site selection completes." ;
        sh:name "engages" ;
    ] ;
    sh:property [
        sh:path top:contractsWith ;
        sh:minCount 0 ;
        sh:nodeKind sh:IRI ;
        sh:description "Contracted CROs." ;
        rdfs:comment "TARGET-MISSING: CRO is a flagged-missing object in OOUX v0.2; needs its own catalog entry." ;
        sh:name "contractsWith" ;
    ] ;
    sh:property [
        sh:path top:holds ;
        sh:class top:Contract ;
        sh:minCount 0 ;
        sh:nodeKind sh:IRI ;
        sh:description "All sponsor-side contracts on this Study. 0..N because contracts get signed over time, not at Sponsor-record creation." ;
        sh:name "holds" ;
    ] ;
    sh:property [
        sh:path top:authors ;
        sh:class top:Budget ;
        sh:minCount 0 ;
        sh:nodeKind sh:IRI ;
        sh:description "Budgets the sponsor maintains for this Study. 0..N because budgets may be authored downstream." ;
        sh:name "authors" ;
    ] ;
    sh:property [
        sh:path top:publishesDocument ;
        sh:class topc:Document ;
        sh:minCount 0 ;
        sh:nodeKind sh:IRI ;
        sh:description "Sponsor-controlled documents on this Study (TMF and beyond). 0..N because TMF accumulates over the Study lifecycle. Split from the polysemous 'publishes' verb in v0.1.2 so each predicate carries one target type, preserving clean projection to USDM and FHIR." ;
        sh:name "publishesDocument" ;
    ] ;
    sh:property [
        sh:path top:publishesPublication ;
        sh:minCount 0 ;
        sh:nodeKind sh:IRI ;
        sh:description "Resulting publications (peer-reviewed, conference, preprint). 0..N because most Studies have not produced publications yet at any given moment. Split from the polysemous 'publishes' verb in v0.1.2 so each predicate carries one target type, preserving clean projection to USDM and FHIR." ;
        rdfs:comment "TARGET-MISSING: Publication is not in OOUX v0.2 catalog; treat as Document subtype until specified." ;
        sh:name "publishesPublication" ;
    ] ;
    sh:property [
        sh:path top:files ;
        sh:class top:RegulatorySubmission ;
        sh:minCount 0 ;
        sh:nodeKind sh:IRI ;
        sh:description "Submissions filed by the sponsor on this Study. 0..N because submissions accumulate; a brand-new Sponsor record has zero filings yet." ;
        sh:name "files" ;
    ] ;
    sh:property [
        sh:path top:commissions ;
        sh:class top:Audit ;
        sh:minCount 0 ;
        sh:nodeKind sh:IRI ;
        sh:description "Sponsor-commissioned audits. 0..N: small IITs and academic-sponsored studies may never commission a sponsor-level audit." ;
        sh:name "commissions" ;
    ] ;
    sh:property [
        sh:path top:operatesTrainingProgram ;
        sh:minCount 0 ;
        sh:nodeKind sh:IRI ;
        sh:description "Sponsor training programs on this Study. 0..N at per-Study Sponsor scope. Split from the polysemous 'operates' verb in v0.1.2 so each predicate carries one target type, preserving clean projection to USDM and FHIR." ;
        rdfs:comment "TARGET-MISSING: TrainingProgram is not in OOUX v0.2 catalog; TrainingRecord (#77) covers individual completions; TrainingProgram needs its own entry." ;
        sh:name "operatesTrainingProgram" ;
    ] ;
    sh:property [
        sh:path top:maintains ;
        sh:minCount 0 ;
        sh:nodeKind sh:IRI ;
        sh:description "Standard operating procedures attached to this Study. 0..N: SOPs are typically maintained at the Organization level and inherited." ;
        rdfs:comment "TARGET-MISSING: SOP is not in OOUX v0.2 catalog; treat as Document subtype until specified." ;
        sh:name "maintains" ;
    ] ;
    sh:property [
        sh:path top:operatesSystem ;
        sh:class top:System ;
        sh:minCount 0 ;
        sh:nodeKind sh:IRI ;
        sh:description "Sponsor-owned systems on this Study (EDC, CTMS, eTMF, IRT, safety database). 0..N because system attachment happens during Study startup, not at Sponsor creation. Split from the polysemous 'operates' verb in v0.1.2 so each predicate carries one target type, preserving clean projection to USDM and FHIR." ;
        sh:name "operatesSystem" ;
    ] ;
    sh:property [
        sh:path top:produces ;
        sh:class top:Report ;
        sh:minCount 0 ;
        sh:nodeKind sh:IRI ;
        sh:description "Reports authored by the sponsor on this Study. 0..N because reports accumulate." ;
        sh:name "produces" ;
    ] ;
    sh:property [
        sh:path top:plans ;
        sh:class top:Milestone ;
        sh:minCount 0 ;
        sh:nodeKind sh:IRI ;
        sh:description "Program-level milestones. 0..N because milestones get planned downstream of Sponsor record creation." ;
        sh:name "plans" ;
    ] ;
    sh:property [
        sh:path top:conducts ;
        sh:class top:RiskAssessment ;
        sh:minCount 0 ;
        sh:nodeKind sh:IRI ;
        sh:description "Risk assessments owned by the sponsor on this Study. 0..N because risk assessment is a workflow that produces records over time." ;
        sh:name "conducts" ;
    ] ;
    sh:property [
        sh:path top:executes ;
        sh:class top:CAPA ;
        sh:minCount 0 ;
        sh:nodeKind sh:IRI ;
        sh:description "Sponsor-driven CAPAs. 0..N: most Sponsor records have zero CAPAs at any given snapshot." ;
        sh:name "executes" ;
    ] ;
    sh:property [
        sh:path top:interfacesWith ;
        sh:class top:OversightBody ;
        sh:minCount 0 ;
        sh:nodeKind sh:IRI ;
        sh:description "IRB, EC, DSMB, IDMC engagement. 0..N because oversight engagement gets established during startup, not at Sponsor creation." ;
        sh:name "interfacesWith" ;
    ] ;
    sh:property [
        sh:path top:signs ;
        sh:minCount 0 ;
        sh:nodeKind sh:IRI ;
        sh:description "Data transfer agreements. 0..N because DTAs are signed downstream of Sponsor record creation." ;
        rdfs:comment "TARGET-MISSING: DataTransferAgreement is a Contract subtype not separately enumerated in OOUX v0.2." ;
        sh:name "signs" ;
    ] ;
    sh:property [
        sh:path top:supplies ;
        sh:class top:InvestigationalProduct ;
        sh:minCount 0 ;
        sh:nodeKind sh:IRI ;
        sh:description "IP the sponsor supplies on this Study. 0..N because IP supply chain attaches downstream, and observational studies may not involve IP at all." ;
        sh:name "supplies" ;
    ] ;
    sh:property [
        sh:path top:organizes ;
        sh:class top:Tag ;
        sh:minCount 0 ;
        sh:nodeKind sh:IRI ;
        sh:description "User-defined organizational tags." ;
        sh:name "organizes" ;
    ] .

top:StudyShape a sh:NodeShape ;
    sh:targetClass top:Study ;
    sh:name "Study" ;
    sh:property [
        sh:path top:studyId ;
        sh:datatype xsd:anyURI ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:name "studyId" ;
    ] ;
    sh:property [
        sh:path top:sponsorProtocolId ;
        sh:datatype xsd:string ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:name "sponsorProtocolId" ;
    ] ;
    sh:property [
        sh:path top:clinicalTrialsGovId ;
        sh:datatype xsd:string ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:name "clinicalTrialsGovId" ;
    ] ;
    sh:property [
        sh:path top:studyStatus ;
        sh:datatype xsd:string ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:description "Aligned with FHIR ResearchStudy.status." ;
        sh:name "studyStatus" ;
    ] ;
    sh:property [
        sh:path top:hasProtocol ;
        sh:class top:Protocol ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:nodeKind sh:IRI ;
        sh:name "hasProtocol" ;
    ] ;
    sh:property [
        sh:path top:hasArm ;
        sh:class top:Arm ;
        sh:minCount 1 ;
        sh:nodeKind sh:IRI ;
        sh:name "hasArm" ;
    ] .

top:ProtocolShape a sh:NodeShape ;
    sh:targetClass top:Protocol ;
    sh:name "Protocol" ;
    sh:property [
        sh:path top:protocolVersion ;
        sh:datatype xsd:string ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:description "semver." ;
        sh:name "protocolVersion" ;
    ] ;
    sh:property [
        sh:path top:protocolStatus ;
        sh:datatype xsd:string ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:description "draft, in_review, approved, amended, superseded." ;
        sh:name "protocolStatus" ;
    ] .

top:ArmShape a sh:NodeShape ;
    sh:targetClass top:Arm ;
    sh:name "Arm" ;
    sh:property [
        sh:path top:armName ;
        sh:datatype xsd:string ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:name "armName" ;
    ] ;
    sh:property [
        sh:path top:armType ;
        sh:datatype xsd:string ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:description "treatment, comparator, placebo, control, observational." ;
        sh:name "armType" ;
    ] .

top:SiteShape a sh:NodeShape ;
    sh:targetClass top:Site ;
    sh:name "Site" ;
    sh:property [
        sh:path top:siteId ;
        sh:datatype xsd:anyURI ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:name "siteId" ;
    ] ;
    sh:property [
        sh:path top:siteName ;
        sh:datatype xsd:string ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:name "siteName" ;
    ] ;
    sh:property [
        sh:path top:siteType ;
        sh:datatype xsd:string ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:in ( "HOSPITAL" "CLINIC" "ACADEMIC_MEDICAL_CENTER" "RESEARCH_CENTER" "COMMUNITY_PRACTICE" "DECENTRALIZED_HUB" "VIRTUAL" ) ;
        sh:name "siteType" ;
    ] ;
    sh:property [
        sh:path top:belongsToOrganization ;
        sh:class topc:Organization ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:nodeKind sh:IRI ;
        sh:description "The Organization that owns or operates this Site. USDM Organization.managedSites is the inverse of this relationship." ;
        sh:name "belongsToOrganization" ;
    ] ;
    sh:property [
        sh:path top:partOfSiteNetwork ;
        sh:class topc:Organization ;
        sh:minCount 0 ;
        sh:maxCount 1 ;
        sh:nodeKind sh:IRI ;
        sh:description "An Organization with type=SITE_NETWORK or SMO that this Site is part of. Elevate Research is an Organization; each of its locations is a Site with partOfSiteNetwork = Elevate Research." ;
        sh:name "partOfSiteNetwork" ;
    ] ;
    sh:property [
        sh:path top:parentSite ;
        sh:class top:Site ;
        sh:minCount 0 ;
        sh:maxCount 1 ;
        sh:nodeKind sh:IRI ;
        sh:description "Self-reference for satellite-of-main-site structure. A satellite clinic Site references the main hospital Site as its parent. Use when the relationship is operationally a parent-child site pair, not just network membership." ;
        sh:name "parentSite" ;
    ] ;
    sh:property [
        sh:path top:hasPrincipalInvestigator ;
        sh:class top:Investigator ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:nodeKind sh:IRI ;
        sh:name "hasPrincipalInvestigator" ;
    ] ;
    sh:property [
        sh:path top:participatesIn ;
        sh:class top:Study ;
        sh:minCount 0 ;
        sh:nodeKind sh:IRI ;
        sh:name "participatesIn" ;
    ] .

# === Cross-cutting horizontals ===

topc:OrganizationShape a sh:NodeShape ;
    sh:targetClass topc:Organization ;
    sh:name "Organization" ;
    sh:description "The legal/business entity. Sponsor and Site (TOP top-levels) reference Organization for their corporate identity and roll-up structure. Modeled after USDM v3 Organization, with the addition of parentOrganization for explicit corporate hierarchy. Cross-industry: Organizations also represent operating companies in energy (refiners, EPCs), manufacturing (parent corp, plants, contract manufacturers), and other domains." ;
    sh:property [
        sh:path topc:organizationId ;
        sh:datatype xsd:anyURI ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:description "Globally unique NGSI-LD identifier." ;
        sh:name "organizationId" ;
    ] ;
    sh:property [
        sh:path topc:organizationName ;
        sh:datatype xsd:string ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:description "Display name." ;
        sh:name "organizationName" ;
    ] ;
    sh:property [
        sh:path topc:legalName ;
        sh:datatype xsd:string ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:description "Registered legal name." ;
        sh:name "legalName" ;
    ] ;
    sh:property [
        sh:path topc:organizationType ;
        sh:datatype xsd:string ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:in ( "PARENT_CORPORATION" "OPERATING_COMPANY" "SPONSOR" "CRO" "SITE" "SITE_NETWORK" "SMO" "REGULATORY_AUTHORITY" "OVERSIGHT_BODY" "VENDOR" "LABORATORY" "CONTRACT_MANUFACTURER" "ACADEMIC_INSTITUTION" "GOVERNMENT" "OTHER" ) ;
        sh:description "Role classification (multi-valued in practice via separate role assignments). Aligns with USDM Organization.type Code." ;
        sh:name "organizationType" ;
    ] ;
    sh:property [
        sh:path topc:identifierScheme ;
        sh:datatype xsd:string ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:in ( "DUNS" "ORCID" "GLN" "GS1" "FDA_FEI" "EMA_EUD" "OTHER" ) ;
        sh:description "Scheme for the identifier value. USDM-aligned." ;
        sh:name "identifierScheme" ;
    ] ;
    sh:property [
        sh:path topc:identifier ;
        sh:datatype xsd:string ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:description "Identifier value scoped by identifierScheme." ;
        sh:name "identifier" ;
    ] ;
    sh:property [
        sh:path topc:legalAddress ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:description "Headquarters/registered address (line1, line2, city, region, postalCode)." ;
        sh:name "legalAddress" ;
    ] ;
    sh:property [
        sh:path topc:country ;
        sh:datatype xsd:string ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:description "ISO 3166-1 alpha-3 country code of registration." ;
        sh:name "country" ;
    ] ;
    sh:property [
        sh:path topc:website ;
        sh:datatype xsd:anyURI ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:description "Organization website." ;
        sh:name "website" ;
    ] ;
    sh:property [
        sh:path topc:status ;
        sh:datatype xsd:string ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:in ( "ACTIVE" "INACTIVE" "MERGED" "ACQUIRED" "DISSOLVED" ) ;
        sh:description "Lifecycle state." ;
        sh:name "status" ;
    ] ;
    sh:property [
        sh:path topc:employeeSizeBand ;
        sh:datatype xsd:string ;
        sh:minCount 0 ;
        sh:maxCount 1 ;
        sh:in ( "1-10" "11-50" "51-200" "201-500" "501-1000" "1001-5000" "5001-10000" "10001-50000" "50001-100000" "100001+" ) ;
        sh:description "Banded employee count for the Organization. Corporate-scope fact; does not vary per Study, so lives here rather than on the per-Study Sponsor entity. Bands rather than exact counts because exact headcount is volatile and rarely needed for Sponsor portfolio analysis." ;
        sh:name "employeeSizeBand" ;
    ] ;
    sh:property [
        sh:path topc:fiscalYearEnd ;
        sh:datatype xsd:string ;
        sh:minCount 0 ;
        sh:maxCount 1 ;
        sh:description "Fiscal year end as MM-DD (e.g., '12-31' for calendar year, '03-31' for many UK/Asian companies, '09-30' for US federal). Corporate-scope fact; useful for budget alignment, contract renewal cycles, and forecasting." ;
        sh:name "fiscalYearEnd" ;
    ] ;
    sh:property [
        sh:path topc:primaryTherapeuticAreas ;
        sh:datatype xsd:string ;
        sh:minCount 0 ;
        sh:maxCount 1 ;
        sh:description "Comma-separated or JSON array of the Organization's portfolio therapeutic areas (oncology, cardiology, infectious disease, rare disease, etc.). Corporate-scope: Pfizer's TA spread is many; J&J Innovative Medicine's TA spread is a subset. The per-Study TA focus lives on Sponsor.primaryTherapeuticArea. Multi-value semantics: native NGSI-LD multi-value once the broker layer is in place; for the strawman emission, comma-separated string is the lowest-risk encoding." ;
        sh:name "primaryTherapeuticAreas" ;
    ] ;
    sh:property [
        sh:path topc:parentOrganization ;
        sh:class topc:Organization ;
        sh:minCount 0 ;
        sh:maxCount 1 ;
        sh:nodeKind sh:IRI ;
        sh:description "Self-reference for corporate hierarchy. J&J Innovative Medicine.parentOrganization = J&J. Empty for root-level entities." ;
        sh:name "parentOrganization" ;
    ] ;
    sh:property [
        sh:path topc:hasChildOrganization ;
        sh:class topc:Organization ;
        sh:minCount 0 ;
        sh:nodeKind sh:IRI ;
        sh:description "Inverse of parentOrganization. Convenience relationship for downward traversal." ;
        sh:name "hasChildOrganization" ;
    ] ;
    sh:property [
        sh:path topc:managesSite ;
        sh:class top:Site ;
        sh:minCount 0 ;
        sh:nodeKind sh:IRI ;
        sh:description "Sites operationally managed by this Organization. USDM Organization.managedSites." ;
        sh:name "managesSite" ;
    ] ;
    sh:property [
        sh:path topc:playsSponsorRole ;
        sh:class top:Sponsor ;
        sh:minCount 0 ;
        sh:nodeKind sh:IRI ;
        sh:description "Sponsor entities backed by this Organization in specific Studies." ;
        sh:name "playsSponsorRole" ;
    ] ;
    sh:property [
        sh:path topc:isLocatedIn ;
        sh:minCount 0 ;
        sh:maxCount 1 ;
        sh:nodeKind sh:IRI ;
        sh:description "Country of registration; flagged-missing object in OOUX v0.2." ;
        rdfs:comment "TARGET-MISSING: Country is a flagged-missing object in OOUX v0.2 (#57). minCount relaxed from 1 to 0 until target class is defined." ;
        sh:name "isLocatedIn" ;
    ] .

topc:DocumentShape a sh:NodeShape ;
    sh:targetClass topc:Document ;
    sh:name "Document" ;
    sh:property [
        sh:path topc:documentType ;
        sh:datatype xsd:string ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:description "Aligned with TMF Reference Model zones." ;
        sh:name "documentType" ;
    ] ;
    sh:property [
        sh:path topc:documentVersion ;
        sh:datatype xsd:string ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:name "documentVersion" ;
    ] ;
    sh:property [
        sh:path topc:etmfLocation ;
        sh:datatype xsd:anyURI ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:name "etmfLocation" ;
    ] .

# === Domain invariants (hand-maintained for strawman phase) ===
# Cross-property and cross-entity rules that property shapes cannot capture.
# Encoded as SHACL-SPARQL constraints (pyshacl supports SPARQLConstraint natively).
# v0.2 will introduce a structured 'invariants' field on each class for mechanical emission.

top:SponsorSoRImpliesRegRespShape a sh:NodeShape ;
    sh:targetClass top:Sponsor ;
    sh:sparql [
        a sh:SPARQLConstraint ;
        sh:message "Sponsor flagged isSponsorOfRecord=true but hasRegulatoryResponsibility=false. Permitted under FDA 21 CFR 312.52 (transfer of obligations) but unusual; verify intent." ;
        sh:severity sh:Warning ;
        sh:select """
            PREFIX top: <https://top.scientix.ai/onto/clinical/v1#>
            SELECT $this WHERE {
                $this top:isSponsorOfRecord true .
                $this top:hasRegulatoryResponsibility false .
            }
        """ ;
    ] .

top:StudyMustHaveSoRShape a sh:NodeShape ;
    sh:targetClass top:Study ;
    sh:sparql [
        a sh:SPARQLConstraint ;
        sh:message "Study has no Sponsor flagged isSponsorOfRecord=true. At least one sponsor of record is required to carry regulatory accountability." ;
        sh:severity sh:Violation ;
        sh:select """
            PREFIX top: <https://top.scientix.ai/onto/clinical/v1#>
            SELECT $this WHERE {
                $this a top:Study .
                FILTER NOT EXISTS {
                    ?sponsor top:runs $this .
                    ?sponsor top:isSponsorOfRecord true .
                }
            }
        """ ;
    ] .

top:OneSoRPerStudyPerJurisdictionShape a sh:NodeShape ;
    sh:targetClass top:Study ;
    sh:sparql [
        a sh:SPARQLConstraint ;
        sh:message "Study has more than one Sponsor flagged isSponsorOfRecord=true under the same RegulatoryAuthority (or with no explicit scope). Exactly one SoR per (Study x jurisdiction) is required; Sponsors lacking regulatoryAuthorityScope are treated as the implicit single-jurisdiction bucket." ;
        sh:severity sh:Violation ;
        sh:select """
            PREFIX top: <https://top.scientix.ai/onto/clinical/v1#>
            SELECT $this ?ra WHERE {
                {
                    SELECT $this ?ra (COUNT(?sponsor) AS ?cnt) WHERE {
                        ?sponsor top:runs $this .
                        ?sponsor top:isSponsorOfRecord true .
                        OPTIONAL { ?sponsor top:regulatoryAuthorityScope ?ra }
                    }
                    GROUP BY $this ?ra
                }
                FILTER (?cnt > 1)
            }
        """ ;
    ] .

top:StudyMustHaveOperationalSponsorShape a sh:NodeShape ;
    sh:targetClass top:Study ;
    sh:sparql [
        a sh:SPARQLConstraint ;
        sh:message "Study has no Sponsor with hasOperationalResponsibility=true. Day-to-day operational ownership must be assigned to at least one Sponsor entity." ;
        sh:severity sh:Violation ;
        sh:select """
            PREFIX top: <https://top.scientix.ai/onto/clinical/v1#>
            SELECT $this WHERE {
                $this a top:Study .
                FILTER NOT EXISTS {
                    ?sponsor top:runs $this .
                    ?sponsor top:hasOperationalResponsibility true .
                }
            }
        """ ;
    ] .
