eyeling 1.16.3 → 1.16.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/HANDBOOK.md +153 -0
  2. package/README.md +0 -1
  3. package/examples/auroracare.n3 +528 -0
  4. package/examples/control-system.n3 +223 -47
  5. package/examples/delfour.n3 +409 -0
  6. package/examples/gps.n3 +144 -53
  7. package/examples/ill-formed-literals.n3 +195 -0
  8. package/examples/output/auroracare.n3 +118 -0
  9. package/examples/output/control-system.n3 +24 -1
  10. package/examples/output/delfour.n3 +40 -0
  11. package/examples/output/gps.n3 +14 -2
  12. package/examples/output/ill-formed-literals.n3 +27 -0
  13. package/examples/output/parcellocker.n3 +15 -0
  14. package/examples/parcellocker.n3 +164 -0
  15. package/eyeling.js +16 -1
  16. package/lib/engine.js +14 -1
  17. package/lib/prelude.js +2 -0
  18. package/package.json +2 -3
  19. package/arctifacts/README.md +0 -59
  20. package/arctifacts/ackermann.html +0 -678
  21. package/arctifacts/auroracare.html +0 -1297
  22. package/arctifacts/bike-trip.html +0 -752
  23. package/arctifacts/binomial-theorem.html +0 -631
  24. package/arctifacts/bmi.html +0 -511
  25. package/arctifacts/building-performance.html +0 -750
  26. package/arctifacts/clinical-care.html +0 -726
  27. package/arctifacts/collatz.html +0 -403
  28. package/arctifacts/complex.html +0 -321
  29. package/arctifacts/control-system.html +0 -482
  30. package/arctifacts/delfour.html +0 -849
  31. package/arctifacts/earthquake-epicenter.html +0 -982
  32. package/arctifacts/eco-route.html +0 -662
  33. package/arctifacts/euclid-infinitude.html +0 -564
  34. package/arctifacts/euler-identity.html +0 -667
  35. package/arctifacts/exoplanet-transit.html +0 -1000
  36. package/arctifacts/faltings-theorem.html +0 -1046
  37. package/arctifacts/fibonacci.html +0 -299
  38. package/arctifacts/fundamental-theorem-arithmetic.html +0 -398
  39. package/arctifacts/godel-numbering.html +0 -743
  40. package/arctifacts/gps-bike.html +0 -759
  41. package/arctifacts/gps-clinical-bench.html +0 -792
  42. package/arctifacts/graph-french.html +0 -449
  43. package/arctifacts/grass-molecular.html +0 -592
  44. package/arctifacts/group-theory.html +0 -740
  45. package/arctifacts/health-info.html +0 -833
  46. package/arctifacts/kaprekar-constant.html +0 -576
  47. package/arctifacts/lee.html +0 -805
  48. package/arctifacts/linked-lists.html +0 -502
  49. package/arctifacts/lldm.html +0 -612
  50. package/arctifacts/matrix-multiplication.html +0 -502
  51. package/arctifacts/matrix.html +0 -651
  52. package/arctifacts/newton-raphson.html +0 -944
  53. package/arctifacts/peano-factorial.html +0 -456
  54. package/arctifacts/pi.html +0 -363
  55. package/arctifacts/polynomial.html +0 -646
  56. package/arctifacts/prime.html +0 -366
  57. package/arctifacts/pythagorean-theorem.html +0 -468
  58. package/arctifacts/rest-path.html +0 -469
  59. package/arctifacts/roots-of-unity.html +0 -363
  60. package/arctifacts/turing.html +0 -409
  61. package/arctifacts/wind-turbines.html +0 -726
