TOP / Visit / spec / v1
2026-05-09 · backed by source/top-strawman.json (v0.5.0-strawman) · sixth top-level · universal Activity + Task foundation · 9 new SHACL invariants (34 total)
Sections: 1. TL;DR 2. First-principles posture 3. Architectural anchor 4. Universal Activity + Task 5. Native temporal+PROV 6. FHIR Questionnaire projection 7. Chain-of-custody view 8. Model summary 9. Architectural decisions 10. Cross-walks 11. SHACL invariants 12. Stress-test scenarios 13. Open issues

1.TL;DR a Visit is a Visit · universal Activity + Task foundation · accommodates any assessment

Visit is the operational visit-occurrence — a Participant attended a real visit at a StudySite on a real date. Per first-principles.md, the foundation carries one universal pattern: Visit → Activity → Task, where Activity is the universal work unit (Vitals, MRI, ePRO, biopsy, IP administration — same shape) and Task is the universal leaf data-capture (BP, MRI Study reference, PHQ-9 item, drug dose — same shape, polymorphic taskValue). Specialization is content (biomedicalConceptCode + taskValueType + URI references), never entity shape. Visit-occurrence is distinct from VisitDefinition (the protocol template, USDM-ingest-derivable). Per the temporal+PROV native commitment: every entity is a typed PROV node (Visit and Activity are prov:Activity, VisitDefinition is prov:Entity); status attributes are NGSI-LD temporal properties; Task captures observedAt as Property metadata. The foundation IS a W3C PROV graph by construction.

2.First-principles posture why the entity looks the way it looks

Three structural commitments, all applied:

  1. Operator-grounded vocabulary. "Visit," "Activity," "Task" — what coordinators say. Not "Encounter" (FHIR's name), not "ScheduledActivityInstance" (USDM's name). Cross-walks live below the line.
  2. Native temporal + provenance. Visit, Activity, Task all declare provType: prov:Activity via rdfs:subClassOf. VisitDefinition declares prov:Entity. visitStatus / activityStatus / taskStatus are NGSI-LD temporal properties. Task.observedAt is canonical Property metadata, not a flat performedDateTime attribute. performedBy declares provSemantics: wasAssociatedWith; usedEquipment declares used.
  3. Universal foundation. Activity and Task are universal containers. A DICOM imaging Activity and a blood-draw Activity are the same TOP entity shape. Specialization is content (biomedicalConceptCode, taskValueType, taskValue) and links (usedEquipment, governedBy), never entity-type discrimination.

3.Architectural anchor Visit's place in the operational hierarchy

Site ─→ StudySite ─→ Study ─→ Protocol ─→ SOA ─→ VisitDefinition (template, USDM-ingest-derivable) ▲ │ definedBy (0..1) │ Participant ─→ Visit (occurrence) ────────────┘ │ ├─ hasActivity → Activity[*] (universal work unit) ├─ hasTask → Task[*] (universal leaf; polymorphic taskValue) ├─ hasVisitObservation → VisitObservation[*] (operator-narrative) ├─ partOf → Visit (multi-part / follow-up linkage) └─ hasAdverseEvent → Event (flagged-missing until v0.7+)

Anchor pattern: Visit.forParticipant (1..1) + Visit.forStudySite (1..1) + Visit.definedBy → VisitDefinition (0..1). The 0..1 cardinality on definedBy lets UNSCHEDULED visits exist (urgent care, AE workup, sponsor-requested) without a corresponding template. SHACL invariant #30 enforces cross-entity consistency: Visit.forParticipant.forStudySite must match Visit.forStudySite.

The OOUX correction tracked since the Site spec is applied here: Visit → 1 Site (OOUX) is corrected to Visit → 1 StudySite (TOP) — Visits anchor at the per-Study site role, not the institutional Site.

4.Universal Activity + Task specialization is content, not shape

