eyeling 1.24.0 → 1.24.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.
Files changed (165) hide show
  1. package/HANDBOOK.md +98 -0
  2. package/README.md +4 -4
  3. package/dist/browser/eyeling.browser.js +103 -0
  4. package/eyeling.js +103 -0
  5. package/lib/lexer.js +103 -0
  6. package/package.json +1 -1
  7. package/see/README.md +13 -11
  8. package/see/examples/_see.js +33 -2
  9. package/see/examples/age.js +37 -11
  10. package/see/examples/annotation.js +37 -11
  11. package/see/examples/backward.js +37 -11
  12. package/see/examples/backward_recursion.js +37 -11
  13. package/see/examples/bayes_diagnosis.js +37 -11
  14. package/see/examples/bayes_therapy.js +37 -11
  15. package/see/examples/bmi.js +37 -11
  16. package/see/examples/builtin_coverage.js +37 -11
  17. package/see/examples/collection.js +37 -11
  18. package/see/examples/complex.js +37 -11
  19. package/see/examples/complex_matrix_stability.js +37 -11
  20. package/see/examples/composition_of_injective_functions_is_injective.js +37 -11
  21. package/see/examples/control_system.js +37 -11
  22. package/see/examples/crypto_builtins_tests.js +37 -11
  23. package/see/examples/delfour.js +37 -11
  24. package/see/examples/digital_product_passport.js +37 -11
  25. package/see/examples/dijkstra.js +37 -11
  26. package/see/examples/dijkstra_risk_path.js +37 -11
  27. package/see/examples/doc/age.md +1 -1
  28. package/see/examples/doc/annotation.md +1 -1
  29. package/see/examples/doc/backward.md +1 -1
  30. package/see/examples/doc/backward_recursion.md +1 -1
  31. package/see/examples/doc/bayes_diagnosis.md +1 -1
  32. package/see/examples/doc/bayes_therapy.md +1 -1
  33. package/see/examples/doc/bmi.md +1 -1
  34. package/see/examples/doc/builtin_coverage.md +1 -1
  35. package/see/examples/doc/collection.md +1 -1
  36. package/see/examples/doc/complex.md +1 -1
  37. package/see/examples/doc/complex_matrix_stability.md +1 -1
  38. package/see/examples/doc/composition_of_injective_functions_is_injective.md +1 -1
  39. package/see/examples/doc/control_system.md +1 -1
  40. package/see/examples/doc/crypto_builtins_tests.md +1 -1
  41. package/see/examples/doc/delfour.md +1 -1
  42. package/see/examples/doc/digital_product_passport.md +1 -1
  43. package/see/examples/doc/dijkstra.md +1 -1
  44. package/see/examples/doc/dijkstra_risk_path.md +1 -1
  45. package/see/examples/doc/dog.md +1 -1
  46. package/see/examples/doc/eco_route_insight.md +1 -1
  47. package/see/examples/doc/equals.md +1 -1
  48. package/see/examples/doc/equivalence_classes_overlap_implies_same_class.md +1 -1
  49. package/see/examples/doc/euler_identity.md +1 -1
  50. package/see/examples/doc/ev_roundtrip_planner.md +1 -1
  51. package/see/examples/doc/existential_rule.md +1 -1
  52. package/see/examples/doc/expression_eval.md +1 -1
  53. package/see/examples/doc/family_cousins.md +1 -1
  54. package/see/examples/doc/fastpow.md +1 -1
  55. package/see/examples/doc/fibonacci.md +1 -1
  56. package/see/examples/doc/french_cities.md +1 -1
  57. package/see/examples/doc/fundamental_theorem_arithmetic.md +1 -1
  58. package/see/examples/doc/genetic_knapsack_selection.md +1 -1
  59. package/see/examples/doc/goldbach_1000.md +1 -1
  60. package/see/examples/doc/good_cobbler.md +1 -1
  61. package/see/examples/doc/gps.md +1 -1
  62. package/see/examples/doc/gray_code_counter.md +1 -1
  63. package/see/examples/doc/greatest_lower_bound_uniqueness.md +1 -1
  64. package/see/examples/doc/group_inverse_uniqueness.md +1 -1
  65. package/see/examples/doc/hadamard_approx.md +1 -1
  66. package/see/examples/doc/hanoi.md +1 -1
  67. package/see/examples/doc/odrl_dpv_risk_ranked.md +1 -1
  68. package/see/examples/doc/path_discovery.md +1 -1
  69. package/see/examples/doc/rc_discharge_envelope.md +1 -1
  70. package/see/examples/doc/rdf_message_flow.md +1 -1
  71. package/see/examples/doc/rdf_messages.md +1 -1
  72. package/see/examples/doc/school_placement_audit.md +1 -1
  73. package/see/examples/doc/smoke_arithmetic.md +1 -1
  74. package/see/examples/doc/socrates.md +1 -1
  75. package/see/examples/doc/triple_terms.md +26 -0
  76. package/see/examples/doc/wind_turbine.md +1 -1
  77. package/see/examples/doc/witch.md +1 -1
  78. package/see/examples/dog.js +37 -11
  79. package/see/examples/eco_route_insight.js +37 -11
  80. package/see/examples/equals.js +37 -11
  81. package/see/examples/equivalence_classes_overlap_implies_same_class.js +37 -11
  82. package/see/examples/euler_identity.js +37 -11
  83. package/see/examples/ev_roundtrip_planner.js +37 -11
  84. package/see/examples/existential_rule.js +37 -11
  85. package/see/examples/expression_eval.js +37 -11
  86. package/see/examples/family_cousins.js +37 -11
  87. package/see/examples/fastpow.js +37 -11
  88. package/see/examples/fibonacci.js +37 -11
  89. package/see/examples/french_cities.js +37 -11
  90. package/see/examples/fundamental_theorem_arithmetic.js +37 -11
  91. package/see/examples/genetic_knapsack_selection.js +37 -11
  92. package/see/examples/goldbach_1000.js +37 -11
  93. package/see/examples/good_cobbler.js +37 -11
  94. package/see/examples/gps.js +37 -11
  95. package/see/examples/gray_code_counter.js +37 -11
  96. package/see/examples/greatest_lower_bound_uniqueness.js +37 -11
  97. package/see/examples/group_inverse_uniqueness.js +37 -11
  98. package/see/examples/hadamard_approx.js +37 -11
  99. package/see/examples/hanoi.js +37 -11
  100. package/see/examples/input/triple_terms.trig +28 -0
  101. package/see/examples/n3/triple_terms.n3 +23 -0
  102. package/see/examples/odrl_dpv_risk_ranked.js +37 -11
  103. package/see/examples/output/age.md +3 -3
  104. package/see/examples/output/annotation.md +4 -4
  105. package/see/examples/output/backward.md +3 -3
  106. package/see/examples/output/backward_recursion.md +3 -3
  107. package/see/examples/output/bayes_diagnosis.md +1 -1
  108. package/see/examples/output/bayes_therapy.md +1 -1
  109. package/see/examples/output/bmi.md +1 -1
  110. package/see/examples/output/builtin_coverage.md +3 -3
  111. package/see/examples/output/collection.md +3 -3
  112. package/see/examples/output/complex.md +4 -4
  113. package/see/examples/output/complex_matrix_stability.md +1 -1
  114. package/see/examples/output/composition_of_injective_functions_is_injective.md +3 -3
  115. package/see/examples/output/control_system.md +3 -3
  116. package/see/examples/output/crypto_builtins_tests.md +3 -3
  117. package/see/examples/output/delfour.md +1 -1
  118. package/see/examples/output/digital_product_passport.md +1 -1
  119. package/see/examples/output/dijkstra.md +3 -3
  120. package/see/examples/output/dijkstra_risk_path.md +1 -1
  121. package/see/examples/output/dog.md +3 -3
  122. package/see/examples/output/eco_route_insight.md +1 -1
  123. package/see/examples/output/equals.md +3 -3
  124. package/see/examples/output/equivalence_classes_overlap_implies_same_class.md +3 -3
  125. package/see/examples/output/euler_identity.md +3 -3
  126. package/see/examples/output/ev_roundtrip_planner.md +1 -1
  127. package/see/examples/output/existential_rule.md +3 -3
  128. package/see/examples/output/expression_eval.md +3 -3
  129. package/see/examples/output/family_cousins.md +3 -3
  130. package/see/examples/output/fastpow.md +1 -1
  131. package/see/examples/output/fibonacci.md +1 -1
  132. package/see/examples/output/french_cities.md +3 -3
  133. package/see/examples/output/fundamental_theorem_arithmetic.md +1 -1
  134. package/see/examples/output/genetic_knapsack_selection.md +1 -1
  135. package/see/examples/output/goldbach_1000.md +1 -1
  136. package/see/examples/output/good_cobbler.md +4 -4
  137. package/see/examples/output/gps.md +1 -1
  138. package/see/examples/output/gray_code_counter.md +1 -1
  139. package/see/examples/output/greatest_lower_bound_uniqueness.md +3 -3
  140. package/see/examples/output/group_inverse_uniqueness.md +3 -3
  141. package/see/examples/output/hadamard_approx.md +3 -3
  142. package/see/examples/output/hanoi.md +3 -3
  143. package/see/examples/output/odrl_dpv_risk_ranked.md +3 -3
  144. package/see/examples/output/path_discovery.md +3 -3
  145. package/see/examples/output/rc_discharge_envelope.md +1 -1
  146. package/see/examples/output/rdf_message_flow.md +1 -1
  147. package/see/examples/output/rdf_messages.md +1 -1
  148. package/see/examples/output/school_placement_audit.md +1 -1
  149. package/see/examples/output/smoke_arithmetic.md +1 -1
  150. package/see/examples/output/socrates.md +3 -3
  151. package/see/examples/output/triple_terms.md +53 -0
  152. package/see/examples/output/wind_turbine.md +1 -1
  153. package/see/examples/output/witch.md +3 -3
  154. package/see/examples/path_discovery.js +37 -11
  155. package/see/examples/rc_discharge_envelope.js +37 -11
  156. package/see/examples/rdf_message_flow.js +37 -11
  157. package/see/examples/rdf_messages.js +37 -11
  158. package/see/examples/school_placement_audit.js +37 -11
  159. package/see/examples/smoke_arithmetic.js +37 -11
  160. package/see/examples/socrates.js +37 -11
  161. package/see/examples/triple_terms.js +1442 -0
  162. package/see/examples/wind_turbine.js +37 -11
  163. package/see/examples/witch.js +37 -11
  164. package/see/see.js +455 -94
  165. package/test/api.test.js +20 -0