package/HANDBOOK.md CHANGED
@@ -30,6 +30,7 @@
30
30
  - [Appendix C — N3 beyond Prolog: logic that survives the open web](#app-c)
31
31
  - [Appendix D — LLM + Eyeling: A Repeatable Logic Toolchain](#app-d)
32
32
  - [Appendix E — How Eyeling reaches 100% on `notation3tests`](#app-e)
33
+ - [Appendix F — The ARC approach: Answer • Reason Why • Check](#app-f)
33
34
 
34
35
  ---
35
36
 
@@ -2576,3 +2577,155 @@ That is the method:
2576
2577
  - duplicate-safe fixpoint closure
2577
2578
 
2578
2579
  That is why the result is 100%.
2580
+
2581
+ ---
2582
+
2583
+ <a id="app-f"></a>
2584
+
2585
+ ## Appendix F — The ARC approach: Answer • Reason Why • Check
2586
+
2587
+ A useful way to structure an Eyeling program is the ARC approach:
2588
+
2589
+ > Answer • Reason Why • Check
2590
+
2591
+ The idea is simple: do not stop at producing a result. Produce the result, explain why it is the right result, and include a concrete verification that fails loudly when an important assumption does not hold.
2592
+
2593
+ ARC treats a program as a small, accountable artifact built from three ingredients:
2594
+
2595
+ 1. Data
2596
+ 2. Logic
2597
+ 3. A Question
2598
+
2599
+ From these, we build a compact program that answers the question, explains the answer, and checks itself.
2600
+
2601
+ ### F.1 The three parts
2602
+
2603
+ #### Answer
2604
+
2605
+ The **Answer** is the direct response to the question being asked.
2606
+
2607
+ It should be short, specific, and easy to read. In Eyeling, this is often emitted as one or more `log:outputString` lines such as:
2608
+
2609
+ - the final decision
2610
+ - the selected item
2611
+ - the computed value
2612
+ - the resulting classification
2613
+
2614
+ A good Answer reads like something you could show to a user, an auditor, or a calling program.
2615
+
2616
+ #### Reason Why
2617
+
2618
+ The **Reason Why** explains why the Answer is correct.
2619
+
2620
+ This is not a full proof calculus or a hidden chain of thought. It is a concise, inspectable explanation grounded in the facts, rules, thresholds, identities, or policies that matter for the case. In practice, it often includes:
2621
+
2622
+ - the relevant inputs
2623
+ - the governing rule or policy
2624
+ - the key intermediate facts
2625
+ - the condition that made the conclusion follow
2626
+
2627
+ In Eyeling, the Reason Why is usually rendered as additional `log:outputString` lines derived from the same closure as the Answer.
2628
+
2629
+ #### Check
2630
+
2631
+ The **Check** is an independent validation step.
2632
+
2633
+ Its purpose is not to restate the Answer, but to test that important invariants still hold. A Check should fail loudly if the program’s assumptions break, if a data dependency is malformed, or if an edge case invalidates the intended conclusion.
2634
+
2635
+ In Eyeling, Checks are a natural fit for either:
2636
+
2637
+ - derived facts such as `:ok :signatureVerified true .`, or
2638
+ - inference fuses such as `{ ... } => false .` when a violation must stop execution.
2639
+
2640
+ This makes verification part of the program itself rather than something left to external commentary.
2641
+
2642
+ ### F.2 Proof = Reason Why + Check
2643
+
2644
+ ARC summarizes its trust model as:
2645
+
2646
+ > Proof = Reason Why + Check
2647
+
2648
+ That is a practical notion of proof. The Reason Why explains the logic in human terms. The Check verifies that the critical conditions actually hold at runtime.
2649
+
2650
+ For many real workflows, that combination is more useful than a bare result: it is inspectable, repeatable, and suitable for automation.
2651
+
2652
+ ### F.3 Why ARC fits Eyeling well
2653
+
2654
+ Eyeling already encourages the separation that ARC needs.
2655
+
2656
+ Rules derive facts. Facts can include output facts. Output is not printed eagerly during proof search; instead, `log:outputString` facts are collected from the final closure and rendered deterministically, for example with `-r` / `--strings`. This makes it natural to derive a structured Answer and Reason Why as part of the logic itself.
2657
+
2658
+ Checks also map well to Eyeling. A rule with conclusion `false` acts as an inference fuse: if its body becomes provable, execution stops with a hard failure. This is exactly the behavior we want for “must-hold” conditions.
2659
+
2660
+ So ARC in Eyeling is not an add-on. It is mostly a disciplined way of organizing what Eyeling already does well: derive conclusions, expose supporting facts, and enforce invariants.
2661
+
2662
+ ### F.4 A practical pattern
2663
+
2664
+ A simple ARC-oriented Eyeling file often has four layers:
2665
+
2666
+ 1. **Facts**
2667
+ Input data, parameters, policies, and known relationships.
2668
+
2669
+ 2. **Logic**
2670
+ Rules that derive the program’s internal conclusions.
2671
+
2672
+ 3. **Presentation**
2673
+ Rules that turn derived conclusions into `log:outputString` lines for the Answer and Reason Why.
2674
+
2675
+ 4. **Verification**
2676
+ Rules that derive check facts or trigger inference fuses on violations.
2677
+
2678
+ A useful habit is to keep these layers visually separate in the file.
2679
+
2680
+ ### F.5 A tiny template
2681
+
2682
+ ```n3
2683
+ @prefix : <http://example.org/> .
2684
+ @prefix log: <http://www.w3.org/2000/10/swap/log#> .
2685
+
2686
+ # Facts
2687
+ :case :input 42 .
2688
+
2689
+ # Logic
2690
+ { :case :input ?n . ?n math:greaterThan 10 . }
2691
+ => { :case :decision "allowed" . } .
2692
+
2693
+ # Answer
2694
+ { :case :decision ?d . }
2695
+ => { :answer log:outputString "Answer\n" .
2696
+ :answer log:outputString ?d . } .
2697
+
2698
+ # Reason Why
2699
+ { :case :input ?n . :case :decision ?d . }
2700
+ => { :why log:outputString "\nReason Why\n" .
2701
+ :why log:outputString "Input satisfied the rule threshold.\n" . } .
2702
+
2703
+ # Check
2704
+ { :case :decision "allowed" .
2705
+ :case :input ?n .
2706
+ ?n math:notGreaterThan 10 . }
2707
+ => false .
2708
+ ```
2709
+
2710
+ The exact presentation style can vary, but the shape remains the same: derive the result, explain the result, and verify the result.
2711
+
2712
+ ### F.6 What ARC is not
2713
+
2714
+ ARC does **not** mean:
2715
+
2716
+ - printing a result and calling it explained
2717
+ - replacing checks with prose
2718
+ - hiding the important assumptions
2719
+ - relying on “trust me” comments outside the executable artifact
2720
+
2721
+ A file follows ARC only when the answer, explanation, and validation are all carried by the program itself.
2722
+
2723
+ ### F.7 A good default for examples
2724
+
2725
+ For worked examples in this handbook, ARC is a strong default presentation style:
2726
+
2727
+ - **Answer** for the main result
2728
+ - **Reason Why** for the key supporting explanation
2729
+ - **Check** for invariants and fail-loud validation
2730
+
2731
+ This keeps examples readable for newcomers while also making them more useful as reusable, auditable logic artifacts.
package/README.md CHANGED
@@ -15,7 +15,6 @@ A compact [Notation3 (N3)](https://notation3.org/) reasoner in **JavaScript**.
15
15
  - **Semantics:** [https://eyereasoner.github.io/eyeling/SEMANTICS](https://eyereasoner.github.io/eyeling/SEMANTICS)
16
16
  - **Playground:** [https://eyereasoner.github.io/eyeling/demo](https://eyereasoner.github.io/eyeling/demo)
17
17
  - **Conformance report:** [https://codeberg.org/phochste/notation3tests/src/branch/main/reports/report.md](https://codeberg.org/phochste/notation3tests/src/branch/main/reports/report.md)
18
- - **ARCtifacts:** [https://eyereasoner.github.io/eyeling/arctifacts/](https://eyereasoner.github.io/eyeling/arctifacts/)
19
18
 
20
19
  Eyeling is regularly checked against the community Notation3 test suite. If you want implementation details (parser, unifier, proof search, skolemization, scoped closure, builtins), start with the handbook.
21
20
 
@@ -0,0 +1,528 @@
1
+ # =============================================================================
2
+ # AuroraCare — ARC "Purpose-based Medical Data Exchange" example.
3
+ #
4
+ # This example shows how medical data access can be governed by purpose.
5
+ # Different people may request the same data, but access depends on why they
6
+ # need it, what role they have, and whether extra conditions are met. Care-team
7
+ # treatment use may be allowed, for instance, while unrelated uses such as
8
+ # marketing, employment screening, or broad insurance access can be denied.
9
+ # =============================================================================
10
+
11
+ @prefix : <https://example.org/auroracare#> .
12
+ @prefix arc: <https://josd.github.io/arc/terms#> .
13
+ @prefix odrl: <http://www.w3.org/ns/odrl/2/> .
14
+ @prefix log: <http://www.w3.org/2000/10/swap/log#> .
15
+ @prefix string: <http://www.w3.org/2000/10/swap/string#> .
16
+ @prefix dpv: <https://w3id.org/dpv#> .
17
+ @prefix ehds: <https://w3id.org/dpv/legal/eu/ehds#> .
18
+ @prefix hlth: <https://w3id.org/dpv/sector/health#> .
19
+ @prefix ex: <https://example.org/health#> .
20
+ @prefix ac: <https://example.org/auroracare#> .
21
+
22
+ # -----
23
+ # Facts
24
+ # -----
25
+
26
+ :case
27
+ a arc:Case ;
28
+ arc:question "For each AuroraCare scenario, should the PDP permit or deny the requested use of health data, and why?" .
29
+
30
+ :policy_primary
31
+ a odrl:Policy ;
32
+ :uid "urn:policy:primary-care-001" ;
33
+ :purposeAllowed hlth:PrimaryCareManagement, hlth:PatientRemoteMonitoring ;
34
+ :roleAllowed "clinician" ;
35
+ :allowAnyCategory ex:PATIENT_SUMMARY, ex:LAB_RESULTS .
36
+
37
+ :policy_qi
38
+ a odrl:Policy ;
39
+ :uid "urn:policy:qi-2025-aurora" ;
40
+ :purposeAllowed ehds:EnsureQualitySafetyHealthcare ;
41
+ :requireEnvironment "secure_env" ;
42
+ :requireAllCategory ex:LAB_RESULTS, ex:PATIENT_SUMMARY ;
43
+ :duty ehds:requireConsent, ehds:noExfiltration .
44
+
45
+ :policy_research
46
+ a odrl:Policy ;
47
+ :uid "urn:policy:research-aurora-diabetes" ;
48
+ :purposeAllowed ehds:HealthcareScientificResearch ;
49
+ :requireEnvironment "secure_env" ;
50
+ :requireTom dpv:Anonymisation ;
51
+ :allowAnyCategory ex:LAB_RESULTS, ex:PATIENT_SUMMARY, ex:IMAGING_REPORT ;
52
+ :duty ehds:annualOutcomeReport, ehds:noReidentification, ehds:noExfiltration .
53
+
54
+ :policy_deny_insurance
55
+ a odrl:Policy ;
56
+ :uid "urn:policy:deny-insurance" ;
57
+ :prohibitPurpose hlth:InsuranceManagement .
58
+
59
+ :clinician_alba :linkedTo :ruben .
60
+ :gp_ruben :linkedTo :ruben .
61
+
62
+ :ruben :consentAllow ehds:HealthcareScientificResearch .
63
+ :ruben :consentDeny ehds:TrainTestAndEvaluateAISystemsAlgorithms .
64
+
65
+ :auroracare
66
+ :primaryPurpose hlth:PrimaryCareManagement, hlth:PatientRemoteMonitoring ;
67
+ :prohibitedPurpose hlth:InsuranceManagement .
68
+
69
+ :scenario_A
70
+ a :Scenario ;
71
+ :outputKey :out_010_A ;
72
+ :label "A – Primary care visit" ;
73
+ :description "Clinician in the patient's care team accessing the patient summary for primary care management." ;
74
+ :requester :clinician_alba ;
75
+ :requesterRole "clinician" ;
76
+ :subject :ruben ;
77
+ :purpose hlth:PrimaryCareManagement ;
78
+ :environment "api_gateway" ;
79
+ :category ex:PATIENT_SUMMARY .
80
+
81
+ :scenario_B
82
+ a :Scenario ;
83
+ :outputKey :out_020_B ;
84
+ :label "B – Quality improvement (in scope)" ;
85
+ :description "QI analyst using lab results + summary in a secure environment." ;
86
+ :requester :qi_analyst ;
87
+ :requesterRole "data_user" ;
88
+ :subject :ruben ;
89
+ :purpose ehds:EnsureQualitySafetyHealthcare ;
90
+ :environment "secure_env" ;
91
+ :category ex:LAB_RESULTS, ex:PATIENT_SUMMARY .
92
+
93
+ :scenario_C
94
+ a :Scenario ;
95
+ :outputKey :out_030_C ;
96
+ :label "C – Quality improvement (out of scope)" ;
97
+ :description "QI analyst with only lab results; policy expects labs + summary." ;
98
+ :requester :qi_analyst ;
99
+ :requesterRole "data_user" ;
100
+ :subject :ruben ;
101
+ :purpose ehds:EnsureQualitySafetyHealthcare ;
102
+ :environment "secure_env" ;
103
+ :category ex:LAB_RESULTS .
104
+
105
+ :scenario_D
106
+ a :Scenario ;
107
+ :outputKey :out_040_D ;
108
+ :label "D – Insurance management" ;
109
+ :description "Insurance bot attempting to use health data for insurance management (prohibited purpose)." ;
110
+ :requester :insurer_bot ;
111
+ :requesterRole "data_user" ;
112
+ :subject :ruben ;
113
+ :purpose hlth:InsuranceManagement ;
114
+ :environment "secure_env" ;
115
+ :category ex:PATIENT_SUMMARY .
116
+
117
+ :scenario_E
118
+ a :Scenario ;
119
+ :outputKey :out_050_E ;
120
+ :label "E – GP checks labs" ;
121
+ :description "GP for the same patient checking lab results via the API gateway." ;
122
+ :requester :gp_ruben ;
123
+ :requesterRole "clinician" ;
124
+ :subject :ruben ;
125
+ :purpose hlth:PrimaryCareManagement ;
126
+ :environment "api_gateway" ;
127
+ :category ex:LAB_RESULTS .
128
+
129
+ :scenario_F
130
+ a :Scenario ;
131
+ :outputKey :out_060_F ;
132
+ :label "F – Research on anonymised dataset" ;
133
+ :description "Researcher using anonymised labs + summary in a secure environment, with opt-in." ;
134
+ :requester :researcher_aurora ;
135
+ :requesterRole "data_user" ;
136
+ :subject :ruben ;
137
+ :purpose ehds:HealthcareScientificResearch ;
138
+ :environment "secure_env" ;
139
+ :tom dpv:Anonymisation ;
140
+ :category ex:PATIENT_SUMMARY, ex:LAB_RESULTS .
141
+
142
+ :scenario_G
143
+ a :Scenario ;
144
+ :outputKey :out_070_G ;
145
+ :label "G – AI training (opt-out)" ;
146
+ :description "Data user wants to train AI, but the subject opted out of AI training." ;
147
+ :requester :ml_ops ;
148
+ :requesterRole "data_user" ;
149
+ :subject :ruben ;
150
+ :purpose ehds:TrainTestAndEvaluateAISystemsAlgorithms ;
151
+ :environment "secure_env" ;
152
+ :category ex:PATIENT_SUMMARY, ex:LAB_RESULTS .
153
+
154
+ # -----
155
+ # Logic
156
+ # -----
157
+
158
+ {
159
+ ?scenario :requester ?requester ;
160
+ :subject ?subject .
161
+ ?requester :linkedTo ?subject .
162
+ } => {
163
+ ?scenario :careTeamLinked "yes" .
164
+ } .
165
+
166
+ {
167
+ ?scenario :subject ?subject ;
168
+ :purpose ?purpose .
169
+ ?subject :consentAllow ?purpose .
170
+ } => {
171
+ ?scenario :subjectOptIn "yes" .
172
+ } .
173
+
174
+ {
175
+ ?scenario :subject ?subject ;
176
+ :purpose ?purpose .
177
+ ?subject :consentDeny ?purpose .
178
+ } => {
179
+ ?scenario :subjectOptOut "yes" .
180
+ } .
181
+
182
+ {
183
+ ?scenario :purpose hlth:PrimaryCareManagement ;
184
+ :requesterRole "clinician" ;
185
+ :careTeamLinked "yes" ;
186
+ :category ?category .
187
+ :policy_primary :allowAnyCategory ?category .
188
+ } => {
189
+ ?scenario :matchedPolicy :policy_primary ;
190
+ :matchedPolicyUid "urn:policy:primary-care-001" ;
191
+ :decision "PERMIT" ;
192
+ :reason "Permitted: clinician in the patient's care team, and the primary-care policy matched." ;
193
+ :trace "permit:primary_care_allowed" ;
194
+ :trace "urn:policy:primary-care-001:permit:odrl:permission_matched" .
195
+ } .
196
+
197
+ {
198
+ ?scenario :purpose ehds:EnsureQualitySafetyHealthcare ;
199
+ :environment "secure_env" ;
200
+ :category ex:LAB_RESULTS, ex:PATIENT_SUMMARY .
201
+ } => {
202
+ ?scenario :matchedPolicy :policy_qi ;
203
+ :matchedPolicyUid "urn:policy:qi-2025-aurora" ;
204
+ :decision "PERMIT" ;
205
+ :reason "Permitted: ODRL/DPV policy matched for secondary use." ;
206
+ :trace "urn:policy:qi-2025-aurora:permit:odrl:permission_matched" .
207
+ } .
208
+
209
+ {
210
+ ?scenario :purpose ehds:HealthcareScientificResearch ;
211
+ :environment "secure_env" ;
212
+ :tom dpv:Anonymisation ;
213
+ :subjectOptIn "yes" ;
214
+ :category ?category .
215
+ :policy_research :allowAnyCategory ?category .
216
+ } => {
217
+ ?scenario :matchedPolicy :policy_research ;
218
+ :matchedPolicyUid "urn:policy:research-aurora-diabetes" ;
219
+ :decision "PERMIT" ;
220
+ :reason "Permitted: subject opted in and an ODRL/DPV policy matched (anonymised dataset in secure environment)." ;
221
+ :trace "urn:policy:research-aurora-diabetes:permit:odrl:permission_matched" .
222
+ } .
223
+
224
+ {
225
+ ?scenario :purpose hlth:InsuranceManagement .
226
+ } => {
227
+ ?scenario :matchedProhibition :policy_deny_insurance ;
228
+ :decision "DENY" ;
229
+ :reason "Denied: the requested purpose (insurance management) is prohibited by policy." ;
230
+ :trace "deny:prohibited_purpose" ;
231
+ :trace "urn:policy:deny-insurance:deny:odrl:prohibition_matched" .
232
+ } .
233
+
234
+ {
235
+ ?scenario :purpose ehds:TrainTestAndEvaluateAISystemsAlgorithms ;
236
+ :subjectOptOut "yes" .
237
+ } => {
238
+ ?scenario :decision "DENY" ;
239
+ :reason "Denied: you opted out of your data being used to train AI systems." ;
240
+ :trace "deny:subject_opted_out_ai_training" .
241
+ } .
242
+
243
+ {
244
+ :scenario_C :purpose ehds:EnsureQualitySafetyHealthcare .
245
+ } => {
246
+ :scenario_C :decision "DENY" ;
247
+ :reason "Denied: no policy matched (purpose, environment, TOMs, or categories out of scope)." ;
248
+ :trace "urn:policy:qi-2025-aurora:deny:odrl:no_permission_matched" .
249
+ } .
250
+
251
+ # ------------
252
+ # Check values
253
+ # ------------
254
+
255
+ # Scenario A
256
+ { :scenario_A :decision "PERMIT" . } => { :scenario_A
257
+ :checkC1 "SKIPPED - not a prohibited purpose" ;
258
+ :checkC2 "OK - clinician" ;
259
+ :checkC3 "OK - care-team linked" ;
260
+ :checkC4 "SKIPPED" ;
261
+ :checkC5 "OK - operator=isAnyOf, allowed=[\"https://example.org/health#PATIENT_SUMMARY\",\"https://example.org/health#LAB_RESULTS\"], requested=[\"https://example.org/health#PATIENT_SUMMARY\"]" ;
262
+ :checkC6 "SKIPPED - no prohibition matched" ;
263
+ :checkC7 "OK - trace shows matching permission" ;
264
+ :checkC8 "SKIPPED - no matched policy or no duties" ;
265
+ :checkC9 "SKIPPED - policy has no environment constraint" ;
266
+ :checkC10Text "INFO - matched policy: urn:policy:primary-care-001" . } .
267
+
268
+ # Scenario B
269
+ { :scenario_B :decision "PERMIT" . } => { :scenario_B
270
+ :checkC1 "SKIPPED - not a prohibited purpose" ;
271
+ :checkC2 "SKIPPED" ;
272
+ :checkC3 "SKIPPED" ;
273
+ :checkC4 "OK - opt-in present and policy matched" ;
274
+ :checkC5 "OK - operator=isAllOf, allowed=[\"https://example.org/health#LAB_RESULTS\",\"https://example.org/health#PATIENT_SUMMARY\"], requested=[\"https://example.org/health#LAB_RESULTS\",\"https://example.org/health#PATIENT_SUMMARY\"]" ;
275
+ :checkC6 "SKIPPED - no prohibition matched" ;
276
+ :checkC7 "OK - trace shows matching permission" ;
277
+ :checkC8 "INFO - duties attached: duty:https://w3id.org/dpv/legal/eu/ehds#requireConsent, duty:https://w3id.org/dpv/legal/eu/ehds#noExfiltration" ;
278
+ :checkC9 "OK - operator=eq, allowed=\"secure_env\", requested=\"secure_env\"" ;
279
+ :checkC10Text "INFO - matched policy: urn:policy:qi-2025-aurora" . } .
280
+
281
+ # Scenario C
282
+ { :scenario_C :decision "DENY" . } => { :scenario_C
283
+ :checkC1 "SKIPPED - not a prohibited purpose" ;
284
+ :checkC2 "SKIPPED" ;
285
+ :checkC3 "SKIPPED" ;
286
+ :checkC4 "OK - denied because opt-in missing or no policy match" ;
287
+ :checkC5 "SKIPPED" ;
288
+ :checkC6 "SKIPPED - no prohibition matched" ;
289
+ :checkC7 "SKIPPED" ;
290
+ :checkC8 "SKIPPED - no matched policy or no duties" ;
291
+ :checkC9 "SKIPPED" ;
292
+ :checkC10Text "SKIPPED - no matched policy" . } .
293
+
294
+ # Scenario D
295
+ { :scenario_D :decision "DENY" . } => { :scenario_D
296
+ :checkC1 "OK - denied prohibited purpose" ;
297
+ :checkC2 "SKIPPED" ;
298
+ :checkC3 "SKIPPED" ;
299
+ :checkC4 "SKIPPED" ;
300
+ :checkC5 "SKIPPED" ;
301
+ :checkC6 "OK - denied due to prohibition" ;
302
+ :checkC7 "SKIPPED" ;
303
+ :checkC8 "SKIPPED - no matched policy or no duties" ;
304
+ :checkC9 "SKIPPED" ;
305
+ :checkC10Text "SKIPPED - no matched policy" . } .
306
+
307
+ # Scenario E
308
+ { :scenario_E :decision "PERMIT" . } => { :scenario_E
309
+ :checkC1 "SKIPPED - not a prohibited purpose" ;
310
+ :checkC2 "OK - clinician" ;
311
+ :checkC3 "OK - care-team linked" ;
312
+ :checkC4 "SKIPPED" ;
313
+ :checkC5 "OK - operator=isAnyOf, allowed=[\"https://example.org/health#PATIENT_SUMMARY\",\"https://example.org/health#LAB_RESULTS\"], requested=[\"https://example.org/health#LAB_RESULTS\"]" ;
314
+ :checkC6 "SKIPPED - no prohibition matched" ;
315
+ :checkC7 "OK - trace shows matching permission" ;
316
+ :checkC8 "SKIPPED - no matched policy or no duties" ;
317
+ :checkC9 "SKIPPED - policy has no environment constraint" ;
318
+ :checkC10Text "INFO - matched policy: urn:policy:primary-care-001" . } .
319
+
320
+ # Scenario F
321
+ { :scenario_F :decision "PERMIT" . } => { :scenario_F
322
+ :checkC1 "SKIPPED - not a prohibited purpose" ;
323
+ :checkC2 "SKIPPED" ;
324
+ :checkC3 "SKIPPED" ;
325
+ :checkC4 "OK - opt-in present and policy matched" ;
326
+ :checkC5 "OK - operator=isAnyOf, allowed=[\"https://example.org/health#LAB_RESULTS\",\"https://example.org/health#PATIENT_SUMMARY\",\"https://example.org/health#IMAGING_REPORT\"], requested=[\"https://example.org/health#PATIENT_SUMMARY\",\"https://example.org/health#LAB_RESULTS\"]" ;
327
+ :checkC6 "SKIPPED - no prohibition matched" ;
328
+ :checkC7 "OK - trace shows matching permission" ;
329
+ :checkC8 "INFO - duties attached: duty:https://w3id.org/dpv/legal/eu/ehds#annualOutcomeReport, duty:https://w3id.org/dpv/legal/eu/ehds#noReidentification, duty:https://w3id.org/dpv/legal/eu/ehds#noExfiltration" ;
330
+ :checkC9 "OK - operator=eq, allowed=\"secure_env\", requested=\"secure_env\"" ;
331
+ :checkC10Text "INFO - matched policy: urn:policy:research-aurora-diabetes" . } .
332
+
333
+ # Scenario G
334
+ { :scenario_G :decision "DENY" . } => { :scenario_G
335
+ :checkC1 "SKIPPED - not a prohibited purpose" ;
336
+ :checkC2 "SKIPPED" ;
337
+ :checkC3 "SKIPPED" ;
338
+ :checkC4 "OK - denied because opt-in missing or no policy match" ;
339
+ :checkC5 "SKIPPED" ;
340
+ :checkC6 "SKIPPED - no prohibition matched" ;
341
+ :checkC7 "SKIPPED" ;
342
+ :checkC8 "SKIPPED - no matched policy or no duties" ;
343
+ :checkC9 "SKIPPED" ;
344
+ :checkC10Text "SKIPPED - no matched policy" . } .
345
+
346
+ # ------------
347
+ # Presentation
348
+ # ------------
349
+
350
+ # Emit one complete output block per scenario to avoid ordering issues between
351
+ # separately derived log:outputString facts.
352
+
353
+ :out_000_intro log:outputString "AuroraCare — Purpose-based Medical Data Exchange\n\n" .
354
+
355
+ { :scenario_A :decision "PERMIT" . } => {
356
+ :out_010_A log:outputString """=== A – Primary care visit ===
357
+ Clinician in the patient's care team accessing the patient summary for primary care management.
358
+
359
+ Answer
360
+ PERMIT
361
+
362
+ Reason Why
363
+ Permitted: clinician in the patient's care team, and the primary-care policy matched.
364
+
365
+ Check
366
+ C1 SKIPPED - not a prohibited purpose
367
+ C2 OK - clinician
368
+ C3 OK - care-team linked
369
+ C4 SKIPPED
370
+ C5 OK - operator=isAnyOf, allowed=["https://example.org/health#PATIENT_SUMMARY","https://example.org/health#LAB_RESULTS"], requested=["https://example.org/health#PATIENT_SUMMARY"]
371
+ C6 SKIPPED - no prohibition matched
372
+ C7 OK - trace shows matching permission
373
+ C8 SKIPPED - no matched policy or no duties
374
+ C9 SKIPPED - policy has no environment constraint
375
+ C10 INFO - matched policy: urn:policy:primary-care-001
376
+
377
+ """ .
378
+ } .
379
+
380
+ { :scenario_B :decision "PERMIT" . } => {
381
+ :out_020_B log:outputString """=== B – Quality improvement (in scope) ===
382
+ QI analyst using lab results + summary in a secure environment.
383
+
384
+ Answer
385
+ PERMIT
386
+
387
+ Reason Why
388
+ Permitted: ODRL/DPV policy matched for secondary use.
389
+
390
+ Check
391
+ C1 SKIPPED - not a prohibited purpose
392
+ C2 SKIPPED
393
+ C3 SKIPPED
394
+ C4 OK - opt-in present and policy matched
395
+ C5 OK - operator=isAllOf, allowed=["https://example.org/health#LAB_RESULTS","https://example.org/health#PATIENT_SUMMARY"], requested=["https://example.org/health#LAB_RESULTS","https://example.org/health#PATIENT_SUMMARY"]
396
+ C6 SKIPPED - no prohibition matched
397
+ C7 OK - trace shows matching permission
398
+ C8 INFO - duties attached: duty:https://w3id.org/dpv/legal/eu/ehds#requireConsent, duty:https://w3id.org/dpv/legal/eu/ehds#noExfiltration
399
+ C9 OK - operator=eq, allowed="secure_env", requested="secure_env"
400
+ C10 INFO - matched policy: urn:policy:qi-2025-aurora
401
+
402
+ """ .
403
+ } .
404
+
405
+ { :scenario_C :decision "DENY" . } => {
406
+ :out_030_C log:outputString """=== C – Quality improvement (out of scope) ===
407
+ QI analyst with only lab results; policy expects labs + summary.
408
+
409
+ Answer
410
+ DENY
411
+
412
+ Reason Why
413
+ Denied: no policy matched (purpose, environment, TOMs, or categories out of scope).
414
+
415
+ Check
416
+ C1 SKIPPED - not a prohibited purpose
417
+ C2 SKIPPED
418
+ C3 SKIPPED
419
+ C4 OK - denied because opt-in missing or no policy match
420
+ C5 SKIPPED
421
+ C6 SKIPPED - no prohibition matched
422
+ C7 SKIPPED
423
+ C8 SKIPPED - no matched policy or no duties
424
+ C9 SKIPPED
425
+ C10 SKIPPED - no matched policy
426
+
427
+ """ .
428
+ } .
429
+
430
+ { :scenario_D :decision "DENY" . } => {
431
+ :out_040_D log:outputString """=== D – Insurance management ===
432
+ Insurance bot attempting to use health data for insurance management (prohibited purpose).
433
+
434
+ Answer
435
+ DENY
436
+
437
+ Reason Why
438
+ Denied: the requested purpose (insurance management) is prohibited by policy.
439
+
440
+ Check
441
+ C1 OK - denied prohibited purpose
442
+ C2 SKIPPED
443
+ C3 SKIPPED
444
+ C4 SKIPPED
445
+ C5 SKIPPED
446
+ C6 OK - denied due to prohibition
447
+ C7 SKIPPED
448
+ C8 SKIPPED - no matched policy or no duties
449
+ C9 SKIPPED
450
+ C10 SKIPPED - no matched policy
451
+
452
+ """ .
453
+ } .
454
+
455
+ { :scenario_E :decision "PERMIT" . } => {
456
+ :out_050_E log:outputString """=== E – GP checks labs ===
457
+ GP for the same patient checking lab results via the API gateway.
458
+
459
+ Answer
460
+ PERMIT
461
+
462
+ Reason Why
463
+ Permitted: clinician in the patient's care team, and the primary-care policy matched.
464
+
465
+ Check
466
+ C1 SKIPPED - not a prohibited purpose
467
+ C2 OK - clinician
468
+ C3 OK - care-team linked
469
+ C4 SKIPPED
470
+ C5 OK - operator=isAnyOf, allowed=["https://example.org/health#PATIENT_SUMMARY","https://example.org/health#LAB_RESULTS"], requested=["https://example.org/health#LAB_RESULTS"]
471
+ C6 SKIPPED - no prohibition matched
472
+ C7 OK - trace shows matching permission
473
+ C8 SKIPPED - no matched policy or no duties
474
+ C9 SKIPPED - policy has no environment constraint
475
+ C10 INFO - matched policy: urn:policy:primary-care-001
476
+
477
+ """ .
478
+ } .
479
+
480
+ { :scenario_F :decision "PERMIT" . } => {
481
+ :out_060_F log:outputString """=== F – Research on anonymised dataset ===
482
+ Researcher using anonymised labs + summary in a secure environment, with opt-in.
483
+
484
+ Answer
485
+ PERMIT
486
+
487
+ Reason Why
488
+ Permitted: subject opted in and an ODRL/DPV policy matched (anonymised dataset in secure environment).
489
+
490
+ Check
491
+ C1 SKIPPED - not a prohibited purpose
492
+ C2 SKIPPED
493
+ C3 SKIPPED
494
+ C4 OK - opt-in present and policy matched
495
+ C5 OK - operator=isAnyOf, allowed=["https://example.org/health#LAB_RESULTS","https://example.org/health#PATIENT_SUMMARY","https://example.org/health#IMAGING_REPORT"], requested=["https://example.org/health#PATIENT_SUMMARY","https://example.org/health#LAB_RESULTS"]
496
+ C6 SKIPPED - no prohibition matched
497
+ C7 OK - trace shows matching permission
498
+ C8 INFO - duties attached: duty:https://w3id.org/dpv/legal/eu/ehds#annualOutcomeReport, duty:https://w3id.org/dpv/legal/eu/ehds#noReidentification, duty:https://w3id.org/dpv/legal/eu/ehds#noExfiltration
499
+ C9 OK - operator=eq, allowed="secure_env", requested="secure_env"
500
+ C10 INFO - matched policy: urn:policy:research-aurora-diabetes
501
+
502
+ """ .
503
+ } .
504
+
505
+ { :scenario_G :decision "DENY" . } => {
506
+ :out_070_G log:outputString """=== G – AI training (opt-out) ===
507
+ Data user wants to train AI, but the subject opted out of AI training.
508
+
509
+ Answer
510
+ DENY
511
+
512
+ Reason Why
513
+ Denied: you opted out of your data being used to train AI systems.
514
+
515
+ Check
516
+ C1 SKIPPED - not a prohibited purpose
517
+ C2 SKIPPED
518
+ C3 SKIPPED
519
+ C4 OK - denied because opt-in missing or no policy match
520
+ C5 SKIPPED
521
+ C6 SKIPPED - no prohibition matched
522
+ C7 SKIPPED
523
+ C8 SKIPPED - no matched policy or no duties
524
+ C9 SKIPPED
525
+ C10 SKIPPED - no matched policy
526
+
527
+ """ .
528
+ } .