Per the universal-foundation posture in FIRST-PRINCIPLES, Visit's sub-objects accommodate any assessment without modeling its specifics. The same TOP entity shape handles vitals, MRI, ePRO, biopsy, IP administration, lab draws, ECGs, sequencing — anything.

ActivityactivityNameactivityTypebiomedicalConceptCodeTask exampleTask.taskValueType
Vital Signs"Vital Signs"ASSESSMENTNCIt:C28267Systolic BP = 128 mmHgNUMERIC
MRI Brain"MRI Brain"IMAGINGNCIt:C16809DICOM Study Instance UID = "urn:dicom:..."URI_REFERENCE
PHQ-9 Questionnaire"PHQ-9"QUESTIONNAIRELOINC:44249-1Item 1 response = "2"CODED
Drug X Administration"Drug X Administration"IP_ADMINISTRATIONNCIt:C172310{dose: 200, unit: "mg", route: "IV", lot: "..."}STRUCTURED
Blood Draw"Blood Draw — CBC"SAMPLE_COLLECTIONNCIt:C25294Hemoglobin = 12.4 g/dLNUMERIC
RECIST Lesion"Tumor Measurement"ASSESSMENTNCIt:C147547{longestDiameter: 22, unit: "mm", lesionId: "L1"}STRUCTURED
Genomic Sequencing"WGS"SAMPLE_COLLECTIONNCIt:C101294VCF reference URIURI_REFERENCE

The architectural moat: standards-up vendors model N entity types per therapeutic area (DICOMImagingStudy, PHQ9Response, LabResult, EProSession). TOP carries one universal pattern that handles all of them. External systems (DICOM PACS, lab LIS, ePRO platform, EHR, sequencing pipelines) hold implementation specifics; TOP holds the universal trial-conduct-realm reference; URI references point to wherever the specialized artifact actually lives.

5.Native temporal + PROV — applied foundation IS a W3C PROV graph by construction

Every Visit-related entity declares its PROV type via rdfs:subClassOf in the emitted shapes (per the v0.4.1 commitment):

TOP entityPROV typeReasoning
Visitprov:ActivityOccurs over time, acts on entities (Participant, Equipment); the occurrence IS an Activity in PROV terms
VisitDefinitionprov:EntityA defined artifact (the protocol template) — Entity in PROV
Activityprov:ActivityOccurs over time within a Visit; the universal work unit
Taskprov:ActivityThe capture event itself is an Activity that generates a value Entity (the taskValue)
VisitObservationprov:ActivityThe recording event; if escalated, the resulting Event was wasInformedBy this Activity

Relationship-level PROV semantics:

TOP relationshiprdfs:subPropertyOf
Activity.performedBy → Personprov:wasAssociatedWith
Activity.usedEquipment → Equipmentprov:used
Task.performedBy → Personprov:wasAssociatedWith
VisitObservation.recordedBy → Personprov:wasAssociatedWith
VisitObservation.escalatedTo → Eventprov:wasInformedBy
Visit.definedBy → VisitDefinitionprov:wasInformedBy

NGSI-LD temporal-property semantics on lifecycle attributes (visitStatus, activityStatus, taskStatus): each value bracketed by validFrom / validUntil, queryable as a trajectory. Task.observedAt is canonical Property metadata for the value's measurement timestamp — NOT a flat performedDateTime attribute.

6.FHIR Questionnaire projection force-multiplier for EHR / SMART / ePRO integration

TOP's universal Visit → Activity → Task hierarchy projects cleanly to FHIR R5 Questionnaire (form template) and QuestionnaireResponse (captured data). This is the consuming view that turns TOP into a single-source-of-truth for clinical-trial forms across Epic, Cerner, SMART on FHIR apps, ePRO vendors.

