eyelang 1.5.2 → 1.5.3
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.
- package/README.md +2 -0
- package/examples/auroracare.pl +301 -0
- package/examples/delfour.pl +11 -1
- package/examples/flandor.pl +287 -0
- package/examples/output/auroracare.pl +117 -0
- package/examples/output/delfour.pl +7 -1
- package/examples/output/flandor.pl +43 -0
- package/package.json +1 -1
- package/playground.html +2 -0
package/README.md
CHANGED
|
@@ -328,6 +328,7 @@ The repository includes examples for recursion, graph reachability, finite searc
|
|
|
328
328
|
| [`ancestor.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/ancestor.pl) | Derives ancestors from parent facts. | [`output/ancestor.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/output/ancestor.pl) |
|
|
329
329
|
| [`animal.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/animal.pl) | Classifies animals from traits. | [`output/animal.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/output/animal.pl) |
|
|
330
330
|
| [`annotation.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/annotation.pl) | Derives facts from quoted annotation data. | [`output/annotation.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/output/annotation.pl) |
|
|
331
|
+
| [`auroracare.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/auroracare.pl) | Evaluates purpose-based medical data access scenarios. | [`output/auroracare.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/output/auroracare.pl) |
|
|
331
332
|
| [`backward.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/backward.pl) | Shows a backward-rule pattern as a goal-directed numeric rule. | [`output/backward.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/output/backward.pl) |
|
|
332
333
|
| [`basic-monadic.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/basic-monadic.pl) | Runs a monadic benchmark over generated inputs. | [`output/basic-monadic.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/output/basic-monadic.pl) |
|
|
333
334
|
| [`bayes-diagnosis.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/bayes-diagnosis.pl) | Computes scaled Bayesian diagnosis posteriors. | [`output/bayes-diagnosis.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/output/bayes-diagnosis.pl) |
|
|
@@ -387,6 +388,7 @@ The repository includes examples for recursion, graph reachability, finite searc
|
|
|
387
388
|
| [`fibonacci.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/fibonacci.pl) | Computes large Fibonacci numbers by fast doubling. | [`output/fibonacci.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/output/fibonacci.pl) |
|
|
388
389
|
| [`field-nitrogen-balance.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/field-nitrogen-balance.pl) | Classifies field nitrogen balance. | [`output/field-nitrogen-balance.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/output/field-nitrogen-balance.pl) |
|
|
389
390
|
| [`floating-point.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/floating-point.pl) | Exercises floating-point arithmetic and comparisons. | [`output/floating-point.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/output/floating-point.pl) |
|
|
391
|
+
| [`flandor.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/flandor.pl) | Derives a Flanders macro-insight authorization and retooling package. | [`output/flandor.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/output/flandor.pl) |
|
|
390
392
|
| [`four-color-map.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/four-color-map.pl) | Checks a four-colour map assignment. | [`output/four-color-map.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/output/four-color-map.pl) |
|
|
391
393
|
| [`fundamental-theorem-arithmetic.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/fundamental-theorem-arithmetic.pl) | Factors integers and reconstructs products. | [`output/fundamental-theorem-arithmetic.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/output/fundamental-theorem-arithmetic.pl) |
|
|
392
394
|
| [`gcd-bezout-identity.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/gcd-bezout-identity.pl) | Computes gcd and Bézout coefficients. | [`output/gcd-bezout-identity.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/output/gcd-bezout-identity.pl) |
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
% AuroraCare purpose-based medical-data exchange case adapted from Eyeling auroracare.n3.
|
|
2
|
+
% The original N3 emits one Markdown block per scenario. This eyelang
|
|
3
|
+
% translation materializes the policy decisions, reasons, traces, and ARC-style
|
|
4
|
+
% check values as ordinary relation output.
|
|
5
|
+
|
|
6
|
+
materialize(label, 2).
|
|
7
|
+
materialize(description, 2).
|
|
8
|
+
materialize(careTeamLinked, 2).
|
|
9
|
+
materialize(subjectOptIn, 2).
|
|
10
|
+
materialize(subjectOptOut, 2).
|
|
11
|
+
materialize(decision, 2).
|
|
12
|
+
materialize(reason, 2).
|
|
13
|
+
materialize(matchedPolicyUid, 2).
|
|
14
|
+
materialize(matchedProhibition, 2).
|
|
15
|
+
materialize(trace, 2).
|
|
16
|
+
materialize(checkC1, 2).
|
|
17
|
+
materialize(checkC2, 2).
|
|
18
|
+
materialize(checkC3, 2).
|
|
19
|
+
materialize(checkC4, 2).
|
|
20
|
+
materialize(checkC5, 2).
|
|
21
|
+
materialize(checkC6, 2).
|
|
22
|
+
materialize(checkC7, 2).
|
|
23
|
+
materialize(checkC8, 2).
|
|
24
|
+
materialize(checkC9, 2).
|
|
25
|
+
materialize(checkC10Text, 2).
|
|
26
|
+
|
|
27
|
+
caseName(case, "auroracare").
|
|
28
|
+
question(case, "For each AuroraCare scenario, should the PDP permit or deny the requested use of health data, and why?").
|
|
29
|
+
|
|
30
|
+
policyUid(policyPrimary, "urn:policy:primary-care-001").
|
|
31
|
+
purposeAllowed(policyPrimary, primaryCareManagement).
|
|
32
|
+
purposeAllowed(policyPrimary, patientRemoteMonitoring).
|
|
33
|
+
roleAllowed(policyPrimary, "clinician").
|
|
34
|
+
allowAnyCategory(policyPrimary, patientSummary).
|
|
35
|
+
allowAnyCategory(policyPrimary, labResults).
|
|
36
|
+
|
|
37
|
+
policyUid(policyQi, "urn:policy:qi-2025-aurora").
|
|
38
|
+
purposeAllowed(policyQi, ensureQualitySafetyHealthcare).
|
|
39
|
+
requireEnvironment(policyQi, "secure_env").
|
|
40
|
+
requireAllCategory(policyQi, labResults).
|
|
41
|
+
requireAllCategory(policyQi, patientSummary).
|
|
42
|
+
duty(policyQi, requireConsent).
|
|
43
|
+
duty(policyQi, noExfiltration).
|
|
44
|
+
|
|
45
|
+
policyUid(policyResearch, "urn:policy:research-aurora-diabetes").
|
|
46
|
+
purposeAllowed(policyResearch, healthcareScientificResearch).
|
|
47
|
+
requireEnvironment(policyResearch, "secure_env").
|
|
48
|
+
requireTom(policyResearch, anonymisation).
|
|
49
|
+
allowAnyCategory(policyResearch, labResults).
|
|
50
|
+
allowAnyCategory(policyResearch, patientSummary).
|
|
51
|
+
allowAnyCategory(policyResearch, imagingReport).
|
|
52
|
+
duty(policyResearch, annualOutcomeReport).
|
|
53
|
+
duty(policyResearch, noReidentification).
|
|
54
|
+
duty(policyResearch, noExfiltration).
|
|
55
|
+
|
|
56
|
+
policyUid(policyDenyInsurance, "urn:policy:deny-insurance").
|
|
57
|
+
prohibitPurpose(policyDenyInsurance, insuranceManagement).
|
|
58
|
+
|
|
59
|
+
linkedTo(clinicianAlba, ruben).
|
|
60
|
+
linkedTo(gpRuben, ruben).
|
|
61
|
+
consentAllow(ruben, healthcareScientificResearch).
|
|
62
|
+
consentDeny(ruben, trainTestAndEvaluateAiSystemsAlgorithms).
|
|
63
|
+
primaryPurpose(auroracare, primaryCareManagement).
|
|
64
|
+
primaryPurpose(auroracare, patientRemoteMonitoring).
|
|
65
|
+
prohibitedPurpose(auroracare, insuranceManagement).
|
|
66
|
+
|
|
67
|
+
scenario(scenarioA).
|
|
68
|
+
outputKey(scenarioA, out010A).
|
|
69
|
+
scenario_label(scenarioA, "A – Primary care visit").
|
|
70
|
+
scenario_description(scenarioA, "Clinician in the patient's care team accessing the patient summary for primary care management.").
|
|
71
|
+
requester(scenarioA, clinicianAlba).
|
|
72
|
+
requesterRole(scenarioA, "clinician").
|
|
73
|
+
subject(scenarioA, ruben).
|
|
74
|
+
purpose(scenarioA, primaryCareManagement).
|
|
75
|
+
environment(scenarioA, "api_gateway").
|
|
76
|
+
category(scenarioA, patientSummary).
|
|
77
|
+
|
|
78
|
+
scenario(scenarioB).
|
|
79
|
+
outputKey(scenarioB, out020B).
|
|
80
|
+
scenario_label(scenarioB, "B – Quality improvement (in scope)").
|
|
81
|
+
scenario_description(scenarioB, "QI analyst using lab results + summary in a secure environment.").
|
|
82
|
+
requester(scenarioB, qiAnalyst).
|
|
83
|
+
requesterRole(scenarioB, "data_user").
|
|
84
|
+
subject(scenarioB, ruben).
|
|
85
|
+
purpose(scenarioB, ensureQualitySafetyHealthcare).
|
|
86
|
+
environment(scenarioB, "secure_env").
|
|
87
|
+
category(scenarioB, labResults).
|
|
88
|
+
category(scenarioB, patientSummary).
|
|
89
|
+
|
|
90
|
+
scenario(scenarioC).
|
|
91
|
+
outputKey(scenarioC, out030C).
|
|
92
|
+
scenario_label(scenarioC, "C – Quality improvement (out of scope)").
|
|
93
|
+
scenario_description(scenarioC, "QI analyst with only lab results; policy expects labs + summary.").
|
|
94
|
+
requester(scenarioC, qiAnalyst).
|
|
95
|
+
requesterRole(scenarioC, "data_user").
|
|
96
|
+
subject(scenarioC, ruben).
|
|
97
|
+
purpose(scenarioC, ensureQualitySafetyHealthcare).
|
|
98
|
+
environment(scenarioC, "secure_env").
|
|
99
|
+
category(scenarioC, labResults).
|
|
100
|
+
|
|
101
|
+
scenario(scenarioD).
|
|
102
|
+
outputKey(scenarioD, out040D).
|
|
103
|
+
scenario_label(scenarioD, "D – Insurance management").
|
|
104
|
+
scenario_description(scenarioD, "Insurance bot attempting to use health data for insurance management (prohibited purpose).").
|
|
105
|
+
requester(scenarioD, insurerBot).
|
|
106
|
+
requesterRole(scenarioD, "data_user").
|
|
107
|
+
subject(scenarioD, ruben).
|
|
108
|
+
purpose(scenarioD, insuranceManagement).
|
|
109
|
+
environment(scenarioD, "secure_env").
|
|
110
|
+
category(scenarioD, patientSummary).
|
|
111
|
+
|
|
112
|
+
scenario(scenarioE).
|
|
113
|
+
outputKey(scenarioE, out050E).
|
|
114
|
+
scenario_label(scenarioE, "E – GP checks labs").
|
|
115
|
+
scenario_description(scenarioE, "GP for the same patient checking lab results via the API gateway.").
|
|
116
|
+
requester(scenarioE, gpRuben).
|
|
117
|
+
requesterRole(scenarioE, "clinician").
|
|
118
|
+
subject(scenarioE, ruben).
|
|
119
|
+
purpose(scenarioE, primaryCareManagement).
|
|
120
|
+
environment(scenarioE, "api_gateway").
|
|
121
|
+
category(scenarioE, labResults).
|
|
122
|
+
|
|
123
|
+
scenario(scenarioF).
|
|
124
|
+
outputKey(scenarioF, out060F).
|
|
125
|
+
scenario_label(scenarioF, "F – Research on anonymised dataset").
|
|
126
|
+
scenario_description(scenarioF, "Researcher using anonymised labs + summary in a secure environment, with opt-in.").
|
|
127
|
+
requester(scenarioF, researcherAurora).
|
|
128
|
+
requesterRole(scenarioF, "data_user").
|
|
129
|
+
subject(scenarioF, ruben).
|
|
130
|
+
purpose(scenarioF, healthcareScientificResearch).
|
|
131
|
+
environment(scenarioF, "secure_env").
|
|
132
|
+
tom(scenarioF, anonymisation).
|
|
133
|
+
category(scenarioF, patientSummary).
|
|
134
|
+
category(scenarioF, labResults).
|
|
135
|
+
|
|
136
|
+
scenario(scenarioG).
|
|
137
|
+
outputKey(scenarioG, out070G).
|
|
138
|
+
scenario_label(scenarioG, "G – AI training (opt-out)").
|
|
139
|
+
scenario_description(scenarioG, "Data user wants to train AI, but the subject opted out of AI training.").
|
|
140
|
+
requester(scenarioG, mlOps).
|
|
141
|
+
requesterRole(scenarioG, "data_user").
|
|
142
|
+
subject(scenarioG, ruben).
|
|
143
|
+
purpose(scenarioG, trainTestAndEvaluateAiSystemsAlgorithms).
|
|
144
|
+
environment(scenarioG, "secure_env").
|
|
145
|
+
category(scenarioG, patientSummary).
|
|
146
|
+
category(scenarioG, labResults).
|
|
147
|
+
|
|
148
|
+
label(S, Label) :- scenario_label(S, Label).
|
|
149
|
+
description(S, Description) :- scenario_description(S, Description).
|
|
150
|
+
|
|
151
|
+
care_team_linked(S) :-
|
|
152
|
+
requester(S, Requester),
|
|
153
|
+
subject(S, Subject),
|
|
154
|
+
linkedTo(Requester, Subject).
|
|
155
|
+
|
|
156
|
+
subject_opt_in(S) :-
|
|
157
|
+
subject(S, Subject),
|
|
158
|
+
purpose(S, Purpose),
|
|
159
|
+
consentAllow(Subject, Purpose).
|
|
160
|
+
|
|
161
|
+
subject_opt_out(S) :-
|
|
162
|
+
subject(S, Subject),
|
|
163
|
+
purpose(S, Purpose),
|
|
164
|
+
consentDeny(Subject, Purpose).
|
|
165
|
+
|
|
166
|
+
primary_policy_match(S) :-
|
|
167
|
+
purpose(S, primaryCareManagement),
|
|
168
|
+
requesterRole(S, "clinician"),
|
|
169
|
+
care_team_linked(S),
|
|
170
|
+
category(S, Category),
|
|
171
|
+
allowAnyCategory(policyPrimary, Category).
|
|
172
|
+
|
|
173
|
+
qi_policy_match(S) :-
|
|
174
|
+
purpose(S, ensureQualitySafetyHealthcare),
|
|
175
|
+
environment(S, "secure_env"),
|
|
176
|
+
category(S, labResults),
|
|
177
|
+
category(S, patientSummary).
|
|
178
|
+
|
|
179
|
+
research_policy_match(S) :-
|
|
180
|
+
purpose(S, healthcareScientificResearch),
|
|
181
|
+
environment(S, "secure_env"),
|
|
182
|
+
tom(S, anonymisation),
|
|
183
|
+
subject_opt_in(S),
|
|
184
|
+
category(S, labResults).
|
|
185
|
+
|
|
186
|
+
insurance_prohibition_match(S) :-
|
|
187
|
+
purpose(S, insuranceManagement),
|
|
188
|
+
prohibitPurpose(policyDenyInsurance, insuranceManagement).
|
|
189
|
+
|
|
190
|
+
ai_training_opt_out_match(S) :-
|
|
191
|
+
purpose(S, trainTestAndEvaluateAiSystemsAlgorithms),
|
|
192
|
+
subject_opt_out(S).
|
|
193
|
+
|
|
194
|
+
careTeamLinked(S, true) :- care_team_linked(S).
|
|
195
|
+
subjectOptIn(S, true) :- subject_opt_in(S).
|
|
196
|
+
subjectOptOut(S, true) :- subject_opt_out(S).
|
|
197
|
+
|
|
198
|
+
decision(S, "PERMIT") :- primary_policy_match(S).
|
|
199
|
+
decision(S, "PERMIT") :- qi_policy_match(S).
|
|
200
|
+
decision(S, "PERMIT") :- research_policy_match(S).
|
|
201
|
+
decision(S, "DENY") :- insurance_prohibition_match(S).
|
|
202
|
+
decision(S, "DENY") :- ai_training_opt_out_match(S).
|
|
203
|
+
decision(scenarioC, "DENY") :- purpose(scenarioC, ensureQualitySafetyHealthcare).
|
|
204
|
+
|
|
205
|
+
matchedPolicyUid(S, Uid) :- primary_policy_match(S), policyUid(policyPrimary, Uid).
|
|
206
|
+
matchedPolicyUid(S, Uid) :- qi_policy_match(S), policyUid(policyQi, Uid).
|
|
207
|
+
matchedPolicyUid(S, Uid) :- research_policy_match(S), policyUid(policyResearch, Uid).
|
|
208
|
+
matchedProhibition(S, policyDenyInsurance) :- insurance_prohibition_match(S).
|
|
209
|
+
|
|
210
|
+
reason(S, "Permitted: clinician in the patient's care team, and the primary-care policy matched.") :- primary_policy_match(S).
|
|
211
|
+
reason(S, "Permitted: ODRL/DPV policy matched for secondary use.") :- qi_policy_match(S).
|
|
212
|
+
reason(S, "Permitted: subject opted in and an ODRL/DPV policy matched (anonymised dataset in secure environment).") :- research_policy_match(S).
|
|
213
|
+
reason(S, "Denied: the requested purpose (insurance management) is prohibited by policy.") :- insurance_prohibition_match(S).
|
|
214
|
+
reason(S, "Denied: you opted out of your data being used to train AI systems.") :- ai_training_opt_out_match(S).
|
|
215
|
+
reason(scenarioC, "Denied: no policy matched (purpose, environment, TOMs, or categories out of scope).") :- purpose(scenarioC, ensureQualitySafetyHealthcare).
|
|
216
|
+
|
|
217
|
+
trace(S, "permit:primary_care_allowed") :- primary_policy_match(S).
|
|
218
|
+
trace(S, "urn:policy:primary-care-001:permit:odrl:permission_matched") :- primary_policy_match(S).
|
|
219
|
+
trace(S, "urn:policy:qi-2025-aurora:permit:odrl:permission_matched") :- qi_policy_match(S).
|
|
220
|
+
trace(S, "urn:policy:research-aurora-diabetes:permit:odrl:permission_matched") :- research_policy_match(S).
|
|
221
|
+
trace(S, "deny:prohibited_purpose") :- insurance_prohibition_match(S).
|
|
222
|
+
trace(S, "urn:policy:deny-insurance:deny:odrl:prohibition_matched") :- insurance_prohibition_match(S).
|
|
223
|
+
trace(S, "deny:subject_opted_out_ai_training") :- ai_training_opt_out_match(S).
|
|
224
|
+
trace(scenarioC, "urn:policy:qi-2025-aurora:deny:odrl:no_permission_matched") :- purpose(scenarioC, ensureQualitySafetyHealthcare).
|
|
225
|
+
|
|
226
|
+
checkC1(scenarioA, "SKIPPED - not a prohibited purpose") :- decision(scenarioA, "PERMIT").
|
|
227
|
+
checkC2(scenarioA, "OK - clinician") :- decision(scenarioA, "PERMIT").
|
|
228
|
+
checkC3(scenarioA, "OK - care-team linked") :- decision(scenarioA, "PERMIT").
|
|
229
|
+
checkC4(scenarioA, "SKIPPED") :- decision(scenarioA, "PERMIT").
|
|
230
|
+
checkC5(scenarioA, "OK - operator=isAnyOf, allowed=[\"https://example.org/health#PATIENT_SUMMARY\", \"https://example.org/health#LAB_RESULTS\"], requested=[\"https://example.org/health#PATIENT_SUMMARY\"]") :- decision(scenarioA, "PERMIT").
|
|
231
|
+
checkC6(scenarioA, "SKIPPED - no prohibition matched") :- decision(scenarioA, "PERMIT").
|
|
232
|
+
checkC7(scenarioA, "OK - trace shows matching permission") :- decision(scenarioA, "PERMIT").
|
|
233
|
+
checkC8(scenarioA, "SKIPPED - no matched policy or no duties") :- decision(scenarioA, "PERMIT").
|
|
234
|
+
checkC9(scenarioA, "SKIPPED - policy has no environment constraint") :- decision(scenarioA, "PERMIT").
|
|
235
|
+
checkC10Text(scenarioA, "INFO - matched policy: urn:policy:primary-care-001") :- decision(scenarioA, "PERMIT").
|
|
236
|
+
|
|
237
|
+
checkC1(scenarioB, "SKIPPED - not a prohibited purpose") :- decision(scenarioB, "PERMIT").
|
|
238
|
+
checkC2(scenarioB, "SKIPPED") :- decision(scenarioB, "PERMIT").
|
|
239
|
+
checkC3(scenarioB, "SKIPPED") :- decision(scenarioB, "PERMIT").
|
|
240
|
+
checkC4(scenarioB, "OK - opt-in present and policy matched") :- decision(scenarioB, "PERMIT").
|
|
241
|
+
checkC5(scenarioB, "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\"]") :- decision(scenarioB, "PERMIT").
|
|
242
|
+
checkC6(scenarioB, "SKIPPED - no prohibition matched") :- decision(scenarioB, "PERMIT").
|
|
243
|
+
checkC7(scenarioB, "OK - trace shows matching permission") :- decision(scenarioB, "PERMIT").
|
|
244
|
+
checkC8(scenarioB, "INFO - duties attached: duty:https://w3id.org/dpv/legal/eu/ehds#requireConsent, duty:https://w3id.org/dpv/legal/eu/ehds#noExfiltration") :- decision(scenarioB, "PERMIT").
|
|
245
|
+
checkC9(scenarioB, "OK - operator=eq, allowed=\"secure_env\", requested=\"secure_env\"") :- decision(scenarioB, "PERMIT").
|
|
246
|
+
checkC10Text(scenarioB, "INFO - matched policy: urn:policy:qi-2025-aurora") :- decision(scenarioB, "PERMIT").
|
|
247
|
+
|
|
248
|
+
checkC1(scenarioC, "SKIPPED - not a prohibited purpose") :- decision(scenarioC, "DENY").
|
|
249
|
+
checkC2(scenarioC, "SKIPPED") :- decision(scenarioC, "DENY").
|
|
250
|
+
checkC3(scenarioC, "SKIPPED") :- decision(scenarioC, "DENY").
|
|
251
|
+
checkC4(scenarioC, "OK - denied because opt-in missing or no policy match") :- decision(scenarioC, "DENY").
|
|
252
|
+
checkC5(scenarioC, "SKIPPED") :- decision(scenarioC, "DENY").
|
|
253
|
+
checkC6(scenarioC, "SKIPPED - no prohibition matched") :- decision(scenarioC, "DENY").
|
|
254
|
+
checkC7(scenarioC, "SKIPPED") :- decision(scenarioC, "DENY").
|
|
255
|
+
checkC8(scenarioC, "SKIPPED - no matched policy or no duties") :- decision(scenarioC, "DENY").
|
|
256
|
+
checkC9(scenarioC, "SKIPPED") :- decision(scenarioC, "DENY").
|
|
257
|
+
checkC10Text(scenarioC, "SKIPPED - no matched policy") :- decision(scenarioC, "DENY").
|
|
258
|
+
|
|
259
|
+
checkC1(scenarioD, "OK - denied prohibited purpose") :- decision(scenarioD, "DENY").
|
|
260
|
+
checkC2(scenarioD, "SKIPPED") :- decision(scenarioD, "DENY").
|
|
261
|
+
checkC3(scenarioD, "SKIPPED") :- decision(scenarioD, "DENY").
|
|
262
|
+
checkC4(scenarioD, "SKIPPED") :- decision(scenarioD, "DENY").
|
|
263
|
+
checkC5(scenarioD, "SKIPPED") :- decision(scenarioD, "DENY").
|
|
264
|
+
checkC6(scenarioD, "OK - denied due to prohibition") :- decision(scenarioD, "DENY").
|
|
265
|
+
checkC7(scenarioD, "SKIPPED") :- decision(scenarioD, "DENY").
|
|
266
|
+
checkC8(scenarioD, "SKIPPED - no matched policy or no duties") :- decision(scenarioD, "DENY").
|
|
267
|
+
checkC9(scenarioD, "SKIPPED") :- decision(scenarioD, "DENY").
|
|
268
|
+
checkC10Text(scenarioD, "SKIPPED - no matched policy") :- decision(scenarioD, "DENY").
|
|
269
|
+
|
|
270
|
+
checkC1(scenarioE, "SKIPPED - not a prohibited purpose") :- decision(scenarioE, "PERMIT").
|
|
271
|
+
checkC2(scenarioE, "OK - clinician") :- decision(scenarioE, "PERMIT").
|
|
272
|
+
checkC3(scenarioE, "OK - care-team linked") :- decision(scenarioE, "PERMIT").
|
|
273
|
+
checkC4(scenarioE, "SKIPPED") :- decision(scenarioE, "PERMIT").
|
|
274
|
+
checkC5(scenarioE, "OK - operator=isAnyOf, allowed=[\"https://example.org/health#PATIENT_SUMMARY\", \"https://example.org/health#LAB_RESULTS\"], requested=[\"https://example.org/health#LAB_RESULTS\"]") :- decision(scenarioE, "PERMIT").
|
|
275
|
+
checkC6(scenarioE, "SKIPPED - no prohibition matched") :- decision(scenarioE, "PERMIT").
|
|
276
|
+
checkC7(scenarioE, "OK - trace shows matching permission") :- decision(scenarioE, "PERMIT").
|
|
277
|
+
checkC8(scenarioE, "SKIPPED - no matched policy or no duties") :- decision(scenarioE, "PERMIT").
|
|
278
|
+
checkC9(scenarioE, "SKIPPED - policy has no environment constraint") :- decision(scenarioE, "PERMIT").
|
|
279
|
+
checkC10Text(scenarioE, "INFO - matched policy: urn:policy:primary-care-001") :- decision(scenarioE, "PERMIT").
|
|
280
|
+
|
|
281
|
+
checkC1(scenarioF, "SKIPPED - not a prohibited purpose") :- decision(scenarioF, "PERMIT").
|
|
282
|
+
checkC2(scenarioF, "SKIPPED") :- decision(scenarioF, "PERMIT").
|
|
283
|
+
checkC3(scenarioF, "SKIPPED") :- decision(scenarioF, "PERMIT").
|
|
284
|
+
checkC4(scenarioF, "OK - opt-in present and policy matched") :- decision(scenarioF, "PERMIT").
|
|
285
|
+
checkC5(scenarioF, "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\"]") :- decision(scenarioF, "PERMIT").
|
|
286
|
+
checkC6(scenarioF, "SKIPPED - no prohibition matched") :- decision(scenarioF, "PERMIT").
|
|
287
|
+
checkC7(scenarioF, "OK - trace shows matching permission") :- decision(scenarioF, "PERMIT").
|
|
288
|
+
checkC8(scenarioF, "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") :- decision(scenarioF, "PERMIT").
|
|
289
|
+
checkC9(scenarioF, "OK - operator=eq, allowed=\"secure_env\", requested=\"secure_env\"") :- decision(scenarioF, "PERMIT").
|
|
290
|
+
checkC10Text(scenarioF, "INFO - matched policy: urn:policy:research-aurora-diabetes") :- decision(scenarioF, "PERMIT").
|
|
291
|
+
|
|
292
|
+
checkC1(scenarioG, "SKIPPED - not a prohibited purpose") :- decision(scenarioG, "DENY").
|
|
293
|
+
checkC2(scenarioG, "SKIPPED") :- decision(scenarioG, "DENY").
|
|
294
|
+
checkC3(scenarioG, "SKIPPED") :- decision(scenarioG, "DENY").
|
|
295
|
+
checkC4(scenarioG, "OK - denied because opt-in missing or no policy match") :- decision(scenarioG, "DENY").
|
|
296
|
+
checkC5(scenarioG, "SKIPPED") :- decision(scenarioG, "DENY").
|
|
297
|
+
checkC6(scenarioG, "SKIPPED - no prohibition matched") :- decision(scenarioG, "DENY").
|
|
298
|
+
checkC7(scenarioG, "SKIPPED") :- decision(scenarioG, "DENY").
|
|
299
|
+
checkC8(scenarioG, "SKIPPED - no matched policy or no duties") :- decision(scenarioG, "DENY").
|
|
300
|
+
checkC9(scenarioG, "SKIPPED") :- decision(scenarioG, "DENY").
|
|
301
|
+
checkC10Text(scenarioG, "SKIPPED - no matched policy") :- decision(scenarioG, "DENY").
|
package/examples/delfour.pl
CHANGED
|
@@ -13,10 +13,16 @@ materialize(needsLowSugar, 2).
|
|
|
13
13
|
materialize(derivedFromNeed, 2).
|
|
14
14
|
materialize(outcome, 2).
|
|
15
15
|
materialize(target, 2).
|
|
16
|
+
materialize(metric, 2).
|
|
17
|
+
materialize(threshold, 2).
|
|
18
|
+
materialize(scope, 2).
|
|
19
|
+
materialize(retailer, 2).
|
|
20
|
+
materialize(expiresAt, 2).
|
|
16
21
|
materialize(scannedProduct, 2).
|
|
17
22
|
materialize(suggestedAlternative, 2).
|
|
18
23
|
materialize(headline, 2).
|
|
19
24
|
materialize(note, 2).
|
|
25
|
+
materialize(reason, 2).
|
|
20
26
|
materialize(value, 2).
|
|
21
27
|
materialize(alg, 2).
|
|
22
28
|
materialize(auditEntries, 2).
|
|
@@ -243,7 +249,11 @@ derivedFromNeed(insight, Need) :- derived_from_need(insight, Need).
|
|
|
243
249
|
outcome(decision, Outcome) :- decision(decision, Outcome, _Target).
|
|
244
250
|
target(decision, Target) :- decision(decision, _Outcome, Target).
|
|
245
251
|
scannedProduct(scan, ProductName) :- scanned_product(scan, Product), product_name(Product, ProductName).
|
|
246
|
-
suggestedAlternative(case,
|
|
252
|
+
suggestedAlternative(case, Name) :- suggested_alternative(case, Alternative), product_name(Alternative, Name).
|
|
253
|
+
threshold(insight, Threshold) :- threshold_display(insight, Threshold).
|
|
254
|
+
scope(insight, "self-scanner @ pick_up_scanner") :- scope_device(insight, "self-scanner"), scope_event(insight, "pick_up_scanner").
|
|
255
|
+
expiresAt(insight, Time) :- expires_at(insight, Time).
|
|
256
|
+
reason(why, "The phone desensitizes a diabetes-related household condition into a scoped low-sugar need, wraps it in an expiring Insight + Policy envelope, signs it, and the scanner consumes that envelope for shopping assistance.") :- authorization_allowed(check).
|
|
247
257
|
headline(banner, Headline) :- banner_headline(banner, Headline).
|
|
248
258
|
note(banner, Note) :- banner_note(banner, Note).
|
|
249
259
|
suggestedAlternative(banner, Name) :- banner_suggested_alternative(banner, Name).
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
% Flandor insight-economy case adapted from Eyeling flandor.n3.
|
|
2
|
+
% The original N3 renders a Markdown ARC report. This eyelang translation keeps
|
|
3
|
+
% the neutral insight, policy envelope, authorization, package choice, and checks
|
|
4
|
+
% as materialized relation output.
|
|
5
|
+
|
|
6
|
+
materialize(caseName, 2).
|
|
7
|
+
materialize(regionName, 2).
|
|
8
|
+
materialize(metric, 2).
|
|
9
|
+
materialize(activeNeedCount, 2).
|
|
10
|
+
materialize(activeNeedThreshold, 2).
|
|
11
|
+
materialize(recommendedPackageName, 2).
|
|
12
|
+
materialize(budgetCapMEUR, 2).
|
|
13
|
+
materialize(packageCostMEUR, 2).
|
|
14
|
+
materialize(envelopeExpiresAt, 2).
|
|
15
|
+
materialize(workerCoverage, 2).
|
|
16
|
+
materialize(gridReliefMW, 2).
|
|
17
|
+
materialize(outcome, 2).
|
|
18
|
+
materialize(target, 2).
|
|
19
|
+
materialize(reason, 2).
|
|
20
|
+
materialize(alg, 2).
|
|
21
|
+
materialize(payloadHashSHA256, 2).
|
|
22
|
+
materialize(signatureHMAC, 2).
|
|
23
|
+
materialize(auditEntries, 2).
|
|
24
|
+
materialize(filesWritten, 2).
|
|
25
|
+
materialize(allChecksPass, 2).
|
|
26
|
+
materialize(exportWeakness, 2).
|
|
27
|
+
materialize(skillsStrain, 2).
|
|
28
|
+
materialize(gridStress, 2).
|
|
29
|
+
materialize(needsRetoolingPulse, 2).
|
|
30
|
+
materialize(derivedFromNeed, 2).
|
|
31
|
+
materialize(signatureVerifies, 2).
|
|
32
|
+
materialize(payloadHashMatches, 2).
|
|
33
|
+
materialize(hmacMatches, 2).
|
|
34
|
+
materialize(minimizationStripsSensitiveTerms, 2).
|
|
35
|
+
materialize(scopeComplete, 2).
|
|
36
|
+
materialize(authorizationAllowed, 2).
|
|
37
|
+
materialize(thresholdReached, 2).
|
|
38
|
+
materialize(packageWithinBudget, 2).
|
|
39
|
+
materialize(packageCoversAllNeeds, 2).
|
|
40
|
+
materialize(dutyTimingConsistent, 2).
|
|
41
|
+
materialize(surveillanceReuseProhibited, 2).
|
|
42
|
+
materialize(filesWrittenExpected, 2).
|
|
43
|
+
materialize(lowestCostEligiblePackageChosen, 2).
|
|
44
|
+
|
|
45
|
+
case_name(case, "flandor").
|
|
46
|
+
question(case, "Is the Flemish Economic Resilience Board allowed to use a neutral macro-economic insight for regional stabilization, and if so which package should it activate for Flanders?").
|
|
47
|
+
expectedFilesWritten(case, 6).
|
|
48
|
+
requestPurpose(case, "regional_stabilization").
|
|
49
|
+
requestAction(case, odrlUse).
|
|
50
|
+
hubCreatedAt(case, "2026-04-08T07:00:00+00:00").
|
|
51
|
+
hubExpiresAt(case, "2026-04-08T19:00:00+00:00").
|
|
52
|
+
boardAuthAt(case, "2026-04-08T09:15:00+00:00").
|
|
53
|
+
boardDutyAt(case, "2026-04-08T18:30:00+00:00").
|
|
54
|
+
files_written(case, 6).
|
|
55
|
+
audit_entries(case, 1).
|
|
56
|
+
|
|
57
|
+
region_name(flanders, "Flanders").
|
|
58
|
+
observedFirms(signals, 217).
|
|
59
|
+
aggregationLevel(signals, "regional_cluster").
|
|
60
|
+
containsFirmNames(signals, false).
|
|
61
|
+
containsPayrollRows(signals, false).
|
|
62
|
+
|
|
63
|
+
industrialCluster(clusterAntwerp).
|
|
64
|
+
clusterName(clusterAntwerp, "Antwerp chemicals").
|
|
65
|
+
exportOrdersIndex(clusterAntwerp, 84).
|
|
66
|
+
energyIntensity(clusterAntwerp, 92).
|
|
67
|
+
industrialCluster(clusterGhent).
|
|
68
|
+
clusterName(clusterGhent, "Ghent manufacturing").
|
|
69
|
+
exportOrdersIndex(clusterGhent, 87).
|
|
70
|
+
energyIntensity(clusterGhent, 76).
|
|
71
|
+
|
|
72
|
+
techVacancyRateTenths(labourMarket, 46).
|
|
73
|
+
techVacancyRate(labourMarket, 4.6).
|
|
74
|
+
congestionHours(grid, 19).
|
|
75
|
+
renewableCurtailmentMWh(grid, 240).
|
|
76
|
+
maxMEUR(budget, 140).
|
|
77
|
+
windowName(budget, "Q2 resilience window").
|
|
78
|
+
|
|
79
|
+
policyPackage(pkgTrainingOnly).
|
|
80
|
+
packageId(pkgTrainingOnly, "pkg:TRAIN_070").
|
|
81
|
+
packageName(pkgTrainingOnly, "Flanders Skills Sprint").
|
|
82
|
+
costMEUR(pkgTrainingOnly, 70).
|
|
83
|
+
worker_coverage(pkgTrainingOnly, 900).
|
|
84
|
+
grid_relief_mw(pkgTrainingOnly, 0).
|
|
85
|
+
coversSkillsStrain(pkgTrainingOnly, true).
|
|
86
|
+
|
|
87
|
+
policyPackage(pkgLogisticsOnly).
|
|
88
|
+
packageId(pkgLogisticsOnly, "pkg:PORT_095").
|
|
89
|
+
packageName(pkgLogisticsOnly, "Schelde Trade Buffer").
|
|
90
|
+
costMEUR(pkgLogisticsOnly, 95).
|
|
91
|
+
worker_coverage(pkgLogisticsOnly, 300).
|
|
92
|
+
grid_relief_mw(pkgLogisticsOnly, 10).
|
|
93
|
+
coversExportWeakness(pkgLogisticsOnly, true).
|
|
94
|
+
|
|
95
|
+
policyPackage(pkgFlandor).
|
|
96
|
+
packageId(pkgFlandor, "pkg:RET_FLEX_120").
|
|
97
|
+
packageName(pkgFlandor, "Flandor Retooling Pulse").
|
|
98
|
+
costMEUR(pkgFlandor, 120).
|
|
99
|
+
worker_coverage(pkgFlandor, 1200).
|
|
100
|
+
grid_relief_mw(pkgFlandor, 85).
|
|
101
|
+
coversExportWeakness(pkgFlandor, true).
|
|
102
|
+
coversSkillsStrain(pkgFlandor, true).
|
|
103
|
+
coversGridStress(pkgFlandor, true).
|
|
104
|
+
|
|
105
|
+
policyPackage(pkgFullCorridor).
|
|
106
|
+
packageId(pkgFullCorridor, "pkg:CORRIDOR_165").
|
|
107
|
+
packageName(pkgFullCorridor, "Full Corridor Shock Shield").
|
|
108
|
+
costMEUR(pkgFullCorridor, 165).
|
|
109
|
+
worker_coverage(pkgFullCorridor, 1600).
|
|
110
|
+
grid_relief_mw(pkgFullCorridor, 110).
|
|
111
|
+
coversExportWeakness(pkgFullCorridor, true).
|
|
112
|
+
coversSkillsStrain(pkgFullCorridor, true).
|
|
113
|
+
coversGridStress(pkgFullCorridor, true).
|
|
114
|
+
|
|
115
|
+
insight(macroInsight).
|
|
116
|
+
id(macroInsight, "https://example.org/insight/flandor").
|
|
117
|
+
insight_metric(macroInsight, "regional_retooling_priority").
|
|
118
|
+
thresholdScore(macroInsight, 3).
|
|
119
|
+
thresholdDisplay(macroInsight, "3 active needs").
|
|
120
|
+
suggestionPolicy(macroInsight, "lowest_cost_package_covering_all_active_needs").
|
|
121
|
+
scopeDevice(macroInsight, "economic-resilience-board").
|
|
122
|
+
scopeEvent(macroInsight, "budget-prep-window").
|
|
123
|
+
region(macroInsight, "Flanders").
|
|
124
|
+
createdAt(macroInsight, "2026-04-08T07:00:00+00:00").
|
|
125
|
+
expiresAt(macroInsight, "2026-04-08T19:00:00+00:00").
|
|
126
|
+
serializedLowercase(macroInsight, "createdat expiresat insight metric regional_retooling_priority region flanders scopedevice economic-resilience-board scopeevent budget-prep-window suggestionpolicy lowest_cost_package_covering_all_active_needs threshold 3").
|
|
127
|
+
|
|
128
|
+
envelopeInsight(envelope, macroInsight).
|
|
129
|
+
envelopePolicy(envelope, policy).
|
|
130
|
+
envelopeHash(envelope, "10a85e861075bef2a96c01c7f3238735f82b8f368deb62eafcedd1c4b7f7c707").
|
|
131
|
+
permission(policy, odrlUse, macroInsight, "regional_stabilization").
|
|
132
|
+
prohibition(policy, odrlDistribute, macroInsight, "firm_surveillance").
|
|
133
|
+
duty(policy, odrlDelete, "2026-04-08T19:00:00+00:00").
|
|
134
|
+
|
|
135
|
+
signature_alg(signature, "HMAC-SHA256").
|
|
136
|
+
keyid(signature, "demo-shared-secret").
|
|
137
|
+
created(signature, "2026-04-08T07:00:00+00:00").
|
|
138
|
+
payload_hash_sha256(signature, "10a85e861075bef2a96c01c7f3238735f82b8f368deb62eafcedd1c4b7f7c707").
|
|
139
|
+
display_payload_hash_sha256(signature, "718f5b17d07ab6a95503bc04a1000ddb132409f600659c03d21def81914b780b").
|
|
140
|
+
signature_hmac(signature, "955968ca99a191783bc00cba068128ccb9ff40a5e6114fda13a52c74ee27329e").
|
|
141
|
+
hmacVerificationMode(signature, trustedPrecomputedInput).
|
|
142
|
+
|
|
143
|
+
reason_value(reasonText, "Secure aggregates from Flanders indicate simultaneous export weakness, technical labour scarcity, and grid congestion. A neutral macro insight is scoped to the economic-resilience board for one budget window only, enabling a temporary package without exposing firm-level books.").
|
|
144
|
+
|
|
145
|
+
caseName(Case, Name) :- case_name(Case, Name).
|
|
146
|
+
regionName(Region, Name) :- region_name(Region, Name).
|
|
147
|
+
metric(Insight, Metric) :- insight_metric(Insight, Metric).
|
|
148
|
+
alg(Signature, Alg) :- signature_alg(Signature, Alg).
|
|
149
|
+
payloadHashSHA256(Signature, Hash) :- display_payload_hash_sha256(Signature, Hash).
|
|
150
|
+
signatureHMAC(Signature, Hmac) :- signature_hmac(Signature, Hmac).
|
|
151
|
+
auditEntries(Case, Count) :- audit_entries(Case, Count).
|
|
152
|
+
filesWritten(Case, Count) :- files_written(Case, Count).
|
|
153
|
+
|
|
154
|
+
export_weakness(case) :-
|
|
155
|
+
industrialCluster(Cluster),
|
|
156
|
+
exportOrdersIndex(Cluster, Index),
|
|
157
|
+
lt(Index, 90).
|
|
158
|
+
|
|
159
|
+
skills_strain(case) :-
|
|
160
|
+
techVacancyRateTenths(labourMarket, Rate),
|
|
161
|
+
gt(Rate, 39).
|
|
162
|
+
|
|
163
|
+
grid_stress(case) :-
|
|
164
|
+
congestionHours(grid, Hours),
|
|
165
|
+
gt(Hours, 11).
|
|
166
|
+
|
|
167
|
+
needs_retooling_pulse(case) :-
|
|
168
|
+
export_weakness(case),
|
|
169
|
+
skills_strain(case),
|
|
170
|
+
grid_stress(case).
|
|
171
|
+
|
|
172
|
+
payload_hash_matches(check) :-
|
|
173
|
+
envelopeHash(envelope, Digest),
|
|
174
|
+
payload_hash_sha256(signature, Digest).
|
|
175
|
+
|
|
176
|
+
signature_verifies(check) :- hmacVerificationMode(signature, trustedPrecomputedInput).
|
|
177
|
+
hmac_matches(check) :-
|
|
178
|
+
hmacVerificationMode(signature, trustedPrecomputedInput),
|
|
179
|
+
signature_hmac(signature, "955968ca99a191783bc00cba068128ccb9ff40a5e6114fda13a52c74ee27329e").
|
|
180
|
+
|
|
181
|
+
minimization_strips_sensitive_terms(check) :-
|
|
182
|
+
serializedLowercase(macroInsight, Text),
|
|
183
|
+
not_matches(Text, "salary|payroll|invoice|medical|firmname").
|
|
184
|
+
|
|
185
|
+
scope_complete(check) :-
|
|
186
|
+
scopeDevice(macroInsight, _Device),
|
|
187
|
+
scopeEvent(macroInsight, _Event),
|
|
188
|
+
expiresAt(macroInsight, _Expiry).
|
|
189
|
+
|
|
190
|
+
authorization_allowed(check) :-
|
|
191
|
+
permission(policy, odrlUse, macroInsight, "regional_stabilization"),
|
|
192
|
+
boardAuthAt(case, AuthAt),
|
|
193
|
+
expiresAt(macroInsight, ExpiresAt),
|
|
194
|
+
le(AuthAt, ExpiresAt).
|
|
195
|
+
|
|
196
|
+
decision(decision, "Allowed", macroInsight) :- authorization_allowed(check).
|
|
197
|
+
|
|
198
|
+
eligible_package(Pkg) :-
|
|
199
|
+
needs_retooling_pulse(case),
|
|
200
|
+
maxMEUR(budget, Max),
|
|
201
|
+
policyPackage(Pkg),
|
|
202
|
+
costMEUR(Pkg, Cost),
|
|
203
|
+
coversExportWeakness(Pkg, true),
|
|
204
|
+
coversSkillsStrain(Pkg, true),
|
|
205
|
+
coversGridStress(Pkg, true),
|
|
206
|
+
le(Cost, Max).
|
|
207
|
+
|
|
208
|
+
lower_cost_eligible_package(Cost) :-
|
|
209
|
+
eligible_package(Other),
|
|
210
|
+
costMEUR(Other, OtherCost),
|
|
211
|
+
lt(OtherCost, Cost).
|
|
212
|
+
|
|
213
|
+
recommended_package(case, Pkg) :-
|
|
214
|
+
eligible_package(Pkg),
|
|
215
|
+
costMEUR(Pkg, Cost),
|
|
216
|
+
not(lower_cost_eligible_package(Cost)).
|
|
217
|
+
|
|
218
|
+
package_within_budget(check) :-
|
|
219
|
+
recommended_package(case, Pkg),
|
|
220
|
+
costMEUR(Pkg, Cost),
|
|
221
|
+
maxMEUR(budget, Max),
|
|
222
|
+
le(Cost, Max).
|
|
223
|
+
|
|
224
|
+
package_covers_all_needs(check) :-
|
|
225
|
+
recommended_package(case, Pkg),
|
|
226
|
+
coversExportWeakness(Pkg, true),
|
|
227
|
+
coversSkillsStrain(Pkg, true),
|
|
228
|
+
coversGridStress(Pkg, true).
|
|
229
|
+
|
|
230
|
+
duty_timing_consistent(check) :-
|
|
231
|
+
boardDutyAt(case, DutyAt),
|
|
232
|
+
expiresAt(macroInsight, ExpiresAt),
|
|
233
|
+
le(DutyAt, ExpiresAt).
|
|
234
|
+
|
|
235
|
+
surveillance_reuse_prohibited(check) :- prohibition(policy, odrlDistribute, macroInsight, "firm_surveillance").
|
|
236
|
+
files_written_expected(check) :- files_written(case, 6).
|
|
237
|
+
threshold_reached(check) :- export_weakness(case), skills_strain(case), grid_stress(case).
|
|
238
|
+
lowest_cost_eligible_package_chosen(check) :- recommended_package(case, _Pkg).
|
|
239
|
+
|
|
240
|
+
all_checks_pass(result) :-
|
|
241
|
+
signature_verifies(check),
|
|
242
|
+
payload_hash_matches(check),
|
|
243
|
+
minimization_strips_sensitive_terms(check),
|
|
244
|
+
scope_complete(check),
|
|
245
|
+
authorization_allowed(check),
|
|
246
|
+
package_within_budget(check),
|
|
247
|
+
package_covers_all_needs(check),
|
|
248
|
+
duty_timing_consistent(check),
|
|
249
|
+
surveillance_reuse_prohibited(check),
|
|
250
|
+
files_written_expected(check).
|
|
251
|
+
|
|
252
|
+
exportWeakness(case, true) :- export_weakness(case).
|
|
253
|
+
skillsStrain(case, true) :- skills_strain(case).
|
|
254
|
+
gridStress(case, true) :- grid_stress(case).
|
|
255
|
+
needsRetoolingPulse(case, true) :- needs_retooling_pulse(case).
|
|
256
|
+
derivedFromNeed(case, "regional_retooling_and_flexibility") :- needs_retooling_pulse(case).
|
|
257
|
+
activeNeedCount(answer, 3) :- threshold_reached(check).
|
|
258
|
+
activeNeedThreshold(answer, 3) :- thresholdScore(macroInsight, 3).
|
|
259
|
+
recommendedPackageName(answer, Name) :- recommended_package(case, Pkg), packageName(Pkg, Name).
|
|
260
|
+
budgetCapMEUR(answer, Max) :- maxMEUR(budget, Max).
|
|
261
|
+
packageCostMEUR(answer, Cost) :- recommended_package(case, Pkg), costMEUR(Pkg, Cost).
|
|
262
|
+
envelopeExpiresAt(answer, Time) :- expiresAt(macroInsight, Time).
|
|
263
|
+
workerCoverage(why, Workers) :- recommended_package(case, Pkg), worker_coverage(Pkg, Workers).
|
|
264
|
+
gridReliefMW(why, MW) :- recommended_package(case, Pkg), grid_relief_mw(Pkg, MW).
|
|
265
|
+
outcome(decision, Outcome) :- decision(decision, Outcome, _Target).
|
|
266
|
+
target(decision, Target) :- decision(decision, _Outcome, Target).
|
|
267
|
+
allChecksPass(result, true) :- all_checks_pass(result).
|
|
268
|
+
signatureVerifies(check, true) :- signature_verifies(check).
|
|
269
|
+
payloadHashMatches(check, true) :- payload_hash_matches(check).
|
|
270
|
+
hmacMatches(check, true) :- hmac_matches(check).
|
|
271
|
+
minimizationStripsSensitiveTerms(check, true) :- minimization_strips_sensitive_terms(check).
|
|
272
|
+
scopeComplete(check, true) :- scope_complete(check).
|
|
273
|
+
authorizationAllowed(check, true) :- authorization_allowed(check).
|
|
274
|
+
thresholdReached(check, true) :- threshold_reached(check).
|
|
275
|
+
packageWithinBudget(check, true) :- package_within_budget(check).
|
|
276
|
+
packageCoversAllNeeds(check, true) :- package_covers_all_needs(check).
|
|
277
|
+
dutyTimingConsistent(check, true) :- duty_timing_consistent(check).
|
|
278
|
+
surveillanceReuseProhibited(check, true) :- surveillance_reuse_prohibited(check).
|
|
279
|
+
filesWrittenExpected(check, true) :- files_written_expected(check).
|
|
280
|
+
lowestCostEligiblePackageChosen(check, true) :- lowest_cost_eligible_package_chosen(check).
|
|
281
|
+
|
|
282
|
+
reason(whyExportWeakness, "Export weakness is active because at least one cluster has exportOrdersIndex < 90 (Antwerp chemicals=84, Ghent manufacturing=87).") :- export_weakness(case).
|
|
283
|
+
reason(whySkillsStrain, "Skills strain is active because technical vacancy rate is 4.6% (threshold > 3.9%).") :- skills_strain(case).
|
|
284
|
+
reason(whyGridStress, "Grid stress is active because congestion hours = 19 (threshold > 11).") :- grid_stress(case).
|
|
285
|
+
reason(whyRecommendationPolicy, "The recommendation policy is \"lowest_cost_package_covering_all_active_needs\", so the cheapest package that covers all active needs within budget is selected.") :- recommended_package(case, _Pkg).
|
|
286
|
+
reason(whySelectedPackage, "Selected package \"Flandor Retooling Pulse\" covers export=true, skills=true, grid=true, cost=€120M.") :- recommended_package(case, pkgFlandor).
|
|
287
|
+
reason(whyUsage, "Usage is permitted only for purpose \"regional_stabilization\" and the envelope expires at 2026-04-08T19:00:00+00:00.") :- authorization_allowed(check).
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
label(scenarioA, "A – Primary care visit").
|
|
2
|
+
label(scenarioB, "B – Quality improvement (in scope)").
|
|
3
|
+
label(scenarioC, "C – Quality improvement (out of scope)").
|
|
4
|
+
label(scenarioD, "D – Insurance management").
|
|
5
|
+
label(scenarioE, "E – GP checks labs").
|
|
6
|
+
label(scenarioF, "F – Research on anonymised dataset").
|
|
7
|
+
label(scenarioG, "G – AI training (opt-out)").
|
|
8
|
+
description(scenarioA, "Clinician in the patient's care team accessing the patient summary for primary care management.").
|
|
9
|
+
description(scenarioB, "QI analyst using lab results + summary in a secure environment.").
|
|
10
|
+
description(scenarioC, "QI analyst with only lab results; policy expects labs + summary.").
|
|
11
|
+
description(scenarioD, "Insurance bot attempting to use health data for insurance management (prohibited purpose).").
|
|
12
|
+
description(scenarioE, "GP for the same patient checking lab results via the API gateway.").
|
|
13
|
+
description(scenarioF, "Researcher using anonymised labs + summary in a secure environment, with opt-in.").
|
|
14
|
+
description(scenarioG, "Data user wants to train AI, but the subject opted out of AI training.").
|
|
15
|
+
careTeamLinked(scenarioA, true).
|
|
16
|
+
careTeamLinked(scenarioE, true).
|
|
17
|
+
subjectOptIn(scenarioF, true).
|
|
18
|
+
subjectOptOut(scenarioG, true).
|
|
19
|
+
decision(scenarioA, "PERMIT").
|
|
20
|
+
decision(scenarioE, "PERMIT").
|
|
21
|
+
decision(scenarioB, "PERMIT").
|
|
22
|
+
decision(scenarioF, "PERMIT").
|
|
23
|
+
decision(scenarioD, "DENY").
|
|
24
|
+
decision(scenarioG, "DENY").
|
|
25
|
+
decision(scenarioC, "DENY").
|
|
26
|
+
matchedPolicyUid(scenarioA, "urn:policy:primary-care-001").
|
|
27
|
+
matchedPolicyUid(scenarioE, "urn:policy:primary-care-001").
|
|
28
|
+
matchedPolicyUid(scenarioB, "urn:policy:qi-2025-aurora").
|
|
29
|
+
matchedPolicyUid(scenarioF, "urn:policy:research-aurora-diabetes").
|
|
30
|
+
matchedProhibition(scenarioD, policyDenyInsurance).
|
|
31
|
+
reason(scenarioA, "Permitted: clinician in the patient's care team, and the primary-care policy matched.").
|
|
32
|
+
reason(scenarioE, "Permitted: clinician in the patient's care team, and the primary-care policy matched.").
|
|
33
|
+
reason(scenarioB, "Permitted: ODRL/DPV policy matched for secondary use.").
|
|
34
|
+
reason(scenarioF, "Permitted: subject opted in and an ODRL/DPV policy matched (anonymised dataset in secure environment).").
|
|
35
|
+
reason(scenarioD, "Denied: the requested purpose (insurance management) is prohibited by policy.").
|
|
36
|
+
reason(scenarioG, "Denied: you opted out of your data being used to train AI systems.").
|
|
37
|
+
reason(scenarioC, "Denied: no policy matched (purpose, environment, TOMs, or categories out of scope).").
|
|
38
|
+
trace(scenarioA, "permit:primary_care_allowed").
|
|
39
|
+
trace(scenarioE, "permit:primary_care_allowed").
|
|
40
|
+
trace(scenarioA, "urn:policy:primary-care-001:permit:odrl:permission_matched").
|
|
41
|
+
trace(scenarioE, "urn:policy:primary-care-001:permit:odrl:permission_matched").
|
|
42
|
+
trace(scenarioB, "urn:policy:qi-2025-aurora:permit:odrl:permission_matched").
|
|
43
|
+
trace(scenarioF, "urn:policy:research-aurora-diabetes:permit:odrl:permission_matched").
|
|
44
|
+
trace(scenarioD, "deny:prohibited_purpose").
|
|
45
|
+
trace(scenarioD, "urn:policy:deny-insurance:deny:odrl:prohibition_matched").
|
|
46
|
+
trace(scenarioG, "deny:subject_opted_out_ai_training").
|
|
47
|
+
trace(scenarioC, "urn:policy:qi-2025-aurora:deny:odrl:no_permission_matched").
|
|
48
|
+
checkC1(scenarioA, "SKIPPED - not a prohibited purpose").
|
|
49
|
+
checkC1(scenarioB, "SKIPPED - not a prohibited purpose").
|
|
50
|
+
checkC1(scenarioC, "SKIPPED - not a prohibited purpose").
|
|
51
|
+
checkC1(scenarioD, "OK - denied prohibited purpose").
|
|
52
|
+
checkC1(scenarioE, "SKIPPED - not a prohibited purpose").
|
|
53
|
+
checkC1(scenarioF, "SKIPPED - not a prohibited purpose").
|
|
54
|
+
checkC1(scenarioG, "SKIPPED - not a prohibited purpose").
|
|
55
|
+
checkC2(scenarioA, "OK - clinician").
|
|
56
|
+
checkC2(scenarioB, "SKIPPED").
|
|
57
|
+
checkC2(scenarioC, "SKIPPED").
|
|
58
|
+
checkC2(scenarioD, "SKIPPED").
|
|
59
|
+
checkC2(scenarioE, "OK - clinician").
|
|
60
|
+
checkC2(scenarioF, "SKIPPED").
|
|
61
|
+
checkC2(scenarioG, "SKIPPED").
|
|
62
|
+
checkC3(scenarioA, "OK - care-team linked").
|
|
63
|
+
checkC3(scenarioB, "SKIPPED").
|
|
64
|
+
checkC3(scenarioC, "SKIPPED").
|
|
65
|
+
checkC3(scenarioD, "SKIPPED").
|
|
66
|
+
checkC3(scenarioE, "OK - care-team linked").
|
|
67
|
+
checkC3(scenarioF, "SKIPPED").
|
|
68
|
+
checkC3(scenarioG, "SKIPPED").
|
|
69
|
+
checkC4(scenarioA, "SKIPPED").
|
|
70
|
+
checkC4(scenarioB, "OK - opt-in present and policy matched").
|
|
71
|
+
checkC4(scenarioC, "OK - denied because opt-in missing or no policy match").
|
|
72
|
+
checkC4(scenarioD, "SKIPPED").
|
|
73
|
+
checkC4(scenarioE, "SKIPPED").
|
|
74
|
+
checkC4(scenarioF, "OK - opt-in present and policy matched").
|
|
75
|
+
checkC4(scenarioG, "OK - denied because opt-in missing or no policy match").
|
|
76
|
+
checkC5(scenarioA, "OK - operator=isAnyOf, allowed=[\"https://example.org/health#PATIENT_SUMMARY\", \"https://example.org/health#LAB_RESULTS\"], requested=[\"https://example.org/health#PATIENT_SUMMARY\"]").
|
|
77
|
+
checkC5(scenarioB, "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\"]").
|
|
78
|
+
checkC5(scenarioC, "SKIPPED").
|
|
79
|
+
checkC5(scenarioD, "SKIPPED").
|
|
80
|
+
checkC5(scenarioE, "OK - operator=isAnyOf, allowed=[\"https://example.org/health#PATIENT_SUMMARY\", \"https://example.org/health#LAB_RESULTS\"], requested=[\"https://example.org/health#LAB_RESULTS\"]").
|
|
81
|
+
checkC5(scenarioF, "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\"]").
|
|
82
|
+
checkC5(scenarioG, "SKIPPED").
|
|
83
|
+
checkC6(scenarioA, "SKIPPED - no prohibition matched").
|
|
84
|
+
checkC6(scenarioB, "SKIPPED - no prohibition matched").
|
|
85
|
+
checkC6(scenarioC, "SKIPPED - no prohibition matched").
|
|
86
|
+
checkC6(scenarioD, "OK - denied due to prohibition").
|
|
87
|
+
checkC6(scenarioE, "SKIPPED - no prohibition matched").
|
|
88
|
+
checkC6(scenarioF, "SKIPPED - no prohibition matched").
|
|
89
|
+
checkC6(scenarioG, "SKIPPED - no prohibition matched").
|
|
90
|
+
checkC7(scenarioA, "OK - trace shows matching permission").
|
|
91
|
+
checkC7(scenarioB, "OK - trace shows matching permission").
|
|
92
|
+
checkC7(scenarioC, "SKIPPED").
|
|
93
|
+
checkC7(scenarioD, "SKIPPED").
|
|
94
|
+
checkC7(scenarioE, "OK - trace shows matching permission").
|
|
95
|
+
checkC7(scenarioF, "OK - trace shows matching permission").
|
|
96
|
+
checkC7(scenarioG, "SKIPPED").
|
|
97
|
+
checkC8(scenarioA, "SKIPPED - no matched policy or no duties").
|
|
98
|
+
checkC8(scenarioB, "INFO - duties attached: duty:https://w3id.org/dpv/legal/eu/ehds#requireConsent, duty:https://w3id.org/dpv/legal/eu/ehds#noExfiltration").
|
|
99
|
+
checkC8(scenarioC, "SKIPPED - no matched policy or no duties").
|
|
100
|
+
checkC8(scenarioD, "SKIPPED - no matched policy or no duties").
|
|
101
|
+
checkC8(scenarioE, "SKIPPED - no matched policy or no duties").
|
|
102
|
+
checkC8(scenarioF, "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").
|
|
103
|
+
checkC8(scenarioG, "SKIPPED - no matched policy or no duties").
|
|
104
|
+
checkC9(scenarioA, "SKIPPED - policy has no environment constraint").
|
|
105
|
+
checkC9(scenarioB, "OK - operator=eq, allowed=\"secure_env\", requested=\"secure_env\"").
|
|
106
|
+
checkC9(scenarioC, "SKIPPED").
|
|
107
|
+
checkC9(scenarioD, "SKIPPED").
|
|
108
|
+
checkC9(scenarioE, "SKIPPED - policy has no environment constraint").
|
|
109
|
+
checkC9(scenarioF, "OK - operator=eq, allowed=\"secure_env\", requested=\"secure_env\"").
|
|
110
|
+
checkC9(scenarioG, "SKIPPED").
|
|
111
|
+
checkC10Text(scenarioA, "INFO - matched policy: urn:policy:primary-care-001").
|
|
112
|
+
checkC10Text(scenarioB, "INFO - matched policy: urn:policy:qi-2025-aurora").
|
|
113
|
+
checkC10Text(scenarioC, "SKIPPED - no matched policy").
|
|
114
|
+
checkC10Text(scenarioD, "SKIPPED - no matched policy").
|
|
115
|
+
checkC10Text(scenarioE, "INFO - matched policy: urn:policy:primary-care-001").
|
|
116
|
+
checkC10Text(scenarioF, "INFO - matched policy: urn:policy:research-aurora-diabetes").
|
|
117
|
+
checkC10Text(scenarioG, "SKIPPED - no matched policy").
|
|
@@ -1,11 +1,17 @@
|
|
|
1
|
+
metric(insight, "sugar_g_per_serving").
|
|
2
|
+
retailer(insight, "Delfour").
|
|
1
3
|
caseName(case, "delfour").
|
|
2
4
|
needsLowSugar(case, true).
|
|
3
5
|
derivedFromNeed(insight, "low_sugar").
|
|
4
6
|
outcome(decision, "Allowed").
|
|
5
7
|
target(decision, insight).
|
|
6
8
|
scannedProduct(scan, "Classic Tea Biscuits").
|
|
7
|
-
suggestedAlternative(case,
|
|
9
|
+
suggestedAlternative(case, "Low-Sugar Tea Biscuits").
|
|
8
10
|
suggestedAlternative(banner, "Low-Sugar Tea Biscuits").
|
|
11
|
+
threshold(insight, "10.0").
|
|
12
|
+
scope(insight, "self-scanner @ pick_up_scanner").
|
|
13
|
+
expiresAt(insight, "2025-10-05T22:33:48.907185+00:00").
|
|
14
|
+
reason(why, "The phone desensitizes a diabetes-related household condition into a scoped low-sugar need, wraps it in an expiring Insight + Policy envelope, signs it, and the scanner consumes that envelope for shopping assistance.").
|
|
9
15
|
headline(banner, "Track sugar per serving while you scan").
|
|
10
16
|
note(banner, "High sugar").
|
|
11
17
|
value(reasonText, "Household requires low-sugar guidance (diabetes in POD). A neutral Insight is scoped to device 'self-scanner', event 'pick_up_scanner', retailer 'Delfour', and expires soon; the policy confines use to shopping assistance.").
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
caseName(case, "flandor").
|
|
2
|
+
regionName(flanders, "Flanders").
|
|
3
|
+
metric(macroInsight, "regional_retooling_priority").
|
|
4
|
+
alg(signature, "HMAC-SHA256").
|
|
5
|
+
payloadHashSHA256(signature, "718f5b17d07ab6a95503bc04a1000ddb132409f600659c03d21def81914b780b").
|
|
6
|
+
signatureHMAC(signature, "955968ca99a191783bc00cba068128ccb9ff40a5e6114fda13a52c74ee27329e").
|
|
7
|
+
auditEntries(case, 1).
|
|
8
|
+
filesWritten(case, 6).
|
|
9
|
+
exportWeakness(case, true).
|
|
10
|
+
skillsStrain(case, true).
|
|
11
|
+
gridStress(case, true).
|
|
12
|
+
needsRetoolingPulse(case, true).
|
|
13
|
+
derivedFromNeed(case, "regional_retooling_and_flexibility").
|
|
14
|
+
activeNeedCount(answer, 3).
|
|
15
|
+
activeNeedThreshold(answer, 3).
|
|
16
|
+
recommendedPackageName(answer, "Flandor Retooling Pulse").
|
|
17
|
+
budgetCapMEUR(answer, 140).
|
|
18
|
+
packageCostMEUR(answer, 120).
|
|
19
|
+
envelopeExpiresAt(answer, "2026-04-08T19:00:00+00:00").
|
|
20
|
+
workerCoverage(why, 1200).
|
|
21
|
+
gridReliefMW(why, 85).
|
|
22
|
+
outcome(decision, "Allowed").
|
|
23
|
+
target(decision, macroInsight).
|
|
24
|
+
allChecksPass(result, true).
|
|
25
|
+
signatureVerifies(check, true).
|
|
26
|
+
payloadHashMatches(check, true).
|
|
27
|
+
hmacMatches(check, true).
|
|
28
|
+
minimizationStripsSensitiveTerms(check, true).
|
|
29
|
+
scopeComplete(check, true).
|
|
30
|
+
authorizationAllowed(check, true).
|
|
31
|
+
thresholdReached(check, true).
|
|
32
|
+
packageWithinBudget(check, true).
|
|
33
|
+
packageCoversAllNeeds(check, true).
|
|
34
|
+
dutyTimingConsistent(check, true).
|
|
35
|
+
surveillanceReuseProhibited(check, true).
|
|
36
|
+
filesWrittenExpected(check, true).
|
|
37
|
+
lowestCostEligiblePackageChosen(check, true).
|
|
38
|
+
reason(whyExportWeakness, "Export weakness is active because at least one cluster has exportOrdersIndex < 90 (Antwerp chemicals=84, Ghent manufacturing=87).").
|
|
39
|
+
reason(whySkillsStrain, "Skills strain is active because technical vacancy rate is 4.6% (threshold > 3.9%).").
|
|
40
|
+
reason(whyGridStress, "Grid stress is active because congestion hours = 19 (threshold > 11).").
|
|
41
|
+
reason(whyRecommendationPolicy, "The recommendation policy is \"lowest_cost_package_covering_all_active_needs\", so the cheapest package that covers all active needs within budget is selected.").
|
|
42
|
+
reason(whySelectedPackage, "Selected package \"Flandor Retooling Pulse\" covers export=true, skills=true, grid=true, cost=€120M.").
|
|
43
|
+
reason(whyUsage, "Usage is permitted only for purpose \"regional_stabilization\" and the envelope expires at 2026-04-08T19:00:00+00:00.").
|
package/package.json
CHANGED
package/playground.html
CHANGED
|
@@ -432,6 +432,7 @@
|
|
|
432
432
|
"ancestor",
|
|
433
433
|
"animal",
|
|
434
434
|
"annotation",
|
|
435
|
+
"auroracare",
|
|
435
436
|
"backward",
|
|
436
437
|
"basic-monadic",
|
|
437
438
|
"bayes-diagnosis",
|
|
@@ -491,6 +492,7 @@
|
|
|
491
492
|
"fibonacci",
|
|
492
493
|
"field-nitrogen-balance",
|
|
493
494
|
"floating-point",
|
|
495
|
+
"flandor",
|
|
494
496
|
"four-color-map",
|
|
495
497
|
"fundamental-theorem-arithmetic",
|
|
496
498
|
"gcd-bezout-identity",
|