eyeling 1.16.3 → 1.16.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/HANDBOOK.md +153 -0
  2. package/README.md +0 -1
  3. package/examples/auroracare.n3 +528 -0
  4. package/examples/control-system.n3 +223 -47
  5. package/examples/delfour.n3 +409 -0
  6. package/examples/gps.n3 +144 -53
  7. package/examples/ill-formed-literals.n3 +195 -0
  8. package/examples/output/auroracare.n3 +118 -0
  9. package/examples/output/control-system.n3 +24 -1
  10. package/examples/output/delfour.n3 +40 -0
  11. package/examples/output/gps.n3 +14 -2
  12. package/examples/output/ill-formed-literals.n3 +27 -0
  13. package/examples/output/parcellocker.n3 +15 -0
  14. package/examples/parcellocker.n3 +164 -0
  15. package/eyeling.js +16 -1
  16. package/lib/engine.js +14 -1
  17. package/lib/prelude.js +2 -0
  18. package/package.json +2 -3
  19. package/arctifacts/README.md +0 -59
  20. package/arctifacts/ackermann.html +0 -678
  21. package/arctifacts/auroracare.html +0 -1297
  22. package/arctifacts/bike-trip.html +0 -752
  23. package/arctifacts/binomial-theorem.html +0 -631
  24. package/arctifacts/bmi.html +0 -511
  25. package/arctifacts/building-performance.html +0 -750
  26. package/arctifacts/clinical-care.html +0 -726
  27. package/arctifacts/collatz.html +0 -403
  28. package/arctifacts/complex.html +0 -321
  29. package/arctifacts/control-system.html +0 -482
  30. package/arctifacts/delfour.html +0 -849
  31. package/arctifacts/earthquake-epicenter.html +0 -982
  32. package/arctifacts/eco-route.html +0 -662
  33. package/arctifacts/euclid-infinitude.html +0 -564
  34. package/arctifacts/euler-identity.html +0 -667
  35. package/arctifacts/exoplanet-transit.html +0 -1000
  36. package/arctifacts/faltings-theorem.html +0 -1046
  37. package/arctifacts/fibonacci.html +0 -299
  38. package/arctifacts/fundamental-theorem-arithmetic.html +0 -398
  39. package/arctifacts/godel-numbering.html +0 -743
  40. package/arctifacts/gps-bike.html +0 -759
  41. package/arctifacts/gps-clinical-bench.html +0 -792
  42. package/arctifacts/graph-french.html +0 -449
  43. package/arctifacts/grass-molecular.html +0 -592
  44. package/arctifacts/group-theory.html +0 -740
  45. package/arctifacts/health-info.html +0 -833
  46. package/arctifacts/kaprekar-constant.html +0 -576
  47. package/arctifacts/lee.html +0 -805
  48. package/arctifacts/linked-lists.html +0 -502
  49. package/arctifacts/lldm.html +0 -612
  50. package/arctifacts/matrix-multiplication.html +0 -502
  51. package/arctifacts/matrix.html +0 -651
  52. package/arctifacts/newton-raphson.html +0 -944
  53. package/arctifacts/peano-factorial.html +0 -456
  54. package/arctifacts/pi.html +0 -363
  55. package/arctifacts/polynomial.html +0 -646
  56. package/arctifacts/prime.html +0 -366
  57. package/arctifacts/pythagorean-theorem.html +0 -468
  58. package/arctifacts/rest-path.html +0 -469
  59. package/arctifacts/roots-of-unity.html +0 -363
  60. package/arctifacts/turing.html +0 -409
  61. package/arctifacts/wind-turbines.html +0 -726
@@ -1,9 +1,22 @@
1
- # ==============
2
- # Control System
3
- # ==============
1
+ # ===========================================================================
2
+ # Control System — ARC-style rewrite of Eyeling's control example.
3
+ # Original example: https://raw.githubusercontent.com/eyereasoner/eyeling/refs/heads/main/examples/control-system.n3
4
+ #
5
+ # This example shows how a small control system turns sensor readings into two
6
+ # actuator commands. One command anticipates a known disturbance before it hits
7
+ # the system. The other compares the measured output with the target and then
8
+ # corrects the remaining gap. The ARC presentation makes the result easy to
9
+ # inspect by giving an Answer, a clear Reason Why, and independent Checks.
10
+ # ===========================================================================
4
11
 
12
+ @prefix log: <http://www.w3.org/2000/10/swap/log#>.
5
13
  @prefix math: <http://www.w3.org/2000/10/swap/math#>.
