eyeling 1.22.1 → 1.22.2

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.
@@ -0,0 +1,166 @@
1
+ # Calidor — ARC Specification
2
+
3
+ ## Status
4
+
5
+ This document is the normative specification for the Calidor case. The file `calidor.model.go` is the reference Go implementation of these clauses. The file `calidor.data.json` is the instance evaluated in this bundle. The file `calidor.expected.json` is the conformance vector for that instance.
6
+
7
+ ## Insight Economy context
8
+
9
+ This case models municipal heatwave support. A household gateway observes local indoor heat stress, local vulnerability signals, and local prepaid-energy stress. Those raw details remain local. The system shares only a narrow, expiring insight that the household qualifies for priority cooling support during the current heat-alert window.
10
+
11
+ The city may use that insight for heatwave response. It may not reuse it for unrelated purposes such as tenant screening.
12
+
13
+ ## Conventions
14
+
15
+ - “iff” means “if and only if”.
16
+ - A clause identifier such as `R1` or `G2` is normative.
17
+ - A conforming implementation may be written in any language, but it shall produce the same derived values and pass/fail outcomes for the supplied instance.
18
+ - The reference implementation in this bundle is written in Go.
19
+ - Input validation is part of the reference model. A malformed instance shall fail before evaluation.
20
+
21
+ ## Vocabulary
22
+
23
+ **V1. Heat alert** is the municipal emergency context.
24
+
25
+ **V2. Unsafe indoor heat** is sustained indoor temperature at or above the configured threshold.
26
+
27
+ **V3. Vulnerability presence** is the existence of at least one local heat-sensitivity or mobility-related flag.
28
+
29
+ **V4. Energy constraint** is insufficient prepaid energy credit to sustain cooling use.
30
+
31
+ **V5. Support package** is a municipal assistance option with a cost and a set of capabilities.
32
+
33
+ **V6. Insight envelope** is the ordered pair `(insight, policy)` together with integrity metadata.
34
+
35
+ ## Input instance
36
+
37
+ **I1.** The municipality is `Calidor`.
38
+
39
+ **I2.** The current heat alert level is `4`.
40
+
41
+ **I3.** The alert threshold is `3`.
42
+
43
+ **I4.** The current indoor temperature is `31.4` °C.
44
+
45
+ **I5.** Unsafe indoor heat requires at least `30.0` °C for at least `6` hours.
46
+
47
+ **I6.** The household has local vulnerability flags.
48
+
49
+ **I7.** Remaining prepaid energy credit is `3.2` EUR.
50
+
51
+ **I8.** Energy constraint holds at or below `5.0` EUR.
52
+
53
+ **I9.** Priority cooling support requires at least `3` active needs.
54
+
55
+ **I10.** The support catalog is the one listed in `calidor.data.json`.
56
+
57
+ ## Derivation clauses
58
+
59
+ **R1. HeatAlertActive.** `HeatAlertActive` holds iff `currentAlertLevel ≥ alertLevelAtLeast`.
60
+
61
+ **R2. UnsafeIndoorHeat.** `UnsafeIndoorHeat` holds iff:
62
+
63
+ 1. `currentIndoorTempC ≥ indoorTempCAtLeast`; and
64
+ 2. `hoursAtOrAboveThreshold ≥ hoursAtOrAboveThresholdAtLeast`.
65
+
66
+ **R3. VulnerabilityPresent.** `VulnerabilityPresent` holds iff the local vulnerability flag list is non-empty.
67
+
68
+ **R4. EnergyConstraint.** `EnergyConstraint` holds iff `remainingPrepaidCreditEur ≤ energyCreditEurAtMost`.
69
+
70
+ **R5. ActiveNeedCount.** `ActiveNeedCount` is the number of true predicates among:
71
+
72
+ - `HeatAlertActive`
73
+ - `UnsafeIndoorHeat`
74
+ - `VulnerabilityPresent`
75
+ - `EnergyConstraint`
76
+
77
+ **R6. PriorityCoolingSupportNeeded.** `PriorityCoolingSupportNeeded` holds iff `ActiveNeedCount ≥ minimumActiveNeedCount`.
78
+
79
+ **R7. RequiredCapabilities.** The required capability set is formed as follows:
80
+
81
+ 1. if `HeatAlertActive` and `UnsafeIndoorHeat`, include `cooling_kit`;
82
+ 2. if `VulnerabilityPresent`, include `welfare_check` and `transport`;
83
+ 3. if `EnergyConstraint`, include `bill_credit`.
84
+
85
+ The resulting set is sorted lexically for reporting.
86
+
87
+ **R8. EligiblePackage(p).** For a package `p`, `EligiblePackage(p)` holds iff:
88
+
89
+ 1. `p.costEur ≤ maxPackageCostEur`; and
90
+ 2. `p.capabilities` cover every entry in `RequiredCapabilities`.
91
+
92
+ **R9. RecommendedPackage.** `RecommendedPackage` is the eligible package with minimum `costEur`, breaking ties lexically by `id`.
93
+
94
+ ## Governance clauses
95
+
96
+ **G1. AuthorizedUse.** `AuthorizedUse` holds iff:
97
+
98
+ 1. the requested action is `odrl:use`;
99
+ 2. the requested purpose is `heatwave_response`; and
100
+ 3. the authorization time is not later than the expiry time.
101
+
102
+ **G2. TenantScreeningProhibited.** `TenantScreeningProhibited` holds iff the policy prohibits distribution for purpose `tenant_screening`.
103
+
104
+ **G3. DutyTimely.** `DutyTimely` holds iff the duty-performance time is not later than the expiry time.
105
+
106
+ ## Integrity and minimization clauses
107
+
108
+ **M1. CanonicalEnvelope.** The canonical envelope string is the JSON serialization of the ordered pair `(insight, policy)` with keys emitted in this exact sequence:
109
+
110
+ - insight: `createdAt`, `expiresAt`, `id`, `metric`, `municipality`, `scopeDevice`, `scopeEvent`, `supportPolicy`, `threshold`, `type`
111
+ - policy: `duty`, `permission`, `profile`, `prohibition`, `type`
112
+
113
+ For this case, `threshold` is serialized lexically as `3.0` rather than `3`, because the integrity vector is defined over those exact envelope bytes.
114
+
115
+ **M2. PayloadHashMatches.** `PayloadHashMatches` holds iff the model-computed SHA-256 of `CanonicalEnvelope` equals the expected SHA-256 value recorded in the conformance vector.
116
+
117
+ **M3. SignatureVerifies.** `SignatureVerifies` holds iff the model-computed HMAC-SHA-256 of `CanonicalEnvelope` equals the expected HMAC value recorded in the conformance vector.
118
+
119
+ **M4. MinimizationRespected.** `MinimizationRespected` holds iff the serialized insight contains none of the forbidden terms:
120
+
121
+ - `heat_sensitive_condition`
122
+ - `mobility_limitation`
123
+ - `credit`
124
+ - `meter_trace`
125
+
126
+ **M5. ScopeComplete.** `ScopeComplete` holds iff the insight contains `scopeDevice`, `scopeEvent`, and `expiresAt`.
127
+
128
+ ## Output contract
129
+
130
+ **O1. Answer.** A conforming renderer shall expose:
131
+
132
+ - the main recommendation sentence
133
+ - recommended package
134
+ - required capabilities
135
+ - payload hash
136
+ - envelope HMAC
137
+
138
+ **O2. Reason Why.** A conforming renderer shall explain that raw household heat, vulnerability, and prepaid-energy details remain local and that only a narrow heatwave-response insight is shared.
139
+
140
+ **O3. Check.** A conforming renderer shall expose a named yes/no or PASS/FAIL outcome for each of:
141
+
142
+ - `signatureVerifies`
143
+ - `payloadHashMatches`
144
+ - `minimizationRespected`
145
+ - `scopeComplete`
146
+ - `authorizationAllowed`
147
+ - `heatAlertActive`
148
+ - `unsafeIndoorHeat`
149
+ - `priorityCoolingSupportNeeded`
150
+ - `recommendedPackageEligible`
151
+ - `dutyTimingConsistent`
152
+ - `tenantScreeningProhibited`
153
+
154
+ ## Reference outcome for this instance
155
+
156
+ For the supplied instance:
157
+
158
+ - `HeatAlertActive = true`
159
+ - `UnsafeIndoorHeat = true`
160
+ - `VulnerabilityPresent = true`
161
+ - `EnergyConstraint = true`
162
+ - `ActiveNeedCount = 4`
163
+ - `PriorityCoolingSupportNeeded = true`
164
+ - `RecommendedPackage = "Calidor Priority Cooling Bundle"`
165
+
166
+ The expected ARC report and integrity values are recorded in `calidor.expected.json`.
@@ -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