TOPFHIR Questionnaire (form template)FHIR QuestionnaireResponse (captured)
VisitDefinitionQuestionnaire (root)(framing context)
VisitDefinition.visitNameQuestionnaire.title
Activity (template, when ActivityTemplate lifts)Questionnaire.item[type=group]QuestionnaireResponse.item[type=group]
Activity.activityNamegroup.text(matched by linkId)
Task (template)Questionnaire.item[type=decimal/string/coding/etc.]QuestionnaireResponse.item.answer
Task.biomedicalConceptCode (NCIt → LOINC)item.code (LOINC)
Task.taskValueTypeitem.type (decimal / coding / date / etc.)(renders correct widget)
Task.taskUnititem.extension[questionnaire-unit] (UCUM)
Task.taskValue (after capture)item.answer.value[x]
Visit.actualStartDate/TimeQuestionnaireResponse.authored
Visit.forParticipantQuestionnaireResponse.subject
Activity.performedByQuestionnaireResponse.author

Force-multiplier story: today, sponsors maintain forms separately in 4-5 systems (CRF builder, SMART on FHIR app, ePRO vendor, EHR config). With TOP, the protocol → TOP VisitDefinition + ActivityTemplate + TaskTemplate is the single source; the FHIR Questionnaire adapter projects to canonical Questionnaire JSON; Epic / Cerner / ePRO devices all consume the same Questionnaire. Reverse: ePRO devices emit QuestionnaireResponse; the TOP ingester reads it and creates Visit + Activities + Tasks.

Adapter implementation deferred to v0.6+: tools/project_to_fhir_questionnaire.py takes a VisitDefinition (with linked Activity-template and Task-template metadata) and emits Questionnaire JSON. ~200-300 lines of Python. Bidirectional: also tools/ingest_fhir_questionnaire_response.py.

7.Chain-of-custody view the consuming view that compliance vendors render from audit logs; TOP renders from foundation facts

The foundation decisions in this lift enable per-Activity provenance views. Compliance vendors render these from hand-curated audit logs; TOP renders them from foundation facts in real time, against any Activity, by graph traversal. Example query for "show me the chain of custody for Maria's blood draw at C1D1":

PREFIX prov: <http://www.w3.org/ns/prov#>
PREFIX top:  <https://top.scientix.ai/onto/clinical/v1#>

SELECT ?activity ?actor ?equipment ?document ?observedAt WHERE {
  ?visit a top:Visit ;
         top:forParticipant ?participant ;
         top:hasActivity ?activity .
  ?participant top:firstName "Maria" .
  ?activity top:activityName "Blood Draw — CBC Panel" ;
            prov:wasAssociatedWith ?actor ;        # phlebotomist
            prov:used ?equipment ;                  # centrifuge, dry shipper
            top:governedBy ?document .              # SOP
  ?activity top:hasTask / top:observedAt ?observedAt .
}

No audit-log table. No translation layer. The chain assembles from foundation triples. Every checkmark in a regulator's compliance view is one of these triples.

8.Model summary attributes, relationships, sub-objects · full set in source/top-strawman.json

Visit attributes (~17)

AttributeTypeNotes
visitIdNGSI-LD URIGlobally unique
visitNumberstring · optionalMaps to SDTM VISITNUM; matches definedBy.visitNumber when populated
visitNamestring · optionalMaps to SDTM VISIT
visitModeenum (6) — IN_PERSON_CLINIC / IN_PERSON_HOME / REMOTE_VIDEO / REMOTE_PHONE / REMOTE_ASYNC / HYBRIDICH E6(R3) Annex 2 DCT framing
visitStatusenum (9) — SCHEDULED / IN_PROGRESS / COMPLETED / PARTIALLY_COMPLETED / MISSED / RESCHEDULED / CANCELLED / OUT_OF_WINDOW / UNSCHEDULEDNGSI-LD temporal property
plannedStartDatedate · optionalWhat the SOA says
plannedEndDatedate · optional
actualStartDatedate · optionalMaps to SDTM SVSTDTC
actualStartTimetime · optional
actualEndDatedate · optionalMaps to SDTM SVENDTC
actualEndTimetime · optional
visitDayinteger · optionalDays from study anchor; maps to SDTM VISITDY
protocolDeviationCodestring · optionalRequired by SHACL when status=OUT_OF_WINDOW
unscheduledReasonstring · optionalRequired by SHACL when status=UNSCHEDULED
tagsstring · optionalComma-separated
validFromdateTime · optionalNGSI-LD temporal-property pattern
validUntildateTime · optional

Visit relationships (~7)

RelationshipTargetCard.Notes
forParticipantParticipant1..1The Participant whose visit this is
forStudySiteStudySite1..1The hosting StudySite (operational owner regardless of physical location)
definedByVisitDefinition0..1Protocol template; 0..1 because UNSCHEDULED visits have no template; provSemantics: wasInformedBy
hasActivityActivity0..NActivities performed at this Visit (universal sub-objects)
hasTaskTask0..NLeaf data-capture units; each task.belongsToActivity links to a parent Activity
hasVisitObservationVisitObservation0..NOperator-narrative observations
partOfVisit0..1Multi-part / follow-up linkage
hasAdverseEventEvent0..NFlagged-missing until v0.7+

Sub-objects of Visit (3)

Sub-objectRoleprovTypeKey attrs
ActivityThe universal work unit (vitals, MRI, ePRO, IP-admin, anything)prov:ActivityactivityName, activityType, activityStatus (temporal), biomedicalConceptCode, validFrom/Until
TaskThe universal leaf data-capture unit; polymorphic taskValueprov:ActivitytaskName, taskValueType, taskValue, taskUnit, biomedicalConceptCode, taskStatus (temporal), observedAt, belongsToActivity
VisitObservationOperator-narrative observation; may escalate to Eventprov:ActivityobservationText, observationCategory, escalated, escalationDate

VisitDefinition (Study sub-object) — ~10 attrs

AttributeTypeNotes
visitDefinitionIdNGSI-LD URIMints from USDM Encounter.id under the urn:top:study:{usdm_study_id}/visit-definition:{usdm_encounter_id} URI policy
visitNumberstringDisplay number (V1, Visit 4, Day 28)
visitNamestringOperator-friendly name
visitDayinteger · optionalDays from study anchor
visitWindowMinDaysinteger · optionalHow early the visit can occur and still be in-window
visitWindowMaxDaysinteger · optionalHow late and still in-window
expectedDurationMinutesinteger · optional
descriptionstring · optional
visitDefinitionStatusenum — DRAFT / EFFECTIVE / SUPERSEDEDNGSI-LD temporal property
tagsstring · optional

9.Why this shape — architectural decisions

Decision 1 — Two separate entities: VisitDefinition + Visit

VisitDefinition (Study sub-object) is USDM-ingest-derivable from StudyDesign.encounters[]; Visit (top-level) is the operational occurrence runtime-created by EDC/scheduling/eVisit systems. Linked by Visit.definedBy (0..1; UNSCHEDULED visits have no template). Per FIRST-PRINCIPLES (different operator vocabulary gets first-class entity): operators talk about "Visit 1" (template) and "Mary's Visit 1" (occurrence) as different operational objects. Two separate entities; clean USDM ingest mapping; clean SHACL invariants.

Decision 2 — Anchor pattern: forParticipant + forStudySite

Visit.forParticipant (1..1) and Visit.forStudySite (1..1) — both direct edges, even though forStudySite is reachable via forParticipant.forStudySite traversal. The redundancy is deliberate (query convenience: "all visits at MSKCC across all participants" without a join). SHACL invariant #30 enforces consistency.

Decision 3 — VisitObservation as sub-object (OOUX Path C)

Captures operator-narrative observations during a visit that don't fit a structured Activity / Task. Has escalated flag and escalatedTo → Event for the Path C reportability handoff. PROV: prov:wasInformedBy on escalatedTo means a categorized Event was informed by this observation.

Decision 4 — Activity + Task as universal sub-objects (specialization is content)

Per Bo's clarification: TOP must accommodate any assessment without modeling its specifics. The foundation carries Activity + Task as universal containers; specialization is content (biomedicalConceptCode + taskValueType + URI references), never entity shape. This eliminates the need for ImagingStudy / IPAdministration / QuestionnaireResponse / per-instrument horizontals. Implementation specifics live in external systems (DICOM PACS, lab LIS, ePRO platform); TOP carries the universal trial-conduct-realm reference. The architectural moat: standards-up vendors model N entity types per therapeutic area; TOP carries one.

Decision 5 — visitMode 6-state enum per ICH E6(R3) Annex 2

IN_PERSON_CLINIC / IN_PERSON_HOME / REMOTE_VIDEO / REMOTE_PHONE / REMOTE_ASYNC / HYBRID. DCT modality is critical for trial-feasibility analytics, post-COVID DCT design, and twin-synthesis covariates (visit modality affects drop-out risk, data quality, and patient burden). FHIR Encounter.virtualService + Encounter.location are the projection targets.

Decision 6 — visitStatus 9-state lifecycle enum (NGSI-LD temporal property)

SCHEDULED / IN_PROGRESS / COMPLETED / PARTIALLY_COMPLETED / MISSED / RESCHEDULED / CANCELLED / OUT_OF_WINDOW / UNSCHEDULED. NGSI-LD temporal property (validFrom/validUntil per value) for trajectory queryability — parallel to Participant.participantStatus from v0.4.0. Trade-off: more states → more SHACL state-machine invariants (4 of the 9 Visit invariants enforce status-driven completeness).

Decision 7 — Protocol-deviation handling via lifecycle states + protocolDeviationCode

Three operationally-distinct deviation modes: OUT_OF_WINDOW (visit happened outside the protocol window — captured by status + protocolDeviationCode), MISSED (didn't happen, no make-up scheduled), UNSCHEDULED (extra visit — captured by status + unscheduledReason). A separate ProtocolDeviation sub-object can lift in v0.6+ if structured deviation modeling becomes operationally needed.

Decision 8 — Planned-vs-actual datetime separation

plannedStartDate / plannedEndDate are operator-grounded scheduling facts (what the SOA / scheduling system says). actualStartDate / actualStartTime / actualEndDate / actualEndTime are runtime facts (what actually happened). Both kept distinct; visit-window adherence analytics use both. NGSI-LD validFrom / validUntil on the entity record bracket operational validity at the entity level (separate concern from per-property-value temporal semantics on visitStatus).

Decision 9 — Twin-queryability extension

Visit lifts the queryability surface for digital-twin synthesis significantly. Per-Participant time-series of Visits + per-Visit Activities + per-Activity Tasks + visit-window adherence as a covariate. The foundation decisions made in Participant v0.4 pay off here. Twin synthesis is downstream tooling (not in TOP); the foundation provides the contract.

10.Cross-walks FHIR R5 / SDTM / CDASH / USDM / OMOP · projection-edge documentation

Visit → standards (operational)

TOP attribute / relationshipFHIR R5SDTM SVCDASHOMOPUSDM
visitId (URI)Encounter.identifier(SUPPDM or implied)visit_occurrence_id
visitNumberEncounter.identifier (use=secondary)VISITNUMVISITNUM(via VisitDefinition)
visitNameEncounter.type[].textVISITVISIT(via VisitDefinition)
visitModeEncounter.virtualService + Encounter.location(SUPPDM)visit_concept_id (with mode-specific concepts)Encounter.environmentalSettings + contactModes
visitStatusEncounter.status(computed from SVSTDTC presence)(computed)
actualStartDate/TimeEncounter.actualPeriod.startSVSTDTCSVSTDTCvisit_start_datetime
actualEndDate/TimeEncounter.actualPeriod.endSVENDTCSVENDTCvisit_end_datetime
visitDay(computed)VISITDY(via VisitDefinition relative-day)
unscheduledReasonEncounter.reason[].textSVUPDESvisit_source_value
forParticipant → ParticipantEncounter.subject (Ref → Patient via ResearchSubject)USUBJIDUSUBJIDperson_id
forStudySite → StudySiteEncounter.serviceProvider + .locationSITEIDSITEIDcare_site_id
definedBy → VisitDefinition(via Encounter.appointment)(implicit via VISITNUM)Encounter (template)
partOf → Visit (parent)Encounter.partOfpreceding_visit_occurrence_id

Activity → standards (varies by content, same TOP shape)

Activity contentFHIR R5 resourceSDTM domainOMOP table
Vital SignsObservation (category=vital-signs)VSmeasurement
Lab TestsObservation (category=laboratory)LBmeasurement
ProcedureProcedurePRprocedure_occurrence
IP AdministrationMedicationAdministrationEXdrug_exposure
ECGObservation + DiagnosticReportEGmeasurement
ImagingImagingStudy(custom or CR)
QuestionnaireQuestionnaireResponseQSobservation

The same TOP Activity entity shape projects to different standards resources depending on its content (biomedicalConceptCode + activityType). The projection adapter at the deployment edge does the routing.

Task → standards (varies by taskValueType + content)

taskValueTypeFHIR Observation.value[x]SDTM
NUMERICvalueQuantity (with UCUM unit)VSORRES (numeric, with VSORRESU unit)
TEXTvalueStringVSORRES (text)
CODEDvalueCodeableConceptVSORRES + VSSTRESC (controlled term)
URI_REFERENCEvalueReference (or extension)(custom — reference to external system)
STRUCTUREDcomponent[] (multi-component Observation)SUPPDM or per-domain qualifier records
DATEvalueDateTime(domain-specific DTC field)
IMAGE_REFERENCEvalueReference → ImagingStudy(custom — DICOM Study Instance UID reference)

11.SHACL invariants 9 new for Visit lift · 34 total in the graph

#SeverityConstraint
26HardCompleted visit needs actual start. Visit with status in {COMPLETED, PARTIALLY_COMPLETED, OUT_OF_WINDOW} requires actualStartDate.
27HardCompleted visit needs actual end. Visit with status=COMPLETED requires actualEndDate.
28HardUnscheduled visit needs reason. Visit with status=UNSCHEDULED requires unscheduledReason.
29HardOut-of-window visit needs deviation code. Visit with status=OUT_OF_WINDOW requires protocolDeviationCode.
30HardVisit-Participant-StudySite consistency. Visit.forParticipant.forStudySite must match Visit.forStudySite.
31HardActivity NOT_PERFORMED needs reason.
32HardTask NOT_PERFORMED needs reason.
33HardCompleted Task needs value. Task with taskStatus=COMPLETED requires non-empty taskValue.
34SoftEscalated VisitObservation needs link. escalated=true should have escalatedTo populated (when Event lifts).

12.Stress-test scenarios six cases · plain English to graph shape

In-person C1D1 visit with vitals + labs + IP administration
Happy path

Maria attends Cycle 1 Day 1 at MSKCC on 2026-04-12. Vitals taken, blood drawn for CBC, drug administered.

Visit (visitMode=IN_PERSON_CLINIC, status=COMPLETED) with three Activities (Vitals, Blood Draw, Drug X Administration) and eight Tasks (Sys/Dia BP, HR, Temp, Hgb, WBC, Platelets, Drug X dose). Each Task carries observedAt, biomedicalConceptCode, performedBy. PROV chain: Activity prov:wasAssociatedWith → Person; Task prov:wasAssociatedWith → Person; Task generated by Activity. (Mirrored in worked example site-mskcc-onco423.ttl.)
Telehealth follow-up visit with ePRO capture
DCT

Maria attends a Day 14 follow-up via video; PHQ-9 questionnaire administered remotely.

Visit (visitMode=REMOTE_VIDEO, status=COMPLETED). One Activity (PHQ-9 Questionnaire, activityType=QUESTIONNAIRE, biomedicalConceptCode=LOINC:44249-1). Nine Tasks (one per PHQ-9 item, taskValueType=CODED, biomedicalConceptCode=LOINC:44261-6 etc.). Task.taskValue carries the response code (0/1/2/3). FHIR projection: QuestionnaireResponse rendering on the patient's device captures the responses; reverse-projection adapter creates Tasks.
Out-of-window visit (protocol deviation)
Deviation

Maria's C2D1 visit was supposed to be on 2026-05-10 (Day 28, window ±3) but happened on 2026-05-15 (Day 33).

Visit with visitStatus=OUT_OF_WINDOW, actualStartDate=2026-05-15, protocolDeviationCode populated. SHACL #29 satisfied. Recorded as a protocol deviation in the sponsor's deviation log via downstream adapter. The visit's data is still valid; the deviation is documented.
Unscheduled visit for AE workup
Unscheduled

Maria reports nausea on Day 10; an unscheduled visit is opened for evaluation.

Visit with visitStatus=UNSCHEDULED, unscheduledReason="AE workup — Grade 2 nausea reported via portal", definedBy=null. SHACL #28 satisfied. Activities for vitals + targeted assessment + clinician note. VisitObservation captures the operator's narrative; if escalated, links to an AE Event (when Event lifts).
DICOM imaging visit (universal foundation handles it)
Imaging

Maria's End-of-Treatment MRI scan on Day 90.

Visit (visitMode=IN_PERSON_CLINIC, status=COMPLETED). One Activity (activityName="MRI Brain", activityType=IMAGING, biomedicalConceptCode=NCIt:C16809). One Task (taskName="MRI Study", taskValueType=URI_REFERENCE, taskValue="urn:dicom:study/1.2.840.113654...", biomedicalConceptCode=NCIt:C16809). The DICOM Study Instance UID is referenced — TOP doesn't store pixels, the PACS does. Same TOP entity shape as a blood draw; specialization is in content.
Compliance query — chain of custody for a single Activity
Provenance

A regulator asks: "Show me the full chain of custody for Maria's blood draw at C1D1."

SPARQL query over foundation (no audit-log join): finds the Activity by Visit + activityName, traverses prov:wasAssociatedWith to the phlebotomist, prov:used to the centrifuge Equipment (which carries its calibration Credential chain), top:governedBy to the lab SOP Document, top:hasTask to the captured measurements. All from foundation triples. The compliance view renders from this in real time — no curated audit log required.

13.Open issues

  1. ActivityTemplate as Study sub-object (v0.6+). Currently VisitDefinition.hasExpectedActivityTemplate is flagged-missing. Lifts when the SOA grid needs richer template modeling and the FHIR Questionnaire projection adapter requires per-Activity template metadata.
  2. FHIR Questionnaire projection adapter (tools/project_to_fhir_questionnaire.py). Implementation deferred to v0.6. Bidirectional: projects VisitDefinition → Questionnaire; ingests QuestionnaireResponse → Visit + Activities + Tasks.
  3. Equipment.calibrationStatus as NGSI-LD temporal property. Per the chain-of-custody mock-up, Equipment calibration currency is a SHACL-queryable assertion at activity-time. Defers to a small Equipment enrichment PR.
  4. Person.hasCredential richer credentialType enum. Per the chain-of-custody mock-up: phlebotomy certs, IATA shipping certs, GCP training certs all surface as Credentials. Defers to a Credential horizontal enrichment.
  5. Document section anchors (§7.4, §7.6 references). Per the chain-of-custody mock-up. Defers to a Document horizontal enrichment.
  6. Continuous monitoring data (Log temporal-property enrichment). Cold-chain temperature logs, CGM streams, wearable data. Log horizontal extension; URI_REFERENCE on Task to the log file is the foundation-side handle.
  7. Event lift (v0.7+). AdverseEvent / Other Clinical Event with eventCategory enum + the OOUX Path C reportability handoff. Visit.hasAdverseEvent + VisitObservation.escalatedTo unlocks at that point.
  8. Twin-spec planning note. Now that Visit + Activity + Task expand the queryability surface significantly, the twin-synthesizer contract spec lifts as a planning note in v0.6+.