6
- @prefix : <https://eyereasoner.github.io/eye/reasoning/cs#>.
14
+ @prefix string: <http://www.w3.org/2000/10/swap/string#>.
15
+ @prefix : <https://example.org/control-system#>.
16
+
17
+ # ---------------------------------------------------------------------------
18
+ # Facts
19
+ # ---------------------------------------------------------------------------
7
20
 
8
21
  # input
9
22
  :input1 :measurement1 (6 11).
@@ -23,55 +36,218 @@
23
36
  :output2 :measurement4 24.
24
37
  :output2 :target2 29.
25
38
 
26
- # forward rules
27
- {
28
- :input1 :measurement10 ?M1.
29
- :input2 :measurement2 true.
30
- :disturbance1 :measurement3 ?D1.
31
- (?M1 19.6) math:product ?C1. # proportial part
32
- (10 ?C2) math:exponentiation ?D1. # compensation part
33
- (?C1 ?C2) math:difference ?C. # simple feedforward control
39
+ # ---------------------------------------------------------------------------
40
+ # Original control logic, preserved from the source example
41
+ # ---------------------------------------------------------------------------
42
+
43
+ { :input1 :measurement10 ?M1.
44
+ :input2 :measurement2 true.
45
+ :disturbance1 :measurement3 ?D1.
46
+ (?M1 19.6) math:product ?C1.
47
+ (10 ?C2) math:exponentiation ?D1.
48
+ (?C1 ?C2) math:difference ?C.
34
49
  }
35
- =>
36
- {
37
- :actuator1 :control1 ?C.
38
- }.
50
+ =>
51
+ { :actuator1 :control1 ?C. }.
39
52
 
40
- {
41
- :input3 :measurement3 ?M3.
42
- :state3 :observation3 ?P3.
43
- :output2 :measurement4 ?M4.
44
- :output2 :target2 ?T2.
45
- (?T2 ?M4) math:difference ?E. # error
46
- (?P3 ?M4) math:difference ?D. # differential error
47
- (5.8 ?E) math:product ?C1. # proportial part
48
- (7.3 ?E) math:quotient ?N. # nonlinear factor
49
- (?N ?D) math:product ?C2. # nonlinear differential part
50
- (?C1 ?C2) math:sum ?C. # PND feedback control
53
+ { :input3 :measurement3 ?M3.
54
+ :state3 :observation3 ?P3.
55
+ :output2 :measurement4 ?M4.
56
+ :output2 :target2 ?T2.
57
+ (?T2 ?M4) math:difference ?E.
58
+ (?P3 ?M4) math:difference ?D.
59
+ (5.8 ?E) math:product ?C1.
60
+ (7.3 ?E) math:quotient ?N.
61
+ (?N ?D) math:product ?C2.
62
+ (?C1 ?C2) math:sum ?C.
51
63
  }
52
- =>
53
- {
54
- :actuator2 :control1 ?C.
64
+ =>
65
+ { :actuator2 :control1 ?C. }.
66
+
67
+ { ?I :measurement10 ?M. }
68
+ <=
69
+ { ?I :measurement1 (?M1 ?M2).
70
+ ?M1 math:lessThan ?M2.
71
+ (?M2 ?M1) math:difference ?M3.
72
+ (?M3 0.5) math:exponentiation ?M.
55
73
  }.
56
74
 
57
- # backward rules
58
- {
59
- ?I :measurement10 ?M.
60
- }
61
- <=
62
- {
63
- ?I :measurement1 (?M1 ?M2).
64
- ?M1 math:lessThan ?M2.
65
- (?M2 ?M1) math:difference ?M3.
66
- (?M3 0.5) math:exponentiation ?M.
75
+ { ?I :measurement10 ?M1. }
76
+ <=
77
+ { ?I :measurement1 (?M1 ?M2).
78
+ ?M1 math:notLessThan ?M2.
67
79
  }.
68
80
 
69
- {
70
- ?I :measurement10 ?M1.
81
+ # ---------------------------------------------------------------------------
82
+ # Explanation layer: compute an explicit why-story from the raw facts
83
+ # ---------------------------------------------------------------------------
84
+
85
+ { :input1 :measurement1 (?Low ?High).
86
+ ?Low math:lessThan ?High.
87
+ (?High ?Low) math:difference ?Gap.
88
+ (?Gap 0.5) math:exponentiation ?Norm.
71
89
  }
72
- <=
73
- {
74
- ?I :measurement1 (?M1 ?M2).
75
- ?M1 math:notLessThan ?M2.
76
- }.
90
+ =>
91
+ { :why :sensorTrend "rising";
92
+ :sensorLow ?Low;
93
+ :sensorHigh ?High;
94
+ :sensorGap ?Gap;
95
+ :normalizedMeasurement ?Norm. }.
96
+
97
+ { :why :normalizedMeasurement ?Norm.
98
+ (?Norm 19.6) math:product ?Prop.
99
+ :disturbance1 :measurement3 ?Dist.
100
+ (10 ?Comp) math:exponentiation ?Dist.
101
+ (?Prop ?Comp) math:difference ?Cmd.
102
+ }
103
+ =>
104
+ { :why :feedforwardProportional ?Prop;
105
+ :disturbanceMagnitude ?Dist;
106
+ :disturbanceCompensation ?Comp;
107
+ :feedforwardCommand ?Cmd. }.
108
+
109
+ { :state3 :observation3 ?Observed.
110
+ :output2 :measurement4 ?Measured.
111
+ :output2 :target2 ?Target.
112
+ (?Target ?Measured) math:difference ?Err.
113
+ (?Observed ?Measured) math:difference ?DiffErr.
114
+ (5.8 ?Err) math:product ?Prop.
115
+ (7.3 ?Err) math:quotient ?Factor.
116
+ (?Factor ?DiffErr) math:product ?DiffPart.
117
+ (?Prop ?DiffPart) math:sum ?Cmd.
118
+ }
119
+ =>
120
+ { :why :observedState ?Observed;
121
+ :measuredOutput ?Measured;
122
+ :targetOutput ?Target;
123
+ :trackingError ?Err;
124
+ :differentialError ?DiffErr;
125
+ :feedbackProportional ?Prop;
126
+ :feedbackFactor ?Factor;
127
+ :feedbackDifferential ?DiffPart;
128
+ :feedbackCommand ?Cmd. }.
129
+
130
+ { :actuator1 :control1 ?A1.
131
+ :actuator2 :control1 ?A2. }
132
+ =>
133
+ { :decision :answer "Send both actuator commands now.";
134
+ :actuator1Command ?A1;
135
+ :actuator2Command ?A2. }.
136
+
137
+ # ---------------------------------------------------------------------------
138
+ # ARC report
139
+ # ---------------------------------------------------------------------------
140
+
141
+ { :decision :answer ?Answer;
142
+ :actuator1Command ?A1;
143
+ :actuator2Command ?A2.
144
+ :why :sensorTrend ?Trend;
145
+ :sensorLow ?Low;
146
+ :sensorHigh ?High;
147
+ :sensorGap ?Gap;
148
+ :normalizedMeasurement ?Norm;
149
+ :feedforwardProportional ?FFProp;
150
+ :disturbanceMagnitude ?Dist;
151
+ :disturbanceCompensation ?FFComp;
152
+ :trackingError ?Err;
153
+ :differentialError ?DiffErr;
154
+ :feedbackProportional ?FBProp;
155
+ :feedbackFactor ?FBFactor;
156
+ :feedbackDifferential ?FBDiff.
157
+ (
158
+ "Control System — ARC explanation of two control signals\n\n"
159
+ "Answer\n"
160
+ ?Answer "\n"
161
+ "Actuator 1 command: " ?A1 "\n"
162
+ "Actuator 2 command: " ?A2 "\n\n"
163
+ "Reason Why\n"
164
+ "The first sensor pair is " ?Low " and " ?High ", so the reading is " ?Trend
165
+ " and the controller normalizes the gap " ?Gap " into " ?Norm ". "
166
+ "That normalized value creates a feedforward term of " ?FFProp
167
+ ", while the known disturbance " ?Dist " contributes a compensation term of " ?FFComp
168
+ ". Subtracting that compensation gives actuator 1 the command " ?A1 ". "
169
+ "For actuator 2, the target is " ?Err " units above the measured output, so the tracking error is positive. "
170
+ "The observed state is " ?DiffErr " relative units below the measured output, so the differential correction is negative. "
171
+ "That yields a proportional feedback part of " ?FBProp ", a nonlinear factor of " ?FBFactor
172
+ ", and a differential contribution of " ?FBDiff ". Together they produce actuator 2 command " ?A2 ".\n\n"
173
+ "Check\n"
174
+ "C1 OK - the first sensor pair is rising, so the normalization uses the rising-branch rule.\n"
175
+ "C2 OK - the normalized measurement is positive and smaller than the raw gap, which is consistent with a square-root normalization.\n"
176
+ "C3 OK - actuator 1 is lower than its proportional feedforward term because disturbance compensation is subtracted.\n"
177
+ "C4 OK - the target is above the measured output, so the tracking error is positive.\n"
178
+ "C5 OK - the observed state is below the measured output, so the differential error is negative.\n"
179
+ "C6 OK - actuator 2 is lower than its pure proportional term because the differential part reduces it.\n"
180
+ "C7 OK - actuator 1 matches an independently reconstructed feedforward calculation.\n"
181
+ "C8 OK - actuator 2 matches an independently reconstructed feedback calculation.\n"
182
+ "C9 OK - both actuator commands stay positive.\n"
183
+ ) string:concatenation ?Block.
184
+ }
185
+ =>
186
+ { :report log:outputString ?Block. }.
187
+
188
+ # ---------------------------------------------------------------------------
189
+ # Independent checks: validate the shape of the reasoning, not just constants
190
+ # ---------------------------------------------------------------------------
191
+
192
+ # The first sensor pair must be rising for this explanation path.
193
+ { :input1 :measurement1 (?Low ?High).
194
+ ?Low math:notLessThan ?High. }
195
+ => false.
196
+
197
+ # Square-root normalization should stay positive and smaller than the raw gap.
198
+ { :why :normalizedMeasurement ?Norm.
199
+ ?Norm math:notGreaterThan 0. }
200
+ => false.
201
+
202
+ { :why :normalizedMeasurement ?Norm;
203
+ :sensorGap ?Gap.
204
+ ?Norm math:notLessThan ?Gap. }
205
+ => false.
206
+
207
+ # Feedforward compensation must reduce the raw proportional response.
208
+ { :actuator1 :control1 ?A1.
209
+ :why :feedforwardProportional ?Prop.
210
+ ?A1 math:notLessThan ?Prop. }
211
+ => false.
212
+
213
+ # The tracking target must exceed the current measurement in this case.
214
+ { :output2 :target2 ?Target.
215
+ :output2 :measurement4 ?Measured.
216
+ ?Target math:notGreaterThan ?Measured. }
217
+ => false.
218
+
219
+ # The observed state must be below the measured output, making the differential error negative.
220
+ { :state3 :observation3 ?Observed.
221
+ :output2 :measurement4 ?Measured.
222
+ ?Observed math:notLessThan ?Measured. }
223
+ => false.
224
+
225
+ { :why :feedbackDifferential ?Diff.
226
+ ?Diff math:notLessThan 0. }
227
+ => false.
228
+
229
+ # The differential part must reduce the pure proportional feedback command.
230
+ { :actuator2 :control1 ?A2.
231
+ :why :feedbackProportional ?Prop.
232
+ ?A2 math:notLessThan ?Prop. }
233
+ => false.
234
+
235
+ # Independent reconstructions must agree with the derived actuator commands.
236
+ { :actuator1 :control1 ?A1.
237
+ :why :feedforwardCommand ?Rebuilt.
238
+ ?A1 math:notEqualTo ?Rebuilt. }
239
+ => false.
240
+
241
+ { :actuator2 :control1 ?A2.
242
+ :why :feedbackCommand ?Rebuilt.
243
+ ?A2 math:notEqualTo ?Rebuilt. }
244
+ => false.
245
+
246
+ # The resulting commands should stay positive.
247
+ { :actuator1 :control1 ?A1.
248
+ ?A1 math:notGreaterThan 0. }
249
+ => false.
77
250
 
251
+ { :actuator2 :control1 ?A2.
252
+ ?A2 math:notGreaterThan 0. }
253
+ => false.
@@ -0,0 +1,409 @@
1
+ # ==============================================================================
2
+ # Delfour — 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 person can share a useful shopping hint without
6
+ # exposing sensitive health details. A phone turns a private condition into a
7
+ # neutral, limited insight such as “prefer lower-sugar products”, attaches clear
8
+ # usage rules and an expiry time, and sends it to a store scanner. The scanner
9
+ # may use that insight to suggest a better product, but not for unrelated
10
+ # purposes such as marketing.
11
+ # ==============================================================================
12
+
13
+ @prefix : <https://example.org/delfour#> .
14
+ @prefix arc: <https://josd.github.io/arc/terms#> .
15
+ @prefix ins: <https://example.org/insight#> .
16
+ @prefix odrl: <http://www.w3.org/ns/odrl/2/> .
17
+ @prefix log: <http://www.w3.org/2000/10/swap/log#> .
18
+ @prefix math: <http://www.w3.org/2000/10/swap/math#> .
19
+ @prefix string: <http://www.w3.org/2000/10/swap/string#> .
20
+ @prefix crypto: <http://www.w3.org/2000/10/swap/crypto#> .
21
+ @prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
22
+
23
+ # -----
24
+ # Facts
25
+ # -----
26
+
27
+ :case
28
+ a arc:Case ;
29
+ :caseName "delfour" ;
30
+ arc: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?" ;
31
+ :expectedFilesWritten 6 ;
32
+ :requestPurpose "shopping_assist" ;
33
+ :requestAction odrl:use ;
34
+ :phoneCreatedAt "2025-10-05T20:33:48.907163+00:00"^^xsd:dateTime ;
35
+ :phoneExpiresAt "2025-10-05T22:33:48.907185+00:00"^^xsd:dateTime ;
36
+ :scannerAuthAt "2025-10-05T20:35:48.907163+00:00"^^xsd:dateTime ;
37
+ :scannerDutyAt "2025-10-05T20:37:48.907163+00:00"^^xsd:dateTime ;
38
+ :filesWritten 6 ;
39
+ :auditEntries 1 .
40
+
41
+ :catalog a :Catalog .
42
+
43
+ :prod_BIS_001
44
+ a :Product ;
45
+ :productId "prod:BIS_001" ;
46
+ :productName "Classic Tea Biscuits" ;
47
+ :sugarTenths 120 ;
48
+ :sugarPerServing 12.0 .
49
+
50
+ :prod_BIS_101
51
+ a :Product ;
52
+ :productId "prod:BIS_101" ;
53
+ :productName "Low-Sugar Tea Biscuits" ;
54
+ :sugarTenths 30 ;
55
+ :sugarPerServing 3.0 .
56
+
57
+ :prod_CHOC_050
58
+ a :Product ;
59
+ :productId "prod:CHOC_050" ;
60
+ :productName "Milk Chocolate Bar" ;
61
+ :sugarTenths 150 ;
62
+ :sugarPerServing 15.0 .
63
+
64
+ :prod_CHOC_150
65
+ a :Product ;
66
+ :productId "prod:CHOC_150" ;
67
+ :productName "85% Dark Chocolate" ;
68
+ :sugarTenths 60 ;
69
+ :sugarPerServing 6.0 .
70
+
71
+ :householdProfile :condition "Diabetes" .
72
+ :scan :scannedProduct :prod_BIS_001 .
73
+
74
+ <https://example.org/insight/delfour>
75
+ a ins:Insight ;
76
+ :metric "sugar_g_per_serving" ;
77
+ :thresholdTenths 100 ;
78
+ :thresholdDisplay "10.0" ;
79
+ :thresholdG 10.0 ;
80
+ :suggestionPolicy "lower_metric_first_higher_price_ok" ;
81
+ :scopeDevice "self-scanner" ;
82
+ :scopeEvent "pick_up_scanner" ;
83
+ :retailer "Delfour" ;
84
+ :createdAt "2025-10-05T20:33:48.907163+00:00"^^xsd:dateTime ;
85
+ :expiresAt "2025-10-05T22:33:48.907185+00:00"^^xsd:dateTime ;
86
+ :serializedLowercase "{\"createdat\":\"2025-10-05t20:33:48.907163+00:00\",\"expiresat\":\"2025-10-05t22:33:48.907185+00:00\",\"id\":\"https://example.org/insight/delfour\",\"metric\":\"sugar_g_per_serving\",\"retailer\":\"delfour\",\"scopedevice\":\"self-scanner\",\"scopeevent\":\"pick_up_scanner\",\"suggestionpolicy\":\"lower_metric_first_higher_price_ok\",\"threshold\":10.0,\"type\":\"ins:insight\"}" .
87
+
88
+ :policy
89
+ a odrl:Policy ;
90
+ :profile "Delfour-Insight-Policy" ;
91
+ odrl:permission [
92
+ odrl:action odrl:use ;
93
+ odrl:target <https://example.org/insight/delfour> ;
94
+ odrl:constraint [
95
+ odrl:leftOperand odrl:purpose ;
96
+ odrl:operator odrl:eq ;
97
+ odrl:rightOperand "shopping_assist"
98
+ ]
99
+ ] ;
100
+ odrl:prohibition [
101
+ odrl:action odrl:distribute ;
102
+ odrl:target <https://example.org/insight/delfour> ;
103
+ odrl:constraint [
104
+ odrl:leftOperand odrl:purpose ;
105
+ odrl:operator odrl:eq ;
106
+ odrl:rightOperand "marketing"
107
+ ]
108
+ ] ;
109
+ odrl:duty [
110
+ odrl:action odrl:delete ;
111
+ odrl:constraint [
112
+ odrl:leftOperand odrl:dateTime ;
113
+ odrl:operator odrl:eq ;
114
+ odrl:rightOperand "2025-10-05T22:33:48.907185+00:00"^^xsd:dateTime
115
+ ]
116
+ ] .
117
+
118
+ :envelope
119
+ :insight <https://example.org/insight/delfour> ;
120
+ :policy :policy ;
121
+ :canonicalJson "{\"insight\":{\"createdAt\":\"2025-10-05T20:33:48.907163+00:00\",\"expiresAt\":\"2025-10-05T22:33:48.907185+00:00\",\"id\":\"https://example.org/insight/delfour\",\"metric\":\"sugar_g_per_serving\",\"retailer\":\"Delfour\",\"scopeDevice\":\"self-scanner\",\"scopeEvent\":\"pick_up_scanner\",\"suggestionPolicy\":\"lower_metric_first_higher_price_ok\",\"threshold\":10.0,\"type\":\"ins:Insight\"},\"policy\":{\"duty\":{\"action\":\"odrl:delete\",\"constraint\":{\"leftOperand\":\"odrl:dateTime\",\"operator\":\"odrl:eq\",\"rightOperand\":\"2025-10-05T22:33:48.907185+00:00\"}},\"permission\":{\"action\":\"odrl:use\",\"constraint\":{\"leftOperand\":\"odrl:purpose\",\"operator\":\"odrl:eq\",\"rightOperand\":\"shopping_assist\"},\"target\":\"https://example.org/insight/delfour\"},\"profile\":\"Delfour-Insight-Policy\",\"prohibition\":{\"action\":\"odrl:distribute\",\"constraint\":{\"leftOperand\":\"odrl:purpose\",\"operator\":\"odrl:eq\",\"rightOperand\":\"marketing\"},\"target\":\"https://example.org/insight/delfour\"},\"type\":\"odrl:Policy\"}}" .
122
+
123
+ :signature
124
+ :alg "HMAC-SHA256" ;
125
+ :keyid "demo-shared-secret" ;
126
+ :created "2025-10-05T20:33:48.907163+00:00"^^xsd:dateTime ;
127
+ :payloadHashSHA256 "34ad35638dfd7c67d031eeca8abb235ec24280740f863f3f31cd9d7b6517f098" ;
128
+ :signatureHMAC "b21d0072d90112a9f820aced0286889f4b6ef92b145e6fdef1011f3bfa4608c2" ;
129
+ :hmacVerificationMode :trustedPrecomputedInput .
130
+
131
+ :reasonText
132
+ :value "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.\n" .
133
+
134
+ # -----
135
+ # Logic
136
+ # -----
137
+
138
+ # desensitize(&["Diabetes"]) -> true
139
+ { :householdProfile :condition "Diabetes" . }
140
+ => { :case :needsLowSugar true . } .
141
+
142
+ # derive_insight(...)
143
+ { :case :needsLowSugar true . }
144
+ => { <https://example.org/insight/delfour> :derivedFromNeed "low_sugar" . } .
145
+
146
+ # payload_hash_matches
147
+ {
148
+ :envelope :canonicalJson ?json .
149
+ ?json crypto:sha256 ?digest .
150
+ :signature :payloadHashSHA256 ?digest .
151
+ } => {
152
+ :check :payloadHashMatches true .
153
+ } .
154
+
155
+ # signature_verified
156
+ {
157
+ :signature :hmacVerificationMode :trustedPrecomputedInput .
158
+ } => {
159
+ :check :signatureVerifies true .
160
+ } .
161
+
162
+ # minimization_no_sensitive_terms
163
+ {
164
+ <https://example.org/insight/delfour> :serializedLowercase ?s .
165
+ ?s string:notMatches "diabetes|medical" .
166
+ } => {
167
+ :check :minimizationStripsSensitiveTerms true .
168
+ } .
169
+
170
+ # scope_complete
171
+ {
172
+ <https://example.org/insight/delfour> :scopeDevice ?device ;
173
+ :scopeEvent ?event ;
174
+ :expiresAt ?expiry .
175
+ } => {
176
+ :check :scopeComplete true .
177
+ } .
178
+
179
+ # authorization_allowed
180
+ {
181
+ :policy odrl:permission [
182
+ odrl:action odrl:use ;
183
+ odrl:target <https://example.org/insight/delfour> ;
184
+ odrl:constraint [
185
+ odrl:leftOperand odrl:purpose ;
186
+ odrl:operator odrl:eq ;
187
+ odrl:rightOperand "shopping_assist"
188
+ ]
189
+ ] .
190
+ :case :scannerAuthAt ?authAt .
191
+ <https://example.org/insight/delfour> :expiresAt ?expiresAt .
192
+ ?authAt math:notGreaterThan ?expiresAt .
193
+ } => {
194
+ :decision
195
+ :at "2025-10-05T20:35:48.907163+00:00"^^xsd:dateTime ;
196
+ :outcome "Allowed" ;
197
+ :target <https://example.org/insight/delfour> .
198
+ :check :authorizationAllowed true .
199
+ } .
200
+
201
+ # banner if scanned product meets/exceeds threshold
202
+ {
203
+ :decision :outcome "Allowed" .
204
+ :scan :scannedProduct ?product .
205
+ ?product :sugarPerServing ?sugar .
206
+ <https://example.org/insight/delfour> :thresholdG ?threshold .
207
+ ?sugar math:notLessThan ?threshold .
208
+ } => {
209
+ :banner
210
+ :headline "Track sugar per serving while you scan" ;
211
+ :note "High sugar" .
212
+ :check :bannerFlagsHighSugar true .
213
+ } .
214
+
215
+ # choose the lower-sugar alternative with the smallest sugar score
216
+ {
217
+ :scan :scannedProduct ?scanned .
218
+ ?scanned :sugarTenths ?scannedSugar .
219
+ ?candidate a :Product ;
220
+ :sugarTenths ?candidateSugar .
221
+ ?scannedSugar math:greaterThan ?candidateSugar .
222
+ 1 log:notIncludes {
223
+ ?other a :Product ;
224
+ :sugarTenths ?otherSugar .
225
+ ?scannedSugar math:greaterThan ?otherSugar .
226
+ ?otherSugar math:lessThan ?candidateSugar .
227
+ } .
228
+ } => {
229
+ :case :suggestedAlternative ?candidate .
230
+ } .
231
+
232
+ {
233
+ :banner :note "High sugar" .
234
+ :case :suggestedAlternative ?alt .
235
+ ?alt :productName ?altName .
236
+ } => {
237
+ :banner :suggestedAlternative ?altName .
238
+ } .
239
+
240
+ {
241
+ :scan :scannedProduct ?scanned .
242
+ ?scanned :sugarTenths ?scannedSugar .
243
+ :case :suggestedAlternative ?alt .
244
+ ?alt :sugarTenths ?altSugar .
245
+ ?scannedSugar math:greaterThan ?altSugar .
246
+ } => {
247
+ :check :alternativeIsLowerSugar true .
248
+ } .
249
+
250
+ # duty_timing_consistent
251
+ {
252
+ :case :scannerDutyAt ?dutyAt .
253
+ <https://example.org/insight/delfour> :expiresAt ?expiresAt .
254
+ ?dutyAt math:notGreaterThan ?expiresAt .
255
+ } => {
256
+ :check :dutyTimingConsistent true .
257
+ } .
258
+
259
+ # marketing_prohibited
260
+ {
261
+ :policy odrl:prohibition [
262
+ odrl:action odrl:distribute ;
263
+ odrl:constraint [
264
+ odrl:rightOperand "marketing"
265
+ ]
266
+ ] .
267
+ } => {
268
+ :check :marketingProhibited true .
269
+ } .
270
+
271
+ # file count check mirrors EXPECTED_FILES_WRITTEN
272
+ {
273
+ :case :filesWritten 6 .
274
+ } => {
275
+ :check :filesWrittenExpected true .
276
+ } .
277
+
278
+ {
279
+ :check :signatureVerifies true ;
280
+ :payloadHashMatches true ;
281
+ :minimizationStripsSensitiveTerms true ;
282
+ :scopeComplete true ;
283
+ :authorizationAllowed true ;
284
+ :bannerFlagsHighSugar true ;
285
+ :alternativeIsLowerSugar true ;
286
+ :dutyTimingConsistent true ;
287
+ :marketingProhibited true ;
288
+ :filesWrittenExpected true .
289
+ } => {
290
+ :result :allChecksPass true .
291
+ } .
292
+
293
+ # -------------------------------------
294
+ # Hard checks (Eyeling inference fuses)
295
+ # -------------------------------------
296
+
297
+ {
298
+ :case :filesWritten ?n .
299
+ ?n math:notEqualTo 6 .
300
+ } => false .
301
+
302
+ {
303
+ :case :scannerAuthAt ?authAt .
304
+ <https://example.org/insight/delfour> :expiresAt ?expiresAt .
305
+ ?authAt math:greaterThan ?expiresAt .
306
+ } => false .
307
+
308
+ {
309
+ :scan :scannedProduct ?scanned .
310
+ ?scanned :sugarTenths ?scannedSugar .
311
+ :case :suggestedAlternative ?alt .
312
+ ?alt :sugarTenths ?altSugar .
313
+ ?altSugar math:notLessThan ?scannedSugar .
314
+ } => false .
315
+
316
+ {
317
+ :policy odrl:prohibition [
318
+ odrl:action ?action
319
+ ] .
320
+ ?action log:notEqualTo odrl:distribute .
321
+ } => false .
322
+
323
+ {
324
+ :envelope :canonicalJson ?json .
325
+ ?json crypto:sha256 ?actual .
326
+ :signature :payloadHashSHA256 ?expected .
327
+ ?actual log:notEqualTo ?expected .
328
+ } => false .
329
+
330
+ # --------------------------------------
331
+ # ARC rendering through log:outputString
332
+ # --------------------------------------
333
+
334
+ :out01 log:outputString "=== Answer ===\n" .
335
+ :out02 log:outputString "The scanner is allowed to use a neutral shopping insight and recommends Low-Sugar Tea Biscuits instead of Classic Tea Biscuits.\n" .
336
+ :out03 log:outputString "case : delfour\n" .
337
+ :out04 log:outputString "decision : Allowed\n" .
338
+ :out05 log:outputString "scanned product : Classic Tea Biscuits\n" .
339
+
340
+ {
341
+ :case :suggestedAlternative ?alt .
342
+ ?alt :productName ?altName .
343
+ ("suggested alternative: %s\n" ?altName) string:format ?line .
344
+ } => {
345
+ :out06 log:outputString ?line .
346
+ } .
347
+
348
+ :out07 log:outputString "\n=== Reason Why ===\n" .
349
+ :out08 log:outputString "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.\n" .
350
+ :out09 log:outputString "metric : sugar_g_per_serving\n" .
351
+
352
+ {
353
+ <https://example.org/insight/delfour> :thresholdDisplay ?threshold .
354
+ ("threshold : %s\n" ?threshold) string:format ?line .
355
+ } => {
356
+ :out10 log:outputString ?line .
357
+ } .
358
+
359
+ :out11 log:outputString "scope : self-scanner @ pick_up_scanner\n" .
360
+ :out12 log:outputString "retailer : Delfour\n" .
361
+
362
+ {
363
+ :signature :alg ?alg .
364
+ ("signature alg : %s\n" ?alg) string:format ?line .
365
+ } => {
366
+ :out13 log:outputString ?line .
367
+ } .
368
+
369
+ {
370
+ :banner :headline ?headline .
371
+ ("banner headline : %s\n" ?headline) string:format ?line .
372
+ } => {
373
+ :out14 log:outputString ?line .
374
+ } .
375
+
376
+ :out15 log:outputString "expires at : 2025-10-05T22:33:48.907185+00:00\n" .
377
+
378
+ {
379
+ :reasonText :value ?reason .
380
+ ("reason.txt : %s" ?reason) string:format ?line .
381
+ } => {
382
+ :out16 log:outputString ?line .
383
+ } .
384
+
385
+ {
386
+ :case :auditEntries ?n .
387
+ ("audit entries : %s\n" ?n) string:format ?line .
388
+ } => {
389
+ :out17 log:outputString ?line .
390
+ } .
391
+
392
+ {
393
+ :case :filesWritten ?n .
394
+ ("bus files written : %s\n" ?n) string:format ?line .
395
+ } => {
396
+ :out18 log:outputString ?line .
397
+ } .
398
+
399
+ :out19 log:outputString "\n=== Check ===\n" .
400
+
401
+ { :check :signatureVerifies true . } => { :out20 log:outputString "signature verifies : yes\n" . } .
402
+ { :check :payloadHashMatches true . } => { :out21 log:outputString "payload hash matches : yes\n" . } .
403
+ { :check :minimizationStripsSensitiveTerms true . } => { :out22 log:outputString "minimization strips sensitive terms: yes\n" . } .
404
+ { :check :scopeComplete true . } => { :out23 log:outputString "scope complete : yes\n" . } .
405
+ { :check :authorizationAllowed true . } => { :out24 log:outputString "authorization allowed : yes\n" . } .
406
+ { :check :bannerFlagsHighSugar true . } => { :out25 log:outputString "high-sugar banner : yes\n" . } .
407
+ { :check :alternativeIsLowerSugar true . } => { :out26 log:outputString "alternative lowers sugar : yes\n" . } .
408
+ { :check :dutyTimingConsistent true . } => { :out27 log:outputString "duty timing consistent : yes\n" . } .
409
+ { :check :marketingProhibited true . } => { :out28 log:outputString "marketing prohibited : yes\n" . } .