@@ -1,10 +1,10 @@
1
1
  # In a partial order, a greatest lower bound is unique.
2
2
 
3
- ## Insight
3
+ ## Entailment
4
4
  The compiled query selected 2 fact(s) after the rule closure was computed.
5
- Main conclusion: **:result :sameGreatestLowerBound (:a :b :g2 :g1).**
5
+ Main entailment: **:result :sameGreatestLowerBound (:a :b :g2 :g1).**
6
6
 
7
- Selected conclusions:
7
+ Selected entailments:
8
8
  - :result :sameGreatestLowerBound (:a :b :g2 :g1) .
9
9
  - :result :sameGreatestLowerBound (:a :b :g1 :g2) .
10
10
 
@@ -1,10 +1,10 @@
1
1
  # In a group, the inverse of an element is unique.
2
2
 
3
- ## Insight
3
+ ## Entailment
4
4
  The compiled query selected 2 fact(s) after the rule closure was computed.
5
- Main conclusion: **:result :sameInverse (:x :j :i).**
5
+ Main entailment: **:result :sameInverse (:x :j :i).**
6
6
 
7
- Selected conclusions:
7
+ Selected entailments:
8
8
  - :result :sameInverse (:x :j :i) .
9
9
  - :result :sameInverse (:x :i :j) .
10
10
 
@@ -1,10 +1,10 @@
1
1
  # Hadamard gate approximation
2
2
 
3
- ## Insight
3
+ ## Entailment
4
4
  The compiled query selected 441 fact(s) after the rule closure was computed.
5
- Main conclusion: **:result :HMidpointEntry (20 1 1 -0.707106733683605 0).**
5
+ Main entailment: **:result :HMidpointEntry (20 1 1 -0.707106733683605 0).**
6
6
 
7
- Selected conclusions:
7
+ Selected entailments:
8
8
  - :result :HMidpointEntry (20 1 1 -0.707106733683605 0) .
9
9
  - :result :HMidpointEntry (20 1 0 0.707106733683605 0) .
10
10
  - :result :HMidpointEntry (20 0 1 0.707106733683605 0) .
@@ -1,10 +1,10 @@
1
1
  # Towers of Hanoi
2
2
 
3
- ## Insight
3
+ ## Entailment
4
4
  The derivation produced 1 new fact(s) from 0 stated fact(s).
5
- Main conclusion: **3 :answer ((:left :right) (:left :center) (:right :center) (:left :right) (:center :left) (:center :right) (:left :right)).**
5
+ Main entailment: **3 :answer ((:left :right) (:left :center) (:right :center) (:left :right) (:center :left) (:center :right) (:left :right)).**
6
6
 
7
- Selected conclusions:
7
+ Selected entailments:
8
8
  - 3 :answer ((:left :right) (:left :center) (:right :center) (:left :right) (:center :left) (:center :right) (:left :right)) .
9
9
 
10
10
  ## Explanation
@@ -1,10 +1,10 @@
1
1
  # ODRL + DPV risk assessment with ranked, explainable output.
2
2
 
3
- ## Insight
3
+ ## Entailment
4
4
  The compiled query selected 65 fact(s) after the rule closure was computed.
5
- Main conclusion: **(:Agreement1 :ConsumerExample 1 930 "C4" 1 _:risk4 _:m14) log:outputString " - mitigation for clause C4: Add a permission allowing data export (or remove the prohibition) to support portability.\n".**
5
+ Main entailment: **(:Agreement1 :ConsumerExample 1 930 "C4" 1 _:risk4 _:m14) log:outputString " - mitigation for clause C4: Add a permission allowing data export (or remove the prohibition) to support portability.\n".**
6
6
 
7
- Selected conclusions:
7
+ Selected entailments:
8
8
  - (:Agreement1 :ConsumerExample 1 930 "C4" 1 _:risk4 _:m14) log:outputString " - mitigation for clause C4: Add a permission allowing data export (or remove the prohibition) to support portability.\n" .
9
9
  - (:Agreement1 :ConsumerExample 1 930 "C3" 1 _:risk4 _:m14) log:outputString " - mitigation for clause C3: Add a permission allowing data export (or remove the prohibition) to support portability.\n" .
10
10
  - (:Agreement1 :ConsumerExample 1 930 "C2" 1 _:risk4 _:m14) log:outputString " - mitigation for clause C2: Add a permission allowing data export (or remove the prohibition) to support portability.\n" .
@@ -1,10 +1,10 @@
1
1
  # Path Discovery
2
2
 
3
- ## Insight
3
+ ## Entailment
4
4
  The derivation produced 3 new fact(s) from 96420 stated fact(s).
5
- Main conclusion: **:discovered :airroute ("Ostend-Bruges International Airport" "Liège Airport" "Palma De Mallorca Airport" "Václav Havel Airport Prague").**
5
+ Main entailment: **:discovered :airroute ("Ostend-Bruges International Airport" "Liège Airport" "Palma De Mallorca Airport" "Václav Havel Airport Prague").**
6
6
 
7
- Selected conclusions:
7
+ Selected entailments:
8
8
  - :discovered :airroute ("Ostend-Bruges International Airport" "Liège Airport" "Palma De Mallorca Airport" "Václav Havel Airport Prague") .
9
9
  - :discovered :airroute ("Ostend-Bruges International Airport" "Liège Airport" "Diagoras Airport" "Václav Havel Airport Prague") .
10
10
  - :discovered :airroute ("Ostend-Bruges International Airport" "Liège Airport" "Heraklion International Nikos Kazantzakis Airport" "Václav Havel Airport Prague") .
@@ -1,6 +1,6 @@
1
1
  # RC Discharge Envelope
2
2
 
3
- ## Insight
3
+ ## Entailment
4
4
  exact decay symbol : exp(-1/4)
5
5
  certified decay interval : [0.7788007830, 0.7788007831]
6
6
  first below tolerance step : 13
@@ -1,6 +1,6 @@
1
1
  # RDF Message Flow
2
2
 
3
- ## Insight
3
+ ## Entailment
4
4
  Continuous RDF Message flow accepted: 5 ordered messages moved through the ingest → validate → interpret → route → sink pipeline. The threshold was 26, so results 21 and 22 were archived, the heartbeat kept the stream alive, and results 28 and 29 were emitted as alerts.
5
5
 
6
6
  ## Explanation
@@ -1,6 +1,6 @@
1
1
  # RDF Messages
2
2
 
3
- ## Insight
3
+ ## Entailment
4
4
  RDF Message log accepted: 3 explicit message boundaries are preserved. Message :m002 is an empty heartbeat, and the local blank-node label _:b0 is safely reused in separate messages.
5
5
 
6
6
  ## Explanation
@@ -1,6 +1,6 @@
1
1
  # School Placement Route Audit
2
2
 
3
- ## Insight
3
+ ## Entailment
4
4
  audit result : fail
5
5
  children affected by straight-line rule : Ada, Björn, Davi
6
6
  largest hidden detour : Ada, 3000 m
@@ -1,6 +1,6 @@
1
1
  # Smoke Arithmetic
2
2
 
3
- ## Insight
3
+ ## Entailment
4
4
  product = 42
5
5
 
6
6
  ## Explanation
@@ -1,10 +1,10 @@
1
1
  # Socrates inference
2
2
 
3
- ## Insight
3
+ ## Entailment
4
4
  The compiled query selected 2 fact(s) after the rule closure was computed.
5
- Main conclusion: **:Socrates is a :Mortal.**
5
+ Main entailment: **:Socrates is a :Mortal.**
6
6
 
7
- Selected conclusions:
7
+ Selected entailments:
8
8
  - :Socrates rdf:type :Mortal .
9
9
  - :Socrates rdf:type :Human .
10
10
 
@@ -0,0 +1,53 @@
1
+ # Triple terms
2
+
3
+ ## Entailment
4
+ The compiled query selected 1 fact(s) after the rule closure was computed.
5
+ Main entailment: **:observation :entails <<( :sensor :needs :inspection )>>.**
6
+
7
+ Selected entailments:
8
+ - :observation :entails <<( :sensor :needs :inspection )>> .
9
+
10
+ ## Explanation
11
+ Starts with 2 source fact(s), applies 1 rule(s), and reaches a fixpoint.
12
+ The log:query projection then keeps only the matching fact(s) shown above.
13
+
14
+ Derivation steps:
15
+ - Rule 1: ?observation rdf:reifies <<( ?device :reports ?condition )>>; ?condition :requires ?action => ?observation :entails <<( ?device :needs ?action )>> derives :observation :entails <<( :sensor :needs :inspection )>> .
16
+ - Uses: :observation rdf:reifies <<( :sensor :reports :overheating )>> . _(source)_; :overheating :requires :inspection . _(source)_
17
+
18
+ Selected explanation support:
19
+ - :observation :entails <<( :sensor :needs :inspection )>> . _(derived by Rule 1)_
20
+ - :observation rdf:reifies <<( :sensor :reports :overheating )>> . _(source)_
21
+ - :overheating :requires :inspection . _(source)_
22
+
23
+ The query-selected facts are serialized in the Formal TriG Output section.
24
+
25
+ ## Formal TriG Output
26
+
27
+ ```trig
28
+ VERSION "1.2"
29
+
30
+ @prefix : <https://eyereasoner.github.io/eyeling/see/examples/triple_terms#> .
31
+ @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
32
+ @prefix log: <http://www.w3.org/2000/10/swap/log#> .
33
+ @prefix see: <https://example.org/see#> .
34
+ @prefix in: <https://example.org/see/input#> .
35
+
36
+ :observation :entails <<( :sensor :needs :inspection )>> .
37
+
38
+ in:metadata {
39
+ in:run a see:InputDataset .
40
+ in:run see:name "triple_terms" .
41
+ in:run see:title "Triple terms" .
42
+ in:run see:sourceFile "examples/n3/triple_terms.n3" .
43
+ in:run see:sourceSHA256 "f94fae1b4087f790c085c4dd1570875694238a882213951b457480ee26c06944" .
44
+ in:run see:description "Demonstrates RDF 1.2 TriG triple terms as input evidence and as a derived entailment." .
45
+ in:run see:compiler "see.js N3-to-JS compiler" .
46
+ in:run see:inputFacts 2 .
47
+ in:run see:compiledRules 1 .
48
+ in:run see:compiledBackwardRules 0 .
49
+ in:run see:compiledFuses 0 .
50
+ in:run see:compiledQueries 1 .
51
+ }
52
+ ```
53
+
@@ -1,6 +1,6 @@
1
1
  # Wind Turbine Envelope
2
2
 
3
- ## Insight
3
+ ## Entailment
4
4
  operating thresholds : cut-in 3.5 m/s, rated 12.0 m/s, cut-out 25.0 m/s
5
5
  rated power : 3.2 MW
6
6
  interval classifications : t1 3.0 m/s stopped 0.000 MW; t2 6.5 m/s partial 0.440 MW; t3 11.2 m/s partial 2.586 MW; t4 15.0 m/s rated 3.200 MW; t5 24.5 m/s rated 3.200 MW; t6 27.0 m/s stopped 0.000 MW
@@ -1,10 +1,10 @@
1
1
  # Burn the witch
2
2
 
3
- ## Insight
3
+ ## Entailment
4
4
  The derivation produced 6 new fact(s) from 3 stated fact(s).
5
- Main conclusion: **:GIRL is a :WITCH.**
5
+ Main entailment: **:GIRL is a :WITCH.**
6
6
 
7
- Selected conclusions:
7
+ Selected entailments:
8
8
  - :GIRL rdf:type :WITCH .
9
9
  - :GIRL rdf:type :BURNS .
10
10
  - :GIRL rdf:type :ISMADEOFWOOD .
@@ -8,6 +8,7 @@ const crypto = require('crypto');
8
8
 
9
9
  function canonical(term) {
10
10
  if (term.kind === 'list') return ['list', term.items.map(canonical)];
11
+ if (term.kind === 'triple') return ['triple', canonical(term.s), canonical(term.p), canonical(term.o)];
11
12
  if (term.kind === 'formula') return ['formula', term.atoms.map((a) => [canonical(a.s), canonical(a.p), canonical(a.o)])];
12
13
  return [term.kind, term.value];
13
14
  }
@@ -17,6 +18,7 @@ function compoundIndexKey() { return Array.from(arguments).map(termIndexKey).joi
17
18
  function termIsConcrete(t) {
18
19
  if (!t || t.kind === 'var') return false;
19
20
  if (t.kind === 'list') return t.items.every(termIsConcrete);
21
+ if (t.kind === 'triple') return termIsConcrete(t.s) && termIsConcrete(t.p) && termIsConcrete(t.o);
20
22
  if (t.kind === 'formula') return t.atoms.every((a) => termIsConcrete(a.s) && termIsConcrete(a.p) && termIsConcrete(a.o));
21
23
  return true;
22
24
  }
@@ -32,6 +34,7 @@ function primitive(t) {
32
34
  if (t.kind === 'iri') return t.value.replace(/^:/, '');
33
35
  if (t.kind === 'blank') return t.value;
34
36
  if (t.kind === 'list') return t.items.map(primitive);
37
+ if (t.kind === 'triple') return termToN3(t);
35
38
  if (t.kind === 'formula') return termToN3(t);
36
39
  return undefined;
37
40
  }
@@ -52,6 +55,7 @@ function termToN3(t) {
52
55
  if (t.kind === 'var') return '?' + t.value;
53
56
  if (t.kind === 'blank') return t.value.startsWith('_:') ? t.value : '_:' + t.value.replace(/^_+/, '');
54
57
  if (t.kind === 'list') return '(' + t.items.map(termToN3).join(' ') + ')';
58
+ if (t.kind === 'triple') return '<<( ' + termToN3(t.s) + ' ' + termToN3(t.p) + ' ' + termToN3(t.o) + ' )>>';
55
59
  if (t.kind === 'formula') return '{ ' + t.atoms.map(atomToN3).join(' . ') + ' }';
56
60
  return String(t.value ?? t);
57
61
  }
@@ -74,6 +78,7 @@ function resolve(term, env, seen = new Set()) {
74
78
  return resolve(env[term.value], env, seen);
75
79
  }
76
80
  if (term.kind === 'list') return list(term.items.map((item) => resolve(item, env, seen)));
81
+ if (term.kind === 'triple') return { kind: 'triple', s: resolve(term.s, env), p: resolve(term.p, env), o: resolve(term.o, env) };
77
82
  if (term.kind === 'formula') return { kind: 'formula', atoms: term.atoms.map((a) => ({ s: resolve(a.s, env), p: resolve(a.p, env), o: resolve(a.o, env) })) };
78
83
  return term;
79
84
  }
@@ -91,6 +96,14 @@ function unify(a, b, env) {
91
96
  }
92
97
  return out;
93
98
  }
99
+ if (a.kind === 'triple' || b.kind === 'triple') {
100
+ if (a.kind !== 'triple' || b.kind !== 'triple') return null;
101
+ let out = unify(a.s, b.s, env);
102
+ if (!out) return null;
103
+ out = unify(a.p, b.p, out);
104
+ if (!out) return null;
105
+ return unify(a.o, b.o, out);
106
+ }
94
107
  return deepEqual(a, b) ? env : null;
95
108
  }
96
109
  function bind(pattern, value, env) { return unify(pattern, value, env); }
@@ -106,6 +119,7 @@ function termIsGround(t, env) {
106
119
  const r = resolve(t, env);
107
120
  if (r.kind === 'var') return false;
108
121
  if (r.kind === 'list') return r.items.every((item) => termIsGround(item, env));
122
+ if (r.kind === 'triple') return termIsGround(r.s, env) && termIsGround(r.p, env) && termIsGround(r.o, env);
109
123
  if (r.kind === 'formula') return r.atoms.every((atom) => atomIsGround(atom, env));
110
124
  return true;
111
125
  }
@@ -665,6 +679,7 @@ function instantiate(term, env, ruleId) {
665
679
  }
666
680
  if (term.kind === 'blank') return blank('_:r' + ruleId + '_' + envSignature(env) + '_' + term.value.replace(/^_/, ''));
667
681
  if (term.kind === 'list') return list(term.items.map((item) => instantiate(item, env, ruleId)));
682
+ if (term.kind === 'triple') return { kind: 'triple', s: instantiate(term.s, env, ruleId), p: instantiate(term.p, env, ruleId), o: instantiate(term.o, env, ruleId) };
668
683
  if (term.kind === 'formula') return { kind: 'formula', atoms: term.atoms.map((a) => ({ s: instantiate(a.s, env, ruleId), p: instantiate(a.p, env, ruleId), o: instantiate(a.o, env, ruleId) })) };
669
684
  return cloneTerm(term);
670
685
  }
@@ -1040,19 +1055,19 @@ function renderStructuredOutput({ title, graph, queries = [], rules = [], initia
1040
1055
  const lines = [];
1041
1056
  lines.push('# ' + title);
1042
1057
  lines.push('');
1043
- lines.push('## Insight');
1058
+ lines.push('## Entailment');
1044
1059
  if (mode === 'query') {
1045
1060
  lines.push('The compiled query selected ' + selected.length + ' fact(s) after the rule closure was computed.');
1046
1061
  } else if (mode === 'formula') {
1047
- lines.push('The derivation produced ' + selected.length + ' formula-valued conclusion(s).');
1062
+ lines.push('The derivation produced ' + selected.length + ' formula-valued entailment(s).');
1048
1063
  } else {
1049
1064
  lines.push('The derivation produced ' + derived.length + ' new fact(s) from ' + initialFacts.length + ' stated fact(s).');
1050
1065
  }
1051
- if (keyFact) lines.push('Main conclusion: **' + factSentence(keyFact) + '**');
1066
+ if (keyFact) lines.push('Main entailment: **' + factSentence(keyFact) + '**');
1052
1067
  const bullets = selected.slice(-6).reverse();
1053
1068
  if (bullets.length) {
1054
1069
  lines.push('');
1055
- lines.push('Selected conclusions:');
1070
+ lines.push('Selected entailments:');
1056
1071
  for (const fact of bullets) lines.push('- ' + codeFact(fact));
1057
1072
  }
1058
1073
  lines.push('');
@@ -1114,15 +1129,15 @@ function dedupeExplanationHeadings(text) {
1114
1129
  function normalizePublicReport(markdown, title) {
1115
1130
  let text = String(markdown || '').trimEnd();
1116
1131
  if (!/^\s*#\s+/m.test(text)) text = '# ' + title + '\n\n' + text;
1117
- if (!/^##\s+Insight\s*$/mi.test(text)) {
1118
- text = text.replace(/^(#\s+[^\n]+\n*)/, '$1\n## Insight\n');
1132
+ if (!/^##\s+Entailment\s*$/mi.test(text)) {
1133
+ text = text.replace(/^(#\s+[^\n]+\n*)/, '$1\n## Entailment\n');
1119
1134
  }
1120
1135
  if (!/^##\s+Explanation\s*$/mi.test(text)) {
1121
1136
  text += '\n\n## Explanation\nNo additional explanation was provided by the generated output.';
1122
1137
  }
1123
1138
  text = text.replace(/^##\s+([^\n]+?)\s*$/gm, (line, heading) => {
1124
1139
  const normalized = heading.trim().toLowerCase();
1125
- if (normalized === 'insight' || normalized === 'explanation') return '## ' + (normalized === 'insight' ? 'Insight' : 'Explanation');
1140
+ if (normalized === 'insight' || normalized === 'conclusion' || normalized === 'entailment' || normalized === 'explanation') return '## ' + (normalized === 'explanation' ? 'Explanation' : 'Entailment');
1126
1141
  return '**' + heading.trim() + '**';
1127
1142
  });
1128
1143
  text = dedupeExplanationHeadings(text);
@@ -1131,13 +1146,13 @@ function normalizePublicReport(markdown, title) {
1131
1146
  function markdownize(raw, title) {
1132
1147
  let text = String(raw || '');
1133
1148
  text = text
1134
- .replace(/===\s*Answer\s*===/g, '## Insight')
1149
+ .replace(/===\s*Answer\s*===/g, '## Entailment')
1135
1150
  .replace(/===\s*Reason\s+Why\s*===/gi, '## Explanation')
1136
1151
  .replace(/===\s*Explanation\s*===/gi, '## Explanation')
1137
1152
  .replace(/===\s*([^=]+?)\s*===/g, (_, h) => '**' + h.trim() + '**');
1138
1153
  text = text.replace(/^C(\d+)\s+OK\s*-\s*/gm, 'C$1: ');
1139
1154
  text = dedupeExplanationHeadings(text);
1140
- if (!text.trim()) text = '## Insight\nNo log:outputString facts were derived.\n\n## Explanation\nThe compiled derivation did not produce authored report text.';
1155
+ if (!text.trim()) text = '## Entailment\nNo log:outputString facts were derived.\n\n## Explanation\nThe compiled derivation did not produce authored report text.';
1141
1156
  return normalizePublicReport(text, title);
1142
1157
  }
1143
1158
  function authoredSupportAppendix(graph, queries, rules, initialFacts, trace) {
@@ -1649,7 +1664,7 @@ const RULES = [
1649
1664
  }
1650
1665
  ];
1651
1666
  const QUERIES = [];
1652
- const DOC_MARKDOWN = "# Path Discovery\n\nGenerated by `see.js` from a Notation3 source file.\n\nSEE version of the Eyeling airport path-discovery example.\nThe full airroutes evidence is intentionally stored in\nexamples/input/path-discovery.trig, not in this source file. This file keeps\nonly the path-finding rules and the query shape from the upstream example.\n\n## Compilation summary\n\n- Example name: `path_discovery`\n- Input facts emitted: 0\n- Forward rules compiled: 1\n- Backward predicate rules compiled: 2\n- Fuses compiled: 0\n- Predicate count: 9\n\n## Built-ins used\n\n- `list:firstRest`\n- `list:notMember`\n- `log:collectAllIn`\n- `math:notGreaterThan`\n- `math:sum`\n\n## Runtime model\n\nThe generated `examples/path_discovery.js` is a specialized JavaScript derivation program. For ordinary sources, `see.js` emits the source facts as `examples/input/path_discovery.trig`. For rules-only sources, generation can reuse an existing external evidence file such as `examples/input/path-discovery.trig` or `examples/input/path_discovery.trig`. The runner reads that TriG evidence directly and performs a local fixpoint derivation; it does not parse the program source or call an external reasoner.\n\n## Output model\n\nRunning `node examples/path_discovery.js` produces a SEE-style Markdown report with an **Insight** section, an **Explanation** section, and a **Formal TriG Output** section containing the selected derived/query facts.\n";
1667
+ const DOC_MARKDOWN = "# Path Discovery\n\nGenerated by `see.js` from a Notation3 source file.\n\nSEE version of the Eyeling airport path-discovery example.\nThe full airroutes evidence is intentionally stored in\nexamples/input/path-discovery.trig, not in this source file. This file keeps\nonly the path-finding rules and the query shape from the upstream example.\n\n## Compilation summary\n\n- Example name: `path_discovery`\n- Input facts emitted: 0\n- Forward rules compiled: 1\n- Backward predicate rules compiled: 2\n- Fuses compiled: 0\n- Predicate count: 9\n\n## Built-ins used\n\n- `list:firstRest`\n- `list:notMember`\n- `log:collectAllIn`\n- `math:notGreaterThan`\n- `math:sum`\n\n## Runtime model\n\nThe generated `examples/path_discovery.js` is a specialized JavaScript derivation program. For ordinary sources, `see.js` emits the source facts as `examples/input/path_discovery.trig`. For rules-only sources, generation can reuse an existing external evidence file such as `examples/input/path-discovery.trig` or `examples/input/path_discovery.trig`. The runner reads that TriG evidence directly and performs a local fixpoint derivation; it does not parse the program source or call an external reasoner.\n\n## Output model\n\nRunning `node examples/path_discovery.js` produces a SEE-style Markdown report with an **Entailment** section, an **Explanation** section, and a **Formal TriG Output** section containing the selected derived/query facts.\n";
1653
1668
  function seeMetadata(data) { return (data && data.__see) || {}; }
1654
1669
  function trustedDerivation(data) { const meta = seeMetadata(data); const facts = data && Array.isArray(data.facts) ? data.facts : []; const expectedFacts = EXPECTED_INPUT_FACTS || Number(meta.InputFacts || 0); if (meta.SourceSHA256 && meta.SourceSHA256 !== "9481a2b27a6cae4d73f312c7d8403d1abd7beab573f627f591ea8301073fe4db") throw new Error('input evidence does not match the N3 source compiled into this example'); const result = saturate(facts, RULES); const rawOutput = renderRawOutput(result.graph, QUERIES, RULES, facts); fail('Compiled N3 derivation failed', { 'input evidence metadata is present and matches compiled source': meta.SourceSHA256 === "9481a2b27a6cae4d73f312c7d8403d1abd7beab573f627f591ea8301073fe4db", 'input evidence facts were loaded': expectedFacts > 0 ? facts.length === expectedFacts : facts.length >= 0, 'compiled rules were loaded': RULES.length === 3, 'compiled query directives were loaded': QUERIES.length === 0, 'a derivation fixpoint was reached': result.graph.facts.length >= facts.length, 'query or output facts were produced': rawOutput.length > 0 }); return { ...result, rawOutput, inputFacts: facts }; }
1655
1670
  function snapshotMarkdown(markdown) { return markdown.split(/\n/).map((line) => line ? line + ' \n' : '\n').join(''); }
@@ -1683,6 +1698,16 @@ function formalOutputFacts(graph, queries, rules, initialFacts) {
1683
1698
  }
1684
1699
  return out;
1685
1700
  }
1701
+ function termHasTripleTerm(term) {
1702
+ if (!term) return false;
1703
+ if (term.kind === 'triple') return true;
1704
+ if (term.kind === 'list') return term.items.some(termHasTripleTerm);
1705
+ if (term.kind === 'formula') return term.atoms.some(atomHasTripleTerm);
1706
+ return false;
1707
+ }
1708
+ function atomHasTripleTerm(atom) { return termHasTripleTerm(atom.s) || termHasTripleTerm(atom.p) || termHasTripleTerm(atom.o); }
1709
+ function factsHaveTripleTerms(facts) { return (facts || []).some(atomHasTripleTerm); }
1710
+ function trigHasVersion12(trig) { return /^s*(?:@version|VERSION)s+["']1.2["']/mi.test(String(trig || '')); }
1686
1711
  function trigGraphBlock(label, atoms) {
1687
1712
  const lines = [label + ' {'];
1688
1713
  for (const atom of atoms || []) lines.push(' ' + atomToN3(atom) + ' .');
@@ -1730,7 +1755,8 @@ function formalOutputToTrig(facts, trig) {
1730
1755
  const prefixes = prefixLinesFromTrig(trig);
1731
1756
  if (state.needOutPrefix && !prefixes.some((line) => line.toLowerCase().startsWith('@prefix out:'))) prefixes.push('@prefix out: <https://example.org/see/output#> .');
1732
1757
  const nl = String.fromCharCode(10);
1733
- return prefixes.join(nl) + nl + nl + body.join(nl);
1758
+ const version = factsHaveTripleTerms(facts) ? 'VERSION "1.2"' + nl + nl : '';
1759
+ return version + prefixes.join(nl) + nl + nl + body.join(nl);
1734
1760
  }
1735
1761
  function appendFormalTrigOutput(markdown, graph, queries, rules, initialFacts, data) {
1736
1762
  const trig = formalOutputToTrig(formalOutputFacts(graph, queries, rules, initialFacts), data && data.trig);
@@ -8,6 +8,7 @@ const crypto = require('crypto');
8
8
 
9
9
  function canonical(term) {
10
10
  if (term.kind === 'list') return ['list', term.items.map(canonical)];
11
+ if (term.kind === 'triple') return ['triple', canonical(term.s), canonical(term.p), canonical(term.o)];
11
12
  if (term.kind === 'formula') return ['formula', term.atoms.map((a) => [canonical(a.s), canonical(a.p), canonical(a.o)])];
12
13
  return [term.kind, term.value];
13
14
  }
@@ -17,6 +18,7 @@ function compoundIndexKey() { return Array.from(arguments).map(termIndexKey).joi
17
18
  function termIsConcrete(t) {
18
19
  if (!t || t.kind === 'var') return false;
19
20
  if (t.kind === 'list') return t.items.every(termIsConcrete);
21
+ if (t.kind === 'triple') return termIsConcrete(t.s) && termIsConcrete(t.p) && termIsConcrete(t.o);
20
22
  if (t.kind === 'formula') return t.atoms.every((a) => termIsConcrete(a.s) && termIsConcrete(a.p) && termIsConcrete(a.o));
21
23
  return true;
22
24
  }
@@ -32,6 +34,7 @@ function primitive(t) {
32
34
  if (t.kind === 'iri') return t.value.replace(/^:/, '');
33
35
  if (t.kind === 'blank') return t.value;
34
36
  if (t.kind === 'list') return t.items.map(primitive);
37
+ if (t.kind === 'triple') return termToN3(t);
35
38
  if (t.kind === 'formula') return termToN3(t);
36
39
  return undefined;
37
40
  }
@@ -52,6 +55,7 @@ function termToN3(t) {
52
55
  if (t.kind === 'var') return '?' + t.value;
53
56
  if (t.kind === 'blank') return t.value.startsWith('_:') ? t.value : '_:' + t.value.replace(/^_+/, '');
54
57
  if (t.kind === 'list') return '(' + t.items.map(termToN3).join(' ') + ')';
58
+ if (t.kind === 'triple') return '<<( ' + termToN3(t.s) + ' ' + termToN3(t.p) + ' ' + termToN3(t.o) + ' )>>';
55
59
  if (t.kind === 'formula') return '{ ' + t.atoms.map(atomToN3).join(' . ') + ' }';
56
60
  return String(t.value ?? t);
57
61
  }
@@ -74,6 +78,7 @@ function resolve(term, env, seen = new Set()) {
74
78
  return resolve(env[term.value], env, seen);
75
79
  }
76
80
  if (term.kind === 'list') return list(term.items.map((item) => resolve(item, env, seen)));
81
+ if (term.kind === 'triple') return { kind: 'triple', s: resolve(term.s, env), p: resolve(term.p, env), o: resolve(term.o, env) };
77
82
  if (term.kind === 'formula') return { kind: 'formula', atoms: term.atoms.map((a) => ({ s: resolve(a.s, env), p: resolve(a.p, env), o: resolve(a.o, env) })) };
78
83
  return term;
79
84
  }
@@ -91,6 +96,14 @@ function unify(a, b, env) {
91
96
  }
92
97
  return out;
93
98
  }
99
+ if (a.kind === 'triple' || b.kind === 'triple') {
100
+ if (a.kind !== 'triple' || b.kind !== 'triple') return null;
101
+ let out = unify(a.s, b.s, env);
102
+ if (!out) return null;
103
+ out = unify(a.p, b.p, out);
104
+ if (!out) return null;
105
+ return unify(a.o, b.o, out);
106
+ }
94
107
  return deepEqual(a, b) ? env : null;
95
108
  }
96
109
  function bind(pattern, value, env) { return unify(pattern, value, env); }
@@ -106,6 +119,7 @@ function termIsGround(t, env) {
106
119
  const r = resolve(t, env);
107
120
  if (r.kind === 'var') return false;
108
121
  if (r.kind === 'list') return r.items.every((item) => termIsGround(item, env));
122
+ if (r.kind === 'triple') return termIsGround(r.s, env) && termIsGround(r.p, env) && termIsGround(r.o, env);
109
123
  if (r.kind === 'formula') return r.atoms.every((atom) => atomIsGround(atom, env));
110
124
  return true;
111
125
  }
@@ -665,6 +679,7 @@ function instantiate(term, env, ruleId) {
665
679
  }
666
680
  if (term.kind === 'blank') return blank('_:r' + ruleId + '_' + envSignature(env) + '_' + term.value.replace(/^_/, ''));
667
681
  if (term.kind === 'list') return list(term.items.map((item) => instantiate(item, env, ruleId)));
682
+ if (term.kind === 'triple') return { kind: 'triple', s: instantiate(term.s, env, ruleId), p: instantiate(term.p, env, ruleId), o: instantiate(term.o, env, ruleId) };
668
683
  if (term.kind === 'formula') return { kind: 'formula', atoms: term.atoms.map((a) => ({ s: instantiate(a.s, env, ruleId), p: instantiate(a.p, env, ruleId), o: instantiate(a.o, env, ruleId) })) };
669
684
  return cloneTerm(term);
670
685
  }
@@ -1040,19 +1055,19 @@ function renderStructuredOutput({ title, graph, queries = [], rules = [], initia
1040
1055
  const lines = [];
1041
1056
  lines.push('# ' + title);
1042
1057
  lines.push('');
1043
- lines.push('## Insight');
1058
+ lines.push('## Entailment');
1044
1059
  if (mode === 'query') {
1045
1060
  lines.push('The compiled query selected ' + selected.length + ' fact(s) after the rule closure was computed.');
1046
1061
  } else if (mode === 'formula') {
1047
- lines.push('The derivation produced ' + selected.length + ' formula-valued conclusion(s).');
1062
+ lines.push('The derivation produced ' + selected.length + ' formula-valued entailment(s).');
1048
1063
  } else {
1049
1064
  lines.push('The derivation produced ' + derived.length + ' new fact(s) from ' + initialFacts.length + ' stated fact(s).');
1050
1065
  }
1051
- if (keyFact) lines.push('Main conclusion: **' + factSentence(keyFact) + '**');
1066
+ if (keyFact) lines.push('Main entailment: **' + factSentence(keyFact) + '**');
1052
1067
  const bullets = selected.slice(-6).reverse();
1053
1068
  if (bullets.length) {
1054
1069
  lines.push('');
1055
- lines.push('Selected conclusions:');
1070
+ lines.push('Selected entailments:');
1056
1071
  for (const fact of bullets) lines.push('- ' + codeFact(fact));
1057
1072
  }
1058
1073
  lines.push('');
@@ -1114,15 +1129,15 @@ function dedupeExplanationHeadings(text) {
1114
1129
  function normalizePublicReport(markdown, title) {
1115
1130
  let text = String(markdown || '').trimEnd();
1116
1131
  if (!/^\s*#\s+/m.test(text)) text = '# ' + title + '\n\n' + text;
1117
- if (!/^##\s+Insight\s*$/mi.test(text)) {
1118
- text = text.replace(/^(#\s+[^\n]+\n*)/, '$1\n## Insight\n');
1132
+ if (!/^##\s+Entailment\s*$/mi.test(text)) {
1133
+ text = text.replace(/^(#\s+[^\n]+\n*)/, '$1\n## Entailment\n');
1119
1134
  }
1120
1135
  if (!/^##\s+Explanation\s*$/mi.test(text)) {
1121
1136
  text += '\n\n## Explanation\nNo additional explanation was provided by the generated output.';
1122
1137
  }
1123
1138
  text = text.replace(/^##\s+([^\n]+?)\s*$/gm, (line, heading) => {
1124
1139
  const normalized = heading.trim().toLowerCase();
1125
- if (normalized === 'insight' || normalized === 'explanation') return '## ' + (normalized === 'insight' ? 'Insight' : 'Explanation');
1140
+ if (normalized === 'insight' || normalized === 'conclusion' || normalized === 'entailment' || normalized === 'explanation') return '## ' + (normalized === 'explanation' ? 'Explanation' : 'Entailment');
1126
1141
  return '**' + heading.trim() + '**';
1127
1142
  });
1128
1143
  text = dedupeExplanationHeadings(text);
@@ -1131,13 +1146,13 @@ function normalizePublicReport(markdown, title) {
1131
1146
  function markdownize(raw, title) {
1132
1147
  let text = String(raw || '');
1133
1148
  text = text
1134
- .replace(/===\s*Answer\s*===/g, '## Insight')
1149
+ .replace(/===\s*Answer\s*===/g, '## Entailment')
1135
1150
  .replace(/===\s*Reason\s+Why\s*===/gi, '## Explanation')
1136
1151
  .replace(/===\s*Explanation\s*===/gi, '## Explanation')
1137
1152
  .replace(/===\s*([^=]+?)\s*===/g, (_, h) => '**' + h.trim() + '**');
1138
1153
  text = text.replace(/^C(\d+)\s+OK\s*-\s*/gm, 'C$1: ');
1139
1154
  text = dedupeExplanationHeadings(text);
1140
- if (!text.trim()) text = '## Insight\nNo log:outputString facts were derived.\n\n## Explanation\nThe compiled derivation did not produce authored report text.';
1155
+ if (!text.trim()) text = '## Entailment\nNo log:outputString facts were derived.\n\n## Explanation\nThe compiled derivation did not produce authored report text.';
1141
1156
  return normalizePublicReport(text, title);
1142
1157
  }
1143
1158
  function authoredSupportAppendix(graph, queries, rules, initialFacts, trace) {
@@ -1868,7 +1883,7 @@ const RULES = [
1868
1883
  }
1869
1884
  ];
1870
1885
  const QUERIES = [];
1871
- const DOC_MARKDOWN = "# RC Discharge Envelope\n\nGenerated by `see.js` from a Notation3 source file.\n\nCertify when a sampled RC capacitor is guaranteed below tolerance using an\nupper decay bound. The witness is derived by N3 rules and compiled to SEE.\n\n## Compilation summary\n\n- Example name: `rc_discharge_envelope`\n- Input facts emitted: 12\n- Forward rules compiled: 6\n- Backward predicate rules compiled: 0\n- Fuses compiled: 0\n- Predicate count: 22\n\n## Built-ins used\n\n- `log:outputString`\n- `math:exponentiation`\n- `math:lessThan`\n- `math:notGreaterThan`\n- `math:notLessThan`\n- `math:product`\n- `string:format`\n\n## Runtime model\n\nThe generated `examples/rc_discharge_envelope.js` is a specialized JavaScript derivation program. For ordinary sources, `see.js` emits the source facts as `examples/input/rc_discharge_envelope.trig`. For rules-only sources, generation can reuse an existing external evidence file such as `examples/input/rc-discharge-envelope.trig` or `examples/input/rc_discharge_envelope.trig`. The runner reads that TriG evidence directly and performs a local fixpoint derivation; it does not parse the program source or call an external reasoner.\n\n## Output model\n\nRunning `node examples/rc_discharge_envelope.js` produces a SEE-style Markdown report with an **Insight** section, an **Explanation** section, and a **Formal TriG Output** section containing the selected derived/query facts.\n";
1886
+ const DOC_MARKDOWN = "# RC Discharge Envelope\n\nGenerated by `see.js` from a Notation3 source file.\n\nCertify when a sampled RC capacitor is guaranteed below tolerance using an\nupper decay bound. The witness is derived by N3 rules and compiled to SEE.\n\n## Compilation summary\n\n- Example name: `rc_discharge_envelope`\n- Input facts emitted: 12\n- Forward rules compiled: 6\n- Backward predicate rules compiled: 0\n- Fuses compiled: 0\n- Predicate count: 22\n\n## Built-ins used\n\n- `log:outputString`\n- `math:exponentiation`\n- `math:lessThan`\n- `math:notGreaterThan`\n- `math:notLessThan`\n- `math:product`\n- `string:format`\n\n## Runtime model\n\nThe generated `examples/rc_discharge_envelope.js` is a specialized JavaScript derivation program. For ordinary sources, `see.js` emits the source facts as `examples/input/rc_discharge_envelope.trig`. For rules-only sources, generation can reuse an existing external evidence file such as `examples/input/rc-discharge-envelope.trig` or `examples/input/rc_discharge_envelope.trig`. The runner reads that TriG evidence directly and performs a local fixpoint derivation; it does not parse the program source or call an external reasoner.\n\n## Output model\n\nRunning `node examples/rc_discharge_envelope.js` produces a SEE-style Markdown report with an **Entailment** section, an **Explanation** section, and a **Formal TriG Output** section containing the selected derived/query facts.\n";
1872
1887
  function seeMetadata(data) { return (data && data.__see) || {}; }
1873
1888
  function trustedDerivation(data) { const meta = seeMetadata(data); const facts = data && Array.isArray(data.facts) ? data.facts : []; const expectedFacts = EXPECTED_INPUT_FACTS || Number(meta.InputFacts || 0); if (meta.SourceSHA256 && meta.SourceSHA256 !== "41acff69cd2994230faedf5a466b4c43d47cdff9dc6d834baecd9865979160b8") throw new Error('input evidence does not match the N3 source compiled into this example'); const result = saturate(facts, RULES); const rawOutput = renderRawOutput(result.graph, QUERIES, RULES, facts); fail('Compiled N3 derivation failed', { 'input evidence metadata is present and matches compiled source': meta.SourceSHA256 === "41acff69cd2994230faedf5a466b4c43d47cdff9dc6d834baecd9865979160b8", 'input evidence facts were loaded': expectedFacts > 0 ? facts.length === expectedFacts : facts.length >= 0, 'compiled rules were loaded': RULES.length === 6, 'compiled query directives were loaded': QUERIES.length === 0, 'a derivation fixpoint was reached': result.graph.facts.length >= facts.length, 'query or output facts were produced': rawOutput.length > 0 }); return { ...result, rawOutput, inputFacts: facts }; }
1874
1889
  function snapshotMarkdown(markdown) { return markdown.split(/\n/).map((line) => line ? line + ' \n' : '\n').join(''); }
@@ -1902,6 +1917,16 @@ function formalOutputFacts(graph, queries, rules, initialFacts) {
1902
1917
  }
1903
1918
  return out;
1904
1919
  }
1920
+ function termHasTripleTerm(term) {
1921
+ if (!term) return false;
1922
+ if (term.kind === 'triple') return true;
1923
+ if (term.kind === 'list') return term.items.some(termHasTripleTerm);
1924
+ if (term.kind === 'formula') return term.atoms.some(atomHasTripleTerm);
1925
+ return false;
1926
+ }
1927
+ function atomHasTripleTerm(atom) { return termHasTripleTerm(atom.s) || termHasTripleTerm(atom.p) || termHasTripleTerm(atom.o); }
1928
+ function factsHaveTripleTerms(facts) { return (facts || []).some(atomHasTripleTerm); }
1929
+ function trigHasVersion12(trig) { return /^s*(?:@version|VERSION)s+["']1.2["']/mi.test(String(trig || '')); }
1905
1930
  function trigGraphBlock(label, atoms) {
1906
1931
  const lines = [label + ' {'];
1907
1932
  for (const atom of atoms || []) lines.push(' ' + atomToN3(atom) + ' .');
@@ -1949,7 +1974,8 @@ function formalOutputToTrig(facts, trig) {
1949
1974
  const prefixes = prefixLinesFromTrig(trig);
1950
1975
  if (state.needOutPrefix && !prefixes.some((line) => line.toLowerCase().startsWith('@prefix out:'))) prefixes.push('@prefix out: <https://example.org/see/output#> .');
1951
1976
  const nl = String.fromCharCode(10);
1952
- return prefixes.join(nl) + nl + nl + body.join(nl);
1977
+ const version = factsHaveTripleTerms(facts) ? 'VERSION "1.2"' + nl + nl : '';
1978
+ return version + prefixes.join(nl) + nl + nl + body.join(nl);
1953
1979
  }
1954
1980
  function appendFormalTrigOutput(markdown, graph, queries, rules, initialFacts, data) {
1955
1981
  const trig = formalOutputToTrig(formalOutputFacts(graph, queries, rules, initialFacts), data && data.trig);