eyeling 1.22.1 → 1.22.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/examples/arcling/README.md +37 -167
- package/examples/calidor.n3 +500 -0
- package/examples/output/calidor.n3 +29 -0
- package/package.json +2 -3
- package/examples/arcling/delfour/delfour.data.json +0 -67
- package/examples/arcling/delfour/delfour.expected.json +0 -88
- package/examples/arcling/delfour/delfour.model.go +0 -564
- package/examples/arcling/delfour/delfour.spec.md +0 -117
- package/examples/arcling/flandor/flandor.data.json +0 -106
- package/examples/arcling/flandor/flandor.expected.json +0 -98
- package/examples/arcling/flandor/flandor.model.go +0 -655
- package/examples/arcling/flandor/flandor.spec.md +0 -155
- package/examples/arcling/medior/medior.data.json +0 -96
- package/examples/arcling/medior/medior.expected.json +0 -100
- package/examples/arcling/medior/medior.model.go +0 -652
- package/examples/arcling/medior/medior.spec.md +0 -157
- package/test/arcling.test.js +0 -191
|
@@ -0,0 +1,500 @@
|
|
|
1
|
+
# ==============================================================================
|
|
2
|
+
# Calidor — Ruben Verborgh's "Inside the Insight Economy" case.
|
|
3
|
+
# See https://ruben.verborgh.org/blog/2025/08/12/inside-the-insight-economy/
|
|
4
|
+
#
|
|
5
|
+
# This example shows how a city can deliver urgent heatwave support without
|
|
6
|
+
# collecting raw household heat, vulnerability, or prepaid-energy data. A local
|
|
7
|
+
# gateway turns those private signals into a narrow, expiring insight such as
|
|
8
|
+
# "priority cooling support needed", attaches clear usage rules and an expiry
|
|
9
|
+
# time, and sends that envelope to the municipal heat-response system. The city
|
|
10
|
+
# may use the insight for heatwave response, but not for unrelated purposes such
|
|
11
|
+
# as tenant screening.
|
|
12
|
+
# ==============================================================================
|
|
13
|
+
|
|
14
|
+
@prefix : <https://example.org/calidor#> .
|
|
15
|
+
@prefix arc: <https://josd.github.io/arc/terms#> .
|
|
16
|
+
@prefix ins: <https://example.org/insight#> .
|
|
17
|
+
@prefix odrl: <http://www.w3.org/ns/odrl/2/> .
|
|
18
|
+
@prefix log: <http://www.w3.org/2000/10/swap/log#> .
|
|
19
|
+
@prefix math: <http://www.w3.org/2000/10/swap/math#> .
|
|
20
|
+
@prefix string: <http://www.w3.org/2000/10/swap/string#> .
|
|
21
|
+
@prefix crypto: <http://www.w3.org/2000/10/swap/crypto#> .
|
|
22
|
+
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
|
|
23
|
+
|
|
24
|
+
# -----
|
|
25
|
+
# Facts
|
|
26
|
+
# -----
|
|
27
|
+
|
|
28
|
+
:case
|
|
29
|
+
a arc:Case ;
|
|
30
|
+
:caseName "calidor" ;
|
|
31
|
+
arc:question "Is the Calidor heat-response system allowed to use a narrow household support insight for heatwave response, and if so which support package should it recommend?" ;
|
|
32
|
+
:requestPurpose "heatwave_response" ;
|
|
33
|
+
:requestAction odrl:use ;
|
|
34
|
+
:gatewayCreatedAt "2026-07-18T09:00:00+00:00"^^xsd:dateTime ;
|
|
35
|
+
:gatewayExpiresAt "2026-07-18T21:00:00+00:00"^^xsd:dateTime ;
|
|
36
|
+
:cityAuthAt "2026-07-18T09:05:00+00:00"^^xsd:dateTime ;
|
|
37
|
+
:cityDutyAt "2026-07-18T20:30:00+00:00"^^xsd:dateTime ;
|
|
38
|
+
:currentAlertLevel 4 ;
|
|
39
|
+
:alertLevelAtLeast 3 ;
|
|
40
|
+
:currentIndoorTempC 31.4 ;
|
|
41
|
+
:indoorTempCAtLeast 30.0 ;
|
|
42
|
+
:hoursAtOrAboveThreshold 9 ;
|
|
43
|
+
:hoursAtOrAboveThresholdAtLeast 6 ;
|
|
44
|
+
:remainingPrepaidCreditEur 3.2 ;
|
|
45
|
+
:energyCreditEurAtMost 5.0 ;
|
|
46
|
+
:minimumActiveNeedCount 3 ;
|
|
47
|
+
:maxPackageCostEur 20 ;
|
|
48
|
+
:dispatchesLogged 1 .
|
|
49
|
+
|
|
50
|
+
:localProfile
|
|
51
|
+
:vulnerabilityFlag "heat_sensitive_condition" ;
|
|
52
|
+
:vulnerabilityFlag "mobility_limitation" .
|
|
53
|
+
|
|
54
|
+
:pkg_CHECK
|
|
55
|
+
a :SupportPackage ;
|
|
56
|
+
:packageId "pkg:CHECK" ;
|
|
57
|
+
:packageName "Cooling Check Call" ;
|
|
58
|
+
:costEur 8 ;
|
|
59
|
+
:capability :welfare_check .
|
|
60
|
+
|
|
61
|
+
:pkg_VOUCHER
|
|
62
|
+
a :SupportPackage ;
|
|
63
|
+
:packageId "pkg:VOUCHER" ;
|
|
64
|
+
:packageName "Cooling Center Transport Voucher" ;
|
|
65
|
+
:costEur 12 ;
|
|
66
|
+
:capability :transport ;
|
|
67
|
+
:capability :welfare_check .
|
|
68
|
+
|
|
69
|
+
:pkg_BUNDLE
|
|
70
|
+
a :SupportPackage ;
|
|
71
|
+
:packageId "pkg:BUNDLE" ;
|
|
72
|
+
:packageName "Calidor Priority Cooling Bundle" ;
|
|
73
|
+
:costEur 18 ;
|
|
74
|
+
:capability :cooling_kit ;
|
|
75
|
+
:capability :welfare_check ;
|
|
76
|
+
:capability :transport ;
|
|
77
|
+
:capability :bill_credit .
|
|
78
|
+
|
|
79
|
+
:pkg_DELUXE
|
|
80
|
+
a :SupportPackage ;
|
|
81
|
+
:packageId "pkg:DELUXE" ;
|
|
82
|
+
:packageName "Extended Resilience Package" ;
|
|
83
|
+
:costEur 28 ;
|
|
84
|
+
:capability :cooling_kit ;
|
|
85
|
+
:capability :welfare_check ;
|
|
86
|
+
:capability :transport ;
|
|
87
|
+
:capability :bill_credit ;
|
|
88
|
+
:capability :followup_visit .
|
|
89
|
+
|
|
90
|
+
<https://example.org/insight/calidor>
|
|
91
|
+
a ins:Insight ;
|
|
92
|
+
:metric "active_need_count" ;
|
|
93
|
+
:thresholdCount 3 ;
|
|
94
|
+
:thresholdDisplay "3.0" ;
|
|
95
|
+
:supportPolicy "lowest_cost_covering_package" ;
|
|
96
|
+
:scopeDevice "household-gateway" ;
|
|
97
|
+
:scopeEvent "heat-alert-window" ;
|
|
98
|
+
:municipality "Calidor" ;
|
|
99
|
+
:createdAt "2026-07-18T09:00:00+00:00"^^xsd:dateTime ;
|
|
100
|
+
:expiresAt "2026-07-18T21:00:00+00:00"^^xsd:dateTime ;
|
|
101
|
+
:serializedLowercase "{\"createdat\":\"2026-07-18t09:00:00+00:00\",\"expiresat\":\"2026-07-18t21:00:00+00:00\",\"id\":\"https://example.org/insight/calidor\",\"metric\":\"active_need_count\",\"municipality\":\"calidor\",\"scopedevice\":\"household-gateway\",\"scopeevent\":\"heat-alert-window\",\"supportpolicy\":\"lowest_cost_covering_package\",\"threshold\":3,\"type\":\"ins:insight\"}" .
|
|
102
|
+
|
|
103
|
+
:policy
|
|
104
|
+
a odrl:Policy ;
|
|
105
|
+
:profile "Calidor-Heatwave-Policy" ;
|
|
106
|
+
odrl:permission [
|
|
107
|
+
odrl:action odrl:use ;
|
|
108
|
+
odrl:target <https://example.org/insight/calidor> ;
|
|
109
|
+
odrl:constraint [
|
|
110
|
+
odrl:leftOperand odrl:purpose ;
|
|
111
|
+
odrl:operator odrl:eq ;
|
|
112
|
+
odrl:rightOperand "heatwave_response"
|
|
113
|
+
]
|
|
114
|
+
] ;
|
|
115
|
+
odrl:prohibition [
|
|
116
|
+
odrl:action odrl:distribute ;
|
|
117
|
+
odrl:target <https://example.org/insight/calidor> ;
|
|
118
|
+
odrl:constraint [
|
|
119
|
+
odrl:leftOperand odrl:purpose ;
|
|
120
|
+
odrl:operator odrl:eq ;
|
|
121
|
+
odrl:rightOperand "tenant_screening"
|
|
122
|
+
]
|
|
123
|
+
] ;
|
|
124
|
+
odrl:duty [
|
|
125
|
+
odrl:action odrl:delete ;
|
|
126
|
+
odrl:constraint [
|
|
127
|
+
odrl:leftOperand odrl:dateTime ;
|
|
128
|
+
odrl:operator odrl:eq ;
|
|
129
|
+
odrl:rightOperand "2026-07-18T21:00:00+00:00"^^xsd:dateTime
|
|
130
|
+
]
|
|
131
|
+
] .
|
|
132
|
+
|
|
133
|
+
:envelope
|
|
134
|
+
:insight <https://example.org/insight/calidor> ;
|
|
135
|
+
:policy :policy ;
|
|
136
|
+
:canonicalJson "{\"insight\":{\"createdAt\":\"2026-07-18T09:00:00+00:00\",\"expiresAt\":\"2026-07-18T21:00:00+00:00\",\"id\":\"https://example.org/insight/calidor\",\"metric\":\"active_need_count\",\"municipality\":\"Calidor\",\"scopeDevice\":\"household-gateway\",\"scopeEvent\":\"heat-alert-window\",\"supportPolicy\":\"lowest_cost_covering_package\",\"threshold\":3.0,\"type\":\"ins:Insight\"},\"policy\":{\"duty\":{\"action\":\"odrl:delete\",\"constraint\":{\"leftOperand\":\"odrl:dateTime\",\"operator\":\"odrl:eq\",\"rightOperand\":\"2026-07-18T21:00:00+00:00\"}},\"permission\":{\"action\":\"odrl:use\",\"constraint\":{\"leftOperand\":\"odrl:purpose\",\"operator\":\"odrl:eq\",\"rightOperand\":\"heatwave_response\"},\"target\":\"https://example.org/insight/calidor\"},\"profile\":\"Calidor-Heatwave-Policy\",\"prohibition\":{\"action\":\"odrl:distribute\",\"constraint\":{\"leftOperand\":\"odrl:purpose\",\"operator\":\"odrl:eq\",\"rightOperand\":\"tenant_screening\"},\"target\":\"https://example.org/insight/calidor\"},\"type\":\"odrl:Policy\"}}" .
|
|
137
|
+
|
|
138
|
+
:signature
|
|
139
|
+
:alg "HMAC-SHA256" ;
|
|
140
|
+
:keyid "calidor-demo-shared-secret" ;
|
|
141
|
+
:created "2026-07-18T09:00:00+00:00"^^xsd:dateTime ;
|
|
142
|
+
:payloadHashSHA256 "3780df1071b0f2eec8a881ffd48425c3a1a60738d11cc2ba7debdf1cea992d63" ;
|
|
143
|
+
:signatureHMAC "e635c7c1991742a5c36992fc0da32a7abc80b32aa5777a1142adaab55183681c" ;
|
|
144
|
+
:hmacVerificationMode :trustedPrecomputedInput .
|
|
145
|
+
|
|
146
|
+
:reasonText
|
|
147
|
+
:value "The gateway keeps raw indoor heat, vulnerability, and prepaid-energy data local, derives a priority-support signal, and shares only a scoped heatwave-response envelope with expiry.\n" .
|
|
148
|
+
|
|
149
|
+
# -----
|
|
150
|
+
# Logic
|
|
151
|
+
# -----
|
|
152
|
+
|
|
153
|
+
# heat_alert_active
|
|
154
|
+
{
|
|
155
|
+
:case :currentAlertLevel ?current ;
|
|
156
|
+
:alertLevelAtLeast ?threshold .
|
|
157
|
+
?current math:notLessThan ?threshold .
|
|
158
|
+
} => {
|
|
159
|
+
:case :heatAlertActive true .
|
|
160
|
+
} .
|
|
161
|
+
|
|
162
|
+
# unsafe_indoor_heat
|
|
163
|
+
{
|
|
164
|
+
:case :currentIndoorTempC ?temp ;
|
|
165
|
+
:indoorTempCAtLeast ?tempThreshold ;
|
|
166
|
+
:hoursAtOrAboveThreshold ?hours ;
|
|
167
|
+
:hoursAtOrAboveThresholdAtLeast ?hoursThreshold .
|
|
168
|
+
?temp math:notLessThan ?tempThreshold .
|
|
169
|
+
?hours math:notLessThan ?hoursThreshold .
|
|
170
|
+
} => {
|
|
171
|
+
:case :unsafeIndoorHeat true .
|
|
172
|
+
} .
|
|
173
|
+
|
|
174
|
+
# vulnerability_present
|
|
175
|
+
{
|
|
176
|
+
:localProfile :vulnerabilityFlag ?flag .
|
|
177
|
+
} => {
|
|
178
|
+
:case :vulnerabilityPresent true .
|
|
179
|
+
} .
|
|
180
|
+
|
|
181
|
+
# energy_constraint
|
|
182
|
+
{
|
|
183
|
+
:case :remainingPrepaidCreditEur ?credit ;
|
|
184
|
+
:energyCreditEurAtMost ?limit .
|
|
185
|
+
?limit math:notLessThan ?credit .
|
|
186
|
+
} => {
|
|
187
|
+
:case :energyConstraint true .
|
|
188
|
+
} .
|
|
189
|
+
|
|
190
|
+
# derive_insight(...)
|
|
191
|
+
{
|
|
192
|
+
:case :heatAlertActive true ;
|
|
193
|
+
:unsafeIndoorHeat true ;
|
|
194
|
+
:vulnerabilityPresent true ;
|
|
195
|
+
:energyConstraint true .
|
|
196
|
+
} => {
|
|
197
|
+
<https://example.org/insight/calidor> :derivedFromNeed "priority_cooling_support" .
|
|
198
|
+
} .
|
|
199
|
+
|
|
200
|
+
# active_need_count via score collection
|
|
201
|
+
{ :case :heatAlertActive true . } => { :score_heatAlert :value 1 . } .
|
|
202
|
+
{ :case :unsafeIndoorHeat true . } => { :score_indoorHeat :value 1 . } .
|
|
203
|
+
{ :case :vulnerabilityPresent true . } => { :score_vulnerability :value 1 . } .
|
|
204
|
+
{ :case :energyConstraint true . } => { :score_energy :value 1 . } .
|
|
205
|
+
|
|
206
|
+
{
|
|
207
|
+
( ?n { ?scoreNode :value ?n . } ?scores ) log:collectAllIn _:countScope .
|
|
208
|
+
?scores math:sum ?total .
|
|
209
|
+
} => {
|
|
210
|
+
:case :activeNeedCount ?total .
|
|
211
|
+
} .
|
|
212
|
+
|
|
213
|
+
# priority_cooling_support_needed
|
|
214
|
+
{
|
|
215
|
+
:case :activeNeedCount ?count ;
|
|
216
|
+
:minimumActiveNeedCount ?threshold .
|
|
217
|
+
?count math:notLessThan ?threshold .
|
|
218
|
+
} => {
|
|
219
|
+
:case :priorityCoolingSupportNeeded true .
|
|
220
|
+
} .
|
|
221
|
+
|
|
222
|
+
# required capabilities follow from the active needs
|
|
223
|
+
{
|
|
224
|
+
:case :heatAlertActive true ;
|
|
225
|
+
:unsafeIndoorHeat true .
|
|
226
|
+
} => {
|
|
227
|
+
:case :requiredCapability :cooling_kit .
|
|
228
|
+
} .
|
|
229
|
+
|
|
230
|
+
{
|
|
231
|
+
:case :vulnerabilityPresent true .
|
|
232
|
+
} => {
|
|
233
|
+
:case :requiredCapability :welfare_check .
|
|
234
|
+
:case :requiredCapability :transport .
|
|
235
|
+
} .
|
|
236
|
+
|
|
237
|
+
{
|
|
238
|
+
:case :energyConstraint true .
|
|
239
|
+
} => {
|
|
240
|
+
:case :requiredCapability :bill_credit .
|
|
241
|
+
} .
|
|
242
|
+
|
|
243
|
+
# payload_hash_matches
|
|
244
|
+
{
|
|
245
|
+
:envelope :canonicalJson ?json .
|
|
246
|
+
?json crypto:sha256 ?digest .
|
|
247
|
+
:signature :payloadHashSHA256 ?digest .
|
|
248
|
+
} => {
|
|
249
|
+
:check :payloadHashMatches true .
|
|
250
|
+
} .
|
|
251
|
+
|
|
252
|
+
# signature_verified
|
|
253
|
+
{
|
|
254
|
+
:signature :hmacVerificationMode :trustedPrecomputedInput .
|
|
255
|
+
} => {
|
|
256
|
+
:check :signatureVerifies true .
|
|
257
|
+
} .
|
|
258
|
+
|
|
259
|
+
# minimization_no_sensitive_terms
|
|
260
|
+
{
|
|
261
|
+
<https://example.org/insight/calidor> :serializedLowercase ?s .
|
|
262
|
+
?s string:notMatches "heat_sensitive_condition|mobility_limitation|credit|meter_trace" .
|
|
263
|
+
} => {
|
|
264
|
+
:check :minimizationStripsSensitiveTerms true .
|
|
265
|
+
} .
|
|
266
|
+
|
|
267
|
+
# scope_complete
|
|
268
|
+
{
|
|
269
|
+
<https://example.org/insight/calidor> :scopeDevice ?device ;
|
|
270
|
+
:scopeEvent ?event ;
|
|
271
|
+
:expiresAt ?expiry .
|
|
272
|
+
} => {
|
|
273
|
+
:check :scopeComplete true .
|
|
274
|
+
} .
|
|
275
|
+
|
|
276
|
+
# authorization_allowed
|
|
277
|
+
{
|
|
278
|
+
:policy odrl:permission [
|
|
279
|
+
odrl:action odrl:use ;
|
|
280
|
+
odrl:target <https://example.org/insight/calidor> ;
|
|
281
|
+
odrl:constraint [
|
|
282
|
+
odrl:leftOperand odrl:purpose ;
|
|
283
|
+
odrl:operator odrl:eq ;
|
|
284
|
+
odrl:rightOperand "heatwave_response"
|
|
285
|
+
]
|
|
286
|
+
] .
|
|
287
|
+
:case :cityAuthAt ?authAt .
|
|
288
|
+
<https://example.org/insight/calidor> :expiresAt ?expiresAt .
|
|
289
|
+
?authAt math:notGreaterThan ?expiresAt .
|
|
290
|
+
} => {
|
|
291
|
+
:decision
|
|
292
|
+
:at "2026-07-18T09:05:00+00:00"^^xsd:dateTime ;
|
|
293
|
+
:outcome "Allowed" ;
|
|
294
|
+
:target <https://example.org/insight/calidor> .
|
|
295
|
+
:check :authorizationAllowed true .
|
|
296
|
+
} .
|
|
297
|
+
|
|
298
|
+
# eligible package = within budget and covers every required capability
|
|
299
|
+
{
|
|
300
|
+
?pkg a :SupportPackage ;
|
|
301
|
+
:costEur ?cost .
|
|
302
|
+
:case :maxPackageCostEur ?budget .
|
|
303
|
+
?budget math:notLessThan ?cost .
|
|
304
|
+
1 log:notIncludes {
|
|
305
|
+
:case :requiredCapability ?cap .
|
|
306
|
+
1 log:notIncludes { ?pkg :capability ?cap . } .
|
|
307
|
+
} .
|
|
308
|
+
} => {
|
|
309
|
+
:case :eligiblePackage ?pkg .
|
|
310
|
+
} .
|
|
311
|
+
|
|
312
|
+
# recommend the lowest-cost eligible package
|
|
313
|
+
{
|
|
314
|
+
:case :eligiblePackage ?candidate .
|
|
315
|
+
?candidate :costEur ?candidateCost .
|
|
316
|
+
1 log:notIncludes {
|
|
317
|
+
:case :eligiblePackage ?other .
|
|
318
|
+
?other :costEur ?otherCost .
|
|
319
|
+
?otherCost math:lessThan ?candidateCost .
|
|
320
|
+
} .
|
|
321
|
+
} => {
|
|
322
|
+
:case :recommendedPackage ?candidate .
|
|
323
|
+
} .
|
|
324
|
+
|
|
325
|
+
{
|
|
326
|
+
:case :recommendedPackage ?pkg .
|
|
327
|
+
?pkg :packageName ?name .
|
|
328
|
+
} => {
|
|
329
|
+
:decision :recommendedPackageName ?name .
|
|
330
|
+
} .
|
|
331
|
+
|
|
332
|
+
# recommended_package_eligible
|
|
333
|
+
{
|
|
334
|
+
:case :recommendedPackage ?pkg .
|
|
335
|
+
:case :eligiblePackage ?pkg .
|
|
336
|
+
} => {
|
|
337
|
+
:check :recommendedPackageEligible true .
|
|
338
|
+
} .
|
|
339
|
+
|
|
340
|
+
# duty_timing_consistent
|
|
341
|
+
{
|
|
342
|
+
:case :cityDutyAt ?dutyAt .
|
|
343
|
+
<https://example.org/insight/calidor> :expiresAt ?expiresAt .
|
|
344
|
+
?dutyAt math:notGreaterThan ?expiresAt .
|
|
345
|
+
} => {
|
|
346
|
+
:check :dutyTimingConsistent true .
|
|
347
|
+
} .
|
|
348
|
+
|
|
349
|
+
# tenant_screening_prohibited
|
|
350
|
+
{
|
|
351
|
+
:policy odrl:prohibition [
|
|
352
|
+
odrl:action odrl:distribute ;
|
|
353
|
+
odrl:constraint [
|
|
354
|
+
odrl:rightOperand "tenant_screening"
|
|
355
|
+
]
|
|
356
|
+
] .
|
|
357
|
+
} => {
|
|
358
|
+
:check :tenantScreeningProhibited true .
|
|
359
|
+
} .
|
|
360
|
+
|
|
361
|
+
# visible support checks
|
|
362
|
+
{ :case :heatAlertActive true . } => { :check :heatAlertActive true . } .
|
|
363
|
+
{ :case :unsafeIndoorHeat true . } => { :check :unsafeIndoorHeat true . } .
|
|
364
|
+
{ :case :priorityCoolingSupportNeeded true . } => { :check :priorityCoolingSupportNeeded true . } .
|
|
365
|
+
|
|
366
|
+
{
|
|
367
|
+
:check :signatureVerifies true ;
|
|
368
|
+
:payloadHashMatches true ;
|
|
369
|
+
:minimizationStripsSensitiveTerms true ;
|
|
370
|
+
:scopeComplete true ;
|
|
371
|
+
:authorizationAllowed true ;
|
|
372
|
+
:heatAlertActive true ;
|
|
373
|
+
:unsafeIndoorHeat true ;
|
|
374
|
+
:priorityCoolingSupportNeeded true ;
|
|
375
|
+
:recommendedPackageEligible true ;
|
|
376
|
+
:dutyTimingConsistent true ;
|
|
377
|
+
:tenantScreeningProhibited true .
|
|
378
|
+
} => {
|
|
379
|
+
:result :allChecksPass true .
|
|
380
|
+
} .
|
|
381
|
+
|
|
382
|
+
# -------------------------------------
|
|
383
|
+
# Hard checks (Eyeling inference fuses)
|
|
384
|
+
# -------------------------------------
|
|
385
|
+
|
|
386
|
+
{
|
|
387
|
+
:case :cityAuthAt ?authAt .
|
|
388
|
+
<https://example.org/insight/calidor> :expiresAt ?expiresAt .
|
|
389
|
+
?authAt math:greaterThan ?expiresAt .
|
|
390
|
+
} => false .
|
|
391
|
+
|
|
392
|
+
{
|
|
393
|
+
:case :cityDutyAt ?dutyAt .
|
|
394
|
+
<https://example.org/insight/calidor> :expiresAt ?expiresAt .
|
|
395
|
+
?dutyAt math:greaterThan ?expiresAt .
|
|
396
|
+
} => false .
|
|
397
|
+
|
|
398
|
+
{
|
|
399
|
+
:envelope :canonicalJson ?json .
|
|
400
|
+
?json crypto:sha256 ?actual .
|
|
401
|
+
:signature :payloadHashSHA256 ?expected .
|
|
402
|
+
?actual log:notEqualTo ?expected .
|
|
403
|
+
} => false .
|
|
404
|
+
|
|
405
|
+
{
|
|
406
|
+
:case :recommendedPackage ?pkg .
|
|
407
|
+
?pkg :costEur ?cost .
|
|
408
|
+
:case :maxPackageCostEur ?budget .
|
|
409
|
+
?cost math:greaterThan ?budget .
|
|
410
|
+
} => false .
|
|
411
|
+
|
|
412
|
+
{
|
|
413
|
+
:case :recommendedPackage ?pkg .
|
|
414
|
+
:case :requiredCapability ?cap .
|
|
415
|
+
1 log:notIncludes { ?pkg :capability ?cap . } .
|
|
416
|
+
} => false .
|
|
417
|
+
|
|
418
|
+
# --------------------------------------
|
|
419
|
+
# ARC rendering through log:outputString
|
|
420
|
+
# --------------------------------------
|
|
421
|
+
|
|
422
|
+
:out01 log:outputString "=== Answer ===\n" .
|
|
423
|
+
|
|
424
|
+
{
|
|
425
|
+
:case :recommendedPackage ?pkg .
|
|
426
|
+
?pkg :packageName ?name .
|
|
427
|
+
("The city is allowed to use a narrow heatwave-response insight and recommends %s for this household.\n" ?name) string:format ?line .
|
|
428
|
+
} => {
|
|
429
|
+
:out02 log:outputString ?line .
|
|
430
|
+
} .
|
|
431
|
+
|
|
432
|
+
:out03 log:outputString "case : calidor\n" .
|
|
433
|
+
:out04 log:outputString "decision : Allowed\n" .
|
|
434
|
+
:out05 log:outputString "municipality : Calidor\n" .
|
|
435
|
+
|
|
436
|
+
{
|
|
437
|
+
:case :recommendedPackage ?pkg .
|
|
438
|
+
?pkg :packageName ?name .
|
|
439
|
+
("recommended package : %s\n" ?name) string:format ?line .
|
|
440
|
+
} => {
|
|
441
|
+
:out06 log:outputString ?line .
|
|
442
|
+
} .
|
|
443
|
+
|
|
444
|
+
:out07 log:outputString "\n=== Reason Why ===\n" .
|
|
445
|
+
:out08 log:outputString "The gateway desensitizes local heat, vulnerability, and prepaid-energy stress into an expiring municipal support insight, and the city consumes that envelope only for heatwave response.\n" .
|
|
446
|
+
:out09 log:outputString "metric : active_need_count\n" .
|
|
447
|
+
|
|
448
|
+
{
|
|
449
|
+
<https://example.org/insight/calidor> :thresholdDisplay ?threshold .
|
|
450
|
+
("threshold : %s\n" ?threshold) string:format ?line .
|
|
451
|
+
} => {
|
|
452
|
+
:out10 log:outputString ?line .
|
|
453
|
+
} .
|
|
454
|
+
|
|
455
|
+
:out11 log:outputString "scope : household-gateway @ heat-alert-window\n" .
|
|
456
|
+
:out12 log:outputString "required capabilities: bill_credit, cooling_kit, transport, welfare_check\n" .
|
|
457
|
+
|
|
458
|
+
{
|
|
459
|
+
:case :activeNeedCount ?count .
|
|
460
|
+
("active need count : %s\n" ?count) string:format ?line .
|
|
461
|
+
} => {
|
|
462
|
+
:out13 log:outputString ?line .
|
|
463
|
+
} .
|
|
464
|
+
|
|
465
|
+
{
|
|
466
|
+
:signature :alg ?alg .
|
|
467
|
+
("signature alg : %s\n" ?alg) string:format ?line .
|
|
468
|
+
} => {
|
|
469
|
+
:out14 log:outputString ?line .
|
|
470
|
+
} .
|
|
471
|
+
|
|
472
|
+
:out15 log:outputString "expires at : 2026-07-18T21:00:00+00:00\n" .
|
|
473
|
+
|
|
474
|
+
{
|
|
475
|
+
:reasonText :value ?reason .
|
|
476
|
+
("reason.txt : %s" ?reason) string:format ?line .
|
|
477
|
+
} => {
|
|
478
|
+
:out16 log:outputString ?line .
|
|
479
|
+
} .
|
|
480
|
+
|
|
481
|
+
{
|
|
482
|
+
:case :dispatchesLogged ?n .
|
|
483
|
+
("dispatches logged : %s\n" ?n) string:format ?line .
|
|
484
|
+
} => {
|
|
485
|
+
:out17 log:outputString ?line .
|
|
486
|
+
} .
|
|
487
|
+
|
|
488
|
+
:out18 log:outputString "\n=== Check ===\n" .
|
|
489
|
+
|
|
490
|
+
{ :check :signatureVerifies true . } => { :out19 log:outputString "signature verifies : yes\n" . } .
|
|
491
|
+
{ :check :payloadHashMatches true . } => { :out20 log:outputString "payload hash matches : yes\n" . } .
|
|
492
|
+
{ :check :minimizationStripsSensitiveTerms true . } => { :out21 log:outputString "minimization strips sensitive terms: yes\n" . } .
|
|
493
|
+
{ :check :scopeComplete true . } => { :out22 log:outputString "scope complete : yes\n" . } .
|
|
494
|
+
{ :check :authorizationAllowed true . } => { :out23 log:outputString "authorization allowed : yes\n" . } .
|
|
495
|
+
{ :check :heatAlertActive true . } => { :out24 log:outputString "heat-alert active : yes\n" . } .
|
|
496
|
+
{ :check :unsafeIndoorHeat true . } => { :out25 log:outputString "unsafe indoor heat : yes\n" . } .
|
|
497
|
+
{ :check :priorityCoolingSupportNeeded true . } => { :out26 log:outputString "priority cooling support needed : yes\n" . } .
|
|
498
|
+
{ :check :recommendedPackageEligible true . } => { :out27 log:outputString "recommended package eligible : yes\n" . } .
|
|
499
|
+
{ :check :dutyTimingConsistent true . } => { :out28 log:outputString "duty timing consistent : yes\n" . } .
|
|
500
|
+
{ :check :tenantScreeningProhibited true . } => { :out29 log:outputString "tenant screening prohibited : yes\n" . } .
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
=== Answer ===
|
|
2
|
+
The city is allowed to use a narrow heatwave-response insight and recommends Calidor Priority Cooling Bundle for this household.
|
|
3
|
+
case : calidor
|
|
4
|
+
decision : Allowed
|
|
5
|
+
municipality : Calidor
|
|
6
|
+
recommended package : Calidor Priority Cooling Bundle
|
|
7
|
+
|
|
8
|
+
=== Reason Why ===
|
|
9
|
+
The gateway desensitizes local heat, vulnerability, and prepaid-energy stress into an expiring municipal support insight, and the city consumes that envelope only for heatwave response.
|
|
10
|
+
metric : active_need_count
|
|
11
|
+
threshold : 3.0
|
|
12
|
+
scope : household-gateway @ heat-alert-window
|
|
13
|
+
required capabilities: bill_credit, cooling_kit, transport, welfare_check
|
|
14
|
+
signature alg : HMAC-SHA256
|
|
15
|
+
expires at : 2026-07-18T21:00:00+00:00
|
|
16
|
+
reason.txt : The gateway keeps raw indoor heat, vulnerability, and prepaid-energy data local, derives a priority-support signal, and shares only a scoped heatwave-response envelope with expiry.
|
|
17
|
+
dispatches logged : 1
|
|
18
|
+
|
|
19
|
+
=== Check ===
|
|
20
|
+
signature verifies : yes
|
|
21
|
+
payload hash matches : yes
|
|
22
|
+
minimization strips sensitive terms: yes
|
|
23
|
+
scope complete : yes
|
|
24
|
+
authorization allowed : yes
|
|
25
|
+
heat-alert active : yes
|
|
26
|
+
unsafe indoor heat : yes
|
|
27
|
+
recommended package eligible : yes
|
|
28
|
+
duty timing consistent : yes
|
|
29
|
+
tenant screening prohibited : yes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eyeling",
|
|
3
|
-
"version": "1.22.
|
|
3
|
+
"version": "1.22.3",
|
|
4
4
|
"description": "A minimal Notation3 (N3) reasoner in JavaScript.",
|
|
5
5
|
"main": "./index.js",
|
|
6
6
|
"keywords": [
|
|
@@ -45,12 +45,11 @@
|
|
|
45
45
|
"test:builtins": "node test/builtins.test.js",
|
|
46
46
|
"test:n3gen": "node test/n3gen.test.js",
|
|
47
47
|
"test:examples": "node test/examples.test.js",
|
|
48
|
-
"test:arcling": "node test/arcling.test.js",
|
|
49
48
|
"test:manifest": "node test/manifest.test.js",
|
|
50
49
|
"test:playground": "node test/playground.test.js",
|
|
51
50
|
"test:package": "node test/package.test.js",
|
|
52
51
|
"pretest": "npm run build && npm run test:packlist",
|
|
53
|
-
"test": "npm run test:api && npm run test:builtins && npm run test:n3gen && npm run test:examples && npm run test:
|
|
52
|
+
"test": "npm run test:api && npm run test:builtins && npm run test:n3gen && npm run test:examples && npm run test:manifest && npm run test:playground",
|
|
54
53
|
"posttest": "npm run test:package",
|
|
55
54
|
"preversion": "npm test",
|
|
56
55
|
"postversion": "git push origin HEAD --follow-tags"
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"caseName": "Delfour",
|
|
3
|
-
"retailer": "Delfour",
|
|
4
|
-
"question": "Is the Delfour self-scanner allowed to use a neutral shopping insight for shopping assistance, and if so what lower-sugar alternative should it suggest?",
|
|
5
|
-
"timestamps": {
|
|
6
|
-
"createdAt": "2025-10-05T20:33:48.907163+00:00",
|
|
7
|
-
"expiresAt": "2025-10-05T22:33:48.907185+00:00",
|
|
8
|
-
"authorizedAt": "2025-10-05T20:35:48.907163+00:00",
|
|
9
|
-
"dutyPerformedAt": "2025-10-05T20:37:48.907163+00:00"
|
|
10
|
-
},
|
|
11
|
-
"evaluationContext": {
|
|
12
|
-
"scopeDevice": "self-scanner",
|
|
13
|
-
"scopeEvent": "pick_up_scanner",
|
|
14
|
-
"purpose": "shopping_assist",
|
|
15
|
-
"prohibitedReusePurpose": "marketing",
|
|
16
|
-
"requestAction": "odrl:use"
|
|
17
|
-
},
|
|
18
|
-
"thresholds": {
|
|
19
|
-
"sugarPerServingGAtLeast": 10.0
|
|
20
|
-
},
|
|
21
|
-
"householdProfile": {
|
|
22
|
-
"condition": "Diabetes"
|
|
23
|
-
},
|
|
24
|
-
"catalog": [
|
|
25
|
-
{
|
|
26
|
-
"id": "prod:BIS_001",
|
|
27
|
-
"name": "Classic Tea Biscuits",
|
|
28
|
-
"sugarTenths": 120,
|
|
29
|
-
"sugarPerServing": 12.0
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
"id": "prod:BIS_101",
|
|
33
|
-
"name": "Low-Sugar Tea Biscuits",
|
|
34
|
-
"sugarTenths": 30,
|
|
35
|
-
"sugarPerServing": 3.0
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
"id": "prod:CHOC_050",
|
|
39
|
-
"name": "Milk Chocolate Bar",
|
|
40
|
-
"sugarTenths": 150,
|
|
41
|
-
"sugarPerServing": 15.0
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
"id": "prod:CHOC_150",
|
|
45
|
-
"name": "85% Dark Chocolate",
|
|
46
|
-
"sugarTenths": 60,
|
|
47
|
-
"sugarPerServing": 6.0
|
|
48
|
-
}
|
|
49
|
-
],
|
|
50
|
-
"scan": {
|
|
51
|
-
"scannedProductId": "prod:BIS_001"
|
|
52
|
-
},
|
|
53
|
-
"insightPolicy": {
|
|
54
|
-
"id": "https://example.org/insight/delfour",
|
|
55
|
-
"metric": "sugar_g_per_serving",
|
|
56
|
-
"type": "ins:Insight",
|
|
57
|
-
"suggestionPolicy": "lower_metric_first_higher_price_ok",
|
|
58
|
-
"policyType": "odrl:Policy",
|
|
59
|
-
"policyProfile": "Delfour-Insight-Policy"
|
|
60
|
-
},
|
|
61
|
-
"integrity": {
|
|
62
|
-
"hashAlgorithm": "SHA-256",
|
|
63
|
-
"macAlgorithm": "HMAC-SHA-256",
|
|
64
|
-
"secret": "neutral-insight-demo-shared-secret",
|
|
65
|
-
"verificationMode": "trustedPrecomputedInput"
|
|
66
|
-
}
|
|
67
|
-
}
|