eyeling 1.25.4 → 1.25.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.
@@ -5973,6 +5973,158 @@ function __varOccursElsewhereInPremise(premise, name, idx, field) {
5973
5973
  return false;
5974
5974
  }
5975
5975
 
5976
+
5977
+ function __scopedPriorityForTerm(t) {
5978
+ if (t instanceof GraphTerm) return 0;
5979
+ const p0 = __logNaturalPriorityFromTerm(t);
5980
+ return p0 !== null && p0 >= 1 ? p0 : 1;
5981
+ }
5982
+
5983
+ function __pushScopedQueryTriplesFromGraph(term, out) {
5984
+ if (!(term instanceof GraphTerm)) return;
5985
+ for (const tr of term.triples) out.push(tr);
5986
+ }
5987
+
5988
+ function __analyzeForwardRuleScopedUse(rule) {
5989
+ const out = {
5990
+ needsSnap: false,
5991
+ baseLevel: 0,
5992
+ queryTriples: [],
5993
+ hasScopedAggregate: false,
5994
+ };
5995
+
5996
+ if (!rule || !Array.isArray(rule.premise)) return out;
5997
+
5998
+ for (let i = 0; i < rule.premise.length; i++) {
5999
+ const tr = rule.premise[i];
6000
+ if (!(tr && tr.p instanceof Iri)) continue;
6001
+ const pv = tr.p.value;
6002
+
6003
+ if (pv === LOG_NS + 'includes' || pv === LOG_NS + 'notIncludes') {
6004
+ // Explicit quoted scopes are local formulas, not snapshots of the global closure.
6005
+ if (tr.s instanceof GraphTerm) continue;
6006
+
6007
+ // A scope variable that is bound by another premise may become an explicit GraphTerm.
6008
+ // Still, if it remains unbound at runtime the builtin treats it as priority 1.
6009
+ // We therefore record the scoped use, but only use quoted object triples for
6010
+ // dependency analysis; variables inside the quoted object do not bind the scope.
6011
+ out.needsSnap = true;
6012
+ out.baseLevel = Math.max(out.baseLevel, __scopedPriorityForTerm(tr.s));
6013
+ __pushScopedQueryTriplesFromGraph(tr.o, out.queryTriples);
6014
+ continue;
6015
+ }
6016
+
6017
+ if (pv === LOG_NS + 'collectAllIn') {
6018
+ if (tr.o instanceof GraphTerm) continue;
6019
+
6020
+ out.needsSnap = true;
6021
+ out.hasScopedAggregate = true;
6022
+ out.baseLevel = Math.max(out.baseLevel, __scopedPriorityForTerm(tr.o));
6023
+ if (tr.s instanceof ListTerm && tr.s.elems.length === 3) {
6024
+ __pushScopedQueryTriplesFromGraph(tr.s.elems[1], out.queryTriples);
6025
+ }
6026
+ continue;
6027
+ }
6028
+
6029
+ if (pv === LOG_NS + 'forAllIn') {
6030
+ if (tr.o instanceof GraphTerm) continue;
6031
+
6032
+ out.needsSnap = true;
6033
+ out.hasScopedAggregate = true;
6034
+ out.baseLevel = Math.max(out.baseLevel, __scopedPriorityForTerm(tr.o));
6035
+ if (tr.s instanceof ListTerm && tr.s.elems.length === 2) {
6036
+ __pushScopedQueryTriplesFromGraph(tr.s.elems[0], out.queryTriples);
6037
+ __pushScopedQueryTriplesFromGraph(tr.s.elems[1], out.queryTriples);
6038
+ }
6039
+ }
6040
+ }
6041
+
6042
+ return out;
6043
+ }
6044
+
6045
+ function __triplePatternsMayOverlap(a, b) {
6046
+ return unifyTriple(a, b, __emptySubst()) !== null || unifyTriple(b, a, __emptySubst()) !== null;
6047
+ }
6048
+
6049
+ function __ruleConclusionsMayFeedScopedQueries(producer, consumerMeta) {
6050
+ if (!consumerMeta || !consumerMeta.queryTriples || consumerMeta.queryTriples.length === 0) return false;
6051
+
6052
+ // Dynamic conclusions are only known after a proof solution. Conservatively
6053
+ // assume they can feed any scoped query if the producing rule is scoped.
6054
+ if (producer && producer.__dynamicConclusionTerm) return true;
6055
+
6056
+ if (!producer || !Array.isArray(producer.conclusion) || producer.conclusion.length === 0) return false;
6057
+ for (const q of consumerMeta.queryTriples) {
6058
+ for (const h of producer.conclusion) {
6059
+ if (__triplePatternsMayOverlap(q, h)) return true;
6060
+ }
6061
+ }
6062
+ return false;
6063
+ }
6064
+
6065
+ function __setForwardRuleScopedStratumInfo(rule, level) {
6066
+ const value =
6067
+ level > 0
6068
+ ? { needsSnap: true, requiredLevel: level, exactLevel: true }
6069
+ : { needsSnap: false, requiredLevel: 0, exactLevel: false };
6070
+
6071
+ if (!hasOwn.call(rule, '__scopedStratumInfo')) {
6072
+ Object.defineProperty(rule, '__scopedStratumInfo', {
6073
+ value,
6074
+ enumerable: false,
6075
+ writable: true,
6076
+ configurable: true,
6077
+ });
6078
+ } else {
6079
+ rule.__scopedStratumInfo = value;
6080
+ }
6081
+ }
6082
+
6083
+ function __computeForwardRuleScopedStrata(forwardRules) {
6084
+ if (!Array.isArray(forwardRules) || forwardRules.length === 0) return 0;
6085
+
6086
+ const metas = forwardRules.map((r) => __analyzeForwardRuleScopedUse(r));
6087
+ const levels = metas.map((m) => (m.needsSnap ? Math.max(1, m.baseLevel || 1) : 0));
6088
+
6089
+ let maxLevel = 0;
6090
+ for (const lvl of levels) if (lvl > maxLevel) maxLevel = lvl;
6091
+
6092
+ // Stratify scoped rules by data dependency: if a scoped query can read facts
6093
+ // produced by another scoped rule, the reader must run against a later frozen
6094
+ // snapshot. This prevents non-monotonic scoped builtins such as collectAllIn
6095
+ // from emitting an early result before lower strata have derived their facts.
6096
+ const passLimit = Math.max(1, forwardRules.length) + maxLevel + 1;
6097
+ for (let pass = 0; pass < passLimit; pass++) {
6098
+ let changed = false;
6099
+ for (let ci = 0; ci < forwardRules.length; ci++) {
6100
+ if (!metas[ci].needsSnap || !metas[ci].hasScopedAggregate) continue;
6101
+ let needed = levels[ci];
6102
+ for (let pi = 0; pi < forwardRules.length; pi++) {
6103
+ if (pi === ci) continue;
6104
+ if (levels[pi] <= 0) continue;
6105
+ if (metas[pi].hasScopedAggregate) continue;
6106
+ if (!__ruleConclusionsMayFeedScopedQueries(forwardRules[pi], metas[ci])) continue;
6107
+ needed = Math.max(needed, levels[pi] + 1);
6108
+ }
6109
+ if (needed !== levels[ci]) {
6110
+ levels[ci] = needed;
6111
+ if (needed > maxLevel) maxLevel = needed;
6112
+ changed = true;
6113
+ }
6114
+ }
6115
+ if (!changed) break;
6116
+ }
6117
+
6118
+
6119
+ maxLevel = 0;
6120
+ for (let i = 0; i < forwardRules.length; i++) {
6121
+ __setForwardRuleScopedStratumInfo(forwardRules[i], levels[i]);
6122
+ if (levels[i] > maxLevel) maxLevel = levels[i];
6123
+ }
6124
+
6125
+ return maxLevel;
6126
+ }
6127
+
5976
6128
  function __computeForwardRuleScopedSkipInfo(rule) {
5977
6129
  let needsSnap = false;
5978
6130
  let requiredLevel = 0;
@@ -7158,7 +7310,7 @@ function makeSinglePremiseAgendaIndex(forwardRules, backRules) {
7158
7310
  goalPredTid: goal.p instanceof Iri ? goal.p.__tid : null,
7159
7311
  goalSKey,
7160
7312
  goalOKey,
7161
- needsSkipCheck: !!r.__needsForwardSkipCheck,
7313
+ needsSkipCheck: !!(r.__needsForwardSkipCheck || (r.__scopedStratumInfo && r.__scopedStratumInfo.needsSnap)),
7162
7314
  fastSubjectVar,
7163
7315
  fastObjectVar,
7164
7316
  };
@@ -8606,7 +8758,10 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
8606
8758
  let scopedClosureLevel = 0;
8607
8759
 
8608
8760
  // Scan known rules for the maximum requested closure priority in scoped log:* goals.
8609
- let maxScopedClosurePriorityNeeded = __computeMaxScopedClosurePriorityNeeded(forwardRules, backRules);
8761
+ let maxScopedClosurePriorityNeeded = Math.max(
8762
+ __computeMaxScopedClosurePriorityNeeded(forwardRules, backRules),
8763
+ __computeForwardRuleScopedStrata(forwardRules),
8764
+ );
8610
8765
 
8611
8766
  // Conservative fast-skip for forward rules that cannot possibly succeed
8612
8767
  // until a scoped snapshot exists (or a given closure level is reached).
@@ -8660,12 +8815,16 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
8660
8815
  // until a snapshot exists (and a certain closure level is reached).
8661
8816
  // This prevents expensive proofs that will definitely fail in Phase A
8662
8817
  // and in early closure levels.
8663
- const info = r.__scopedSkipInfo;
8818
+ const info = r.__scopedStratumInfo || r.__scopedSkipInfo;
8664
8819
  if (info && info.needsSnap) {
8665
8820
  const snapHere = facts.__scopedSnapshot || null;
8666
8821
  const lvlHere = (facts && typeof facts.__scopedClosureLevel === 'number' && facts.__scopedClosureLevel) || 0;
8667
8822
  if (!snapHere) return true;
8668
- if (lvlHere < info.requiredLevel) return true;
8823
+ if (info.exactLevel) {
8824
+ if (lvlHere !== info.requiredLevel) return true;
8825
+ } else if (lvlHere < info.requiredLevel) {
8826
+ return true;
8827
+ }
8669
8828
  }
8670
8829
 
8671
8830
  // Optimization: if the rule head is **structurally ground** (no vars anywhere, even inside
@@ -8865,6 +9024,10 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
8865
9024
 
8866
9025
  const outcome = __emitForwardRuleSolution(r, entry.ruleIndex, s);
8867
9026
  if (outcome.rulesChanged) {
9027
+ maxScopedClosurePriorityNeeded = Math.max(
9028
+ __computeMaxScopedClosurePriorityNeeded(forwardRules, backRules),
9029
+ __computeForwardRuleScopedStrata(forwardRules),
9030
+ );
8868
9031
  agendaIndex = makeSinglePremiseAgendaIndex(forwardRules, backRules);
8869
9032
  agendaCursor = 0;
8870
9033
  }
@@ -8878,7 +9041,7 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
8878
9041
  for (let i = 0; i < forwardRules.length; i++) {
8879
9042
  const r = forwardRules[i];
8880
9043
  if (agendaIndex.indexed.has(r)) continue;
8881
- if (r.__needsForwardSkipCheck && __skipForwardRuleNow(r)) continue;
9044
+ if ((r.__needsForwardSkipCheck || (r.__scopedStratumInfo && r.__scopedStratumInfo.needsSnap)) && __skipForwardRuleNow(r)) continue;
8882
9045
 
8883
9046
  const headIsStrictGround = r.__headIsStrictGround;
8884
9047
  const maxSols = r.isFuse || headIsStrictGround ? 1 : undefined;
@@ -8899,6 +9062,10 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
8899
9062
  for (const s of sols) {
8900
9063
  const outcome = __emitForwardRuleSolution(r, i, s);
8901
9064
  if (outcome.rulesChanged) {
9065
+ maxScopedClosurePriorityNeeded = Math.max(
9066
+ __computeMaxScopedClosurePriorityNeeded(forwardRules, backRules),
9067
+ __computeForwardRuleScopedStrata(forwardRules),
9068
+ );
8902
9069
  agendaIndex = makeSinglePremiseAgendaIndex(forwardRules, backRules);
8903
9070
  agendaCursor = 0;
8904
9071
  }
@@ -8926,8 +9093,8 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
8926
9093
  // Rules may have been added dynamically (rule-producing triples), possibly
8927
9094
  // introducing scoped builtins and/or higher closure priorities.
8928
9095
  maxScopedClosurePriorityNeeded = Math.max(
8929
- maxScopedClosurePriorityNeeded,
8930
9096
  __computeMaxScopedClosurePriorityNeeded(forwardRules, backRules),
9097
+ __computeForwardRuleScopedStrata(forwardRules),
8931
9098
  );
8932
9099
 
8933
9100
  // If there are no scoped builtins in the entire program, Phase B is pure
@@ -8945,8 +9112,8 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
8945
9112
 
8946
9113
  // Phase B can also derive rule-producing triples.
8947
9114
  maxScopedClosurePriorityNeeded = Math.max(
8948
- maxScopedClosurePriorityNeeded,
8949
9115
  __computeMaxScopedClosurePriorityNeeded(forwardRules, backRules),
9116
+ __computeForwardRuleScopedStrata(forwardRules),
8950
9117
  );
8951
9118
 
8952
9119
  if (!changedA && !changedB && scopedClosureLevel >= maxScopedClosurePriorityNeeded) break;
package/eyeling.js CHANGED
@@ -5973,6 +5973,158 @@ function __varOccursElsewhereInPremise(premise, name, idx, field) {
5973
5973
  return false;
5974
5974
  }
5975
5975
 
5976
+
5977
+ function __scopedPriorityForTerm(t) {
5978
+ if (t instanceof GraphTerm) return 0;
5979
+ const p0 = __logNaturalPriorityFromTerm(t);
5980
+ return p0 !== null && p0 >= 1 ? p0 : 1;
5981
+ }
5982
+
5983
+ function __pushScopedQueryTriplesFromGraph(term, out) {
5984
+ if (!(term instanceof GraphTerm)) return;
5985
+ for (const tr of term.triples) out.push(tr);
5986
+ }
5987
+
5988
+ function __analyzeForwardRuleScopedUse(rule) {
5989
+ const out = {
5990
+ needsSnap: false,
5991
+ baseLevel: 0,
5992
+ queryTriples: [],
5993
+ hasScopedAggregate: false,
5994
+ };
5995
+
5996
+ if (!rule || !Array.isArray(rule.premise)) return out;
5997
+
5998
+ for (let i = 0; i < rule.premise.length; i++) {
5999
+ const tr = rule.premise[i];
6000
+ if (!(tr && tr.p instanceof Iri)) continue;
6001
+ const pv = tr.p.value;
6002
+
6003
+ if (pv === LOG_NS + 'includes' || pv === LOG_NS + 'notIncludes') {
6004
+ // Explicit quoted scopes are local formulas, not snapshots of the global closure.
6005
+ if (tr.s instanceof GraphTerm) continue;
6006
+
6007
+ // A scope variable that is bound by another premise may become an explicit GraphTerm.
6008
+ // Still, if it remains unbound at runtime the builtin treats it as priority 1.
6009
+ // We therefore record the scoped use, but only use quoted object triples for
6010
+ // dependency analysis; variables inside the quoted object do not bind the scope.
6011
+ out.needsSnap = true;
6012
+ out.baseLevel = Math.max(out.baseLevel, __scopedPriorityForTerm(tr.s));
6013
+ __pushScopedQueryTriplesFromGraph(tr.o, out.queryTriples);
6014
+ continue;
6015
+ }
6016
+
6017
+ if (pv === LOG_NS + 'collectAllIn') {
6018
+ if (tr.o instanceof GraphTerm) continue;
6019
+
6020
+ out.needsSnap = true;
6021
+ out.hasScopedAggregate = true;
6022
+ out.baseLevel = Math.max(out.baseLevel, __scopedPriorityForTerm(tr.o));
6023
+ if (tr.s instanceof ListTerm && tr.s.elems.length === 3) {
6024
+ __pushScopedQueryTriplesFromGraph(tr.s.elems[1], out.queryTriples);
6025
+ }
6026
+ continue;
6027
+ }
6028
+
6029
+ if (pv === LOG_NS + 'forAllIn') {
6030
+ if (tr.o instanceof GraphTerm) continue;
6031
+
6032
+ out.needsSnap = true;
6033
+ out.hasScopedAggregate = true;
6034
+ out.baseLevel = Math.max(out.baseLevel, __scopedPriorityForTerm(tr.o));
6035
+ if (tr.s instanceof ListTerm && tr.s.elems.length === 2) {
6036
+ __pushScopedQueryTriplesFromGraph(tr.s.elems[0], out.queryTriples);
6037
+ __pushScopedQueryTriplesFromGraph(tr.s.elems[1], out.queryTriples);
6038
+ }
6039
+ }
6040
+ }
6041
+
6042
+ return out;
6043
+ }
6044
+
6045
+ function __triplePatternsMayOverlap(a, b) {
6046
+ return unifyTriple(a, b, __emptySubst()) !== null || unifyTriple(b, a, __emptySubst()) !== null;
6047
+ }
6048
+
6049
+ function __ruleConclusionsMayFeedScopedQueries(producer, consumerMeta) {
6050
+ if (!consumerMeta || !consumerMeta.queryTriples || consumerMeta.queryTriples.length === 0) return false;
6051
+
6052
+ // Dynamic conclusions are only known after a proof solution. Conservatively
6053
+ // assume they can feed any scoped query if the producing rule is scoped.
6054
+ if (producer && producer.__dynamicConclusionTerm) return true;
6055
+
6056
+ if (!producer || !Array.isArray(producer.conclusion) || producer.conclusion.length === 0) return false;
6057
+ for (const q of consumerMeta.queryTriples) {
6058
+ for (const h of producer.conclusion) {
6059
+ if (__triplePatternsMayOverlap(q, h)) return true;
6060
+ }
6061
+ }
6062
+ return false;
6063
+ }
6064
+
6065
+ function __setForwardRuleScopedStratumInfo(rule, level) {
6066
+ const value =
6067
+ level > 0
6068
+ ? { needsSnap: true, requiredLevel: level, exactLevel: true }
6069
+ : { needsSnap: false, requiredLevel: 0, exactLevel: false };
6070
+
6071
+ if (!hasOwn.call(rule, '__scopedStratumInfo')) {
6072
+ Object.defineProperty(rule, '__scopedStratumInfo', {
6073
+ value,
6074
+ enumerable: false,
6075
+ writable: true,
6076
+ configurable: true,
6077
+ });
6078
+ } else {
6079
+ rule.__scopedStratumInfo = value;
6080
+ }
6081
+ }
6082
+
6083
+ function __computeForwardRuleScopedStrata(forwardRules) {
6084
+ if (!Array.isArray(forwardRules) || forwardRules.length === 0) return 0;
6085
+
6086
+ const metas = forwardRules.map((r) => __analyzeForwardRuleScopedUse(r));
6087
+ const levels = metas.map((m) => (m.needsSnap ? Math.max(1, m.baseLevel || 1) : 0));
6088
+
6089
+ let maxLevel = 0;
6090
+ for (const lvl of levels) if (lvl > maxLevel) maxLevel = lvl;
6091
+
6092
+ // Stratify scoped rules by data dependency: if a scoped query can read facts
6093
+ // produced by another scoped rule, the reader must run against a later frozen
6094
+ // snapshot. This prevents non-monotonic scoped builtins such as collectAllIn
6095
+ // from emitting an early result before lower strata have derived their facts.
6096
+ const passLimit = Math.max(1, forwardRules.length) + maxLevel + 1;
6097
+ for (let pass = 0; pass < passLimit; pass++) {
6098
+ let changed = false;
6099
+ for (let ci = 0; ci < forwardRules.length; ci++) {
6100
+ if (!metas[ci].needsSnap || !metas[ci].hasScopedAggregate) continue;
6101
+ let needed = levels[ci];
6102
+ for (let pi = 0; pi < forwardRules.length; pi++) {
6103
+ if (pi === ci) continue;
6104
+ if (levels[pi] <= 0) continue;
6105
+ if (metas[pi].hasScopedAggregate) continue;
6106
+ if (!__ruleConclusionsMayFeedScopedQueries(forwardRules[pi], metas[ci])) continue;
6107
+ needed = Math.max(needed, levels[pi] + 1);
6108
+ }
6109
+ if (needed !== levels[ci]) {
6110
+ levels[ci] = needed;
6111
+ if (needed > maxLevel) maxLevel = needed;
6112
+ changed = true;
6113
+ }
6114
+ }
6115
+ if (!changed) break;
6116
+ }
6117
+
6118
+
6119
+ maxLevel = 0;
6120
+ for (let i = 0; i < forwardRules.length; i++) {
6121
+ __setForwardRuleScopedStratumInfo(forwardRules[i], levels[i]);
6122
+ if (levels[i] > maxLevel) maxLevel = levels[i];
6123
+ }
6124
+
6125
+ return maxLevel;
6126
+ }
6127
+
5976
6128
  function __computeForwardRuleScopedSkipInfo(rule) {
5977
6129
  let needsSnap = false;
5978
6130
  let requiredLevel = 0;
@@ -7158,7 +7310,7 @@ function makeSinglePremiseAgendaIndex(forwardRules, backRules) {
7158
7310
  goalPredTid: goal.p instanceof Iri ? goal.p.__tid : null,
7159
7311
  goalSKey,
7160
7312
  goalOKey,
7161
- needsSkipCheck: !!r.__needsForwardSkipCheck,
7313
+ needsSkipCheck: !!(r.__needsForwardSkipCheck || (r.__scopedStratumInfo && r.__scopedStratumInfo.needsSnap)),
7162
7314
  fastSubjectVar,
7163
7315
  fastObjectVar,
7164
7316
  };
@@ -8606,7 +8758,10 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
8606
8758
  let scopedClosureLevel = 0;
8607
8759
 
8608
8760
  // Scan known rules for the maximum requested closure priority in scoped log:* goals.
8609
- let maxScopedClosurePriorityNeeded = __computeMaxScopedClosurePriorityNeeded(forwardRules, backRules);
8761
+ let maxScopedClosurePriorityNeeded = Math.max(
8762
+ __computeMaxScopedClosurePriorityNeeded(forwardRules, backRules),
8763
+ __computeForwardRuleScopedStrata(forwardRules),
8764
+ );
8610
8765
 
8611
8766
  // Conservative fast-skip for forward rules that cannot possibly succeed
8612
8767
  // until a scoped snapshot exists (or a given closure level is reached).
@@ -8660,12 +8815,16 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
8660
8815
  // until a snapshot exists (and a certain closure level is reached).
8661
8816
  // This prevents expensive proofs that will definitely fail in Phase A
8662
8817
  // and in early closure levels.
8663
- const info = r.__scopedSkipInfo;
8818
+ const info = r.__scopedStratumInfo || r.__scopedSkipInfo;
8664
8819
  if (info && info.needsSnap) {
8665
8820
  const snapHere = facts.__scopedSnapshot || null;
8666
8821
  const lvlHere = (facts && typeof facts.__scopedClosureLevel === 'number' && facts.__scopedClosureLevel) || 0;
8667
8822
  if (!snapHere) return true;
8668
- if (lvlHere < info.requiredLevel) return true;
8823
+ if (info.exactLevel) {
8824
+ if (lvlHere !== info.requiredLevel) return true;
8825
+ } else if (lvlHere < info.requiredLevel) {
8826
+ return true;
8827
+ }
8669
8828
  }
8670
8829
 
8671
8830
  // Optimization: if the rule head is **structurally ground** (no vars anywhere, even inside
@@ -8865,6 +9024,10 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
8865
9024
 
8866
9025
  const outcome = __emitForwardRuleSolution(r, entry.ruleIndex, s);
8867
9026
  if (outcome.rulesChanged) {
9027
+ maxScopedClosurePriorityNeeded = Math.max(
9028
+ __computeMaxScopedClosurePriorityNeeded(forwardRules, backRules),
9029
+ __computeForwardRuleScopedStrata(forwardRules),
9030
+ );
8868
9031
  agendaIndex = makeSinglePremiseAgendaIndex(forwardRules, backRules);
8869
9032
  agendaCursor = 0;
8870
9033
  }
@@ -8878,7 +9041,7 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
8878
9041
  for (let i = 0; i < forwardRules.length; i++) {
8879
9042
  const r = forwardRules[i];
8880
9043
  if (agendaIndex.indexed.has(r)) continue;
8881
- if (r.__needsForwardSkipCheck && __skipForwardRuleNow(r)) continue;
9044
+ if ((r.__needsForwardSkipCheck || (r.__scopedStratumInfo && r.__scopedStratumInfo.needsSnap)) && __skipForwardRuleNow(r)) continue;
8882
9045
 
8883
9046
  const headIsStrictGround = r.__headIsStrictGround;
8884
9047
  const maxSols = r.isFuse || headIsStrictGround ? 1 : undefined;
@@ -8899,6 +9062,10 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
8899
9062
  for (const s of sols) {
8900
9063
  const outcome = __emitForwardRuleSolution(r, i, s);
8901
9064
  if (outcome.rulesChanged) {
9065
+ maxScopedClosurePriorityNeeded = Math.max(
9066
+ __computeMaxScopedClosurePriorityNeeded(forwardRules, backRules),
9067
+ __computeForwardRuleScopedStrata(forwardRules),
9068
+ );
8902
9069
  agendaIndex = makeSinglePremiseAgendaIndex(forwardRules, backRules);
8903
9070
  agendaCursor = 0;
8904
9071
  }
@@ -8926,8 +9093,8 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
8926
9093
  // Rules may have been added dynamically (rule-producing triples), possibly
8927
9094
  // introducing scoped builtins and/or higher closure priorities.
8928
9095
  maxScopedClosurePriorityNeeded = Math.max(
8929
- maxScopedClosurePriorityNeeded,
8930
9096
  __computeMaxScopedClosurePriorityNeeded(forwardRules, backRules),
9097
+ __computeForwardRuleScopedStrata(forwardRules),
8931
9098
  );
8932
9099
 
8933
9100
  // If there are no scoped builtins in the entire program, Phase B is pure
@@ -8945,8 +9112,8 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
8945
9112
 
8946
9113
  // Phase B can also derive rule-producing triples.
8947
9114
  maxScopedClosurePriorityNeeded = Math.max(
8948
- maxScopedClosurePriorityNeeded,
8949
9115
  __computeMaxScopedClosurePriorityNeeded(forwardRules, backRules),
9116
+ __computeForwardRuleScopedStrata(forwardRules),
8950
9117
  );
8951
9118
 
8952
9119
  if (!changedA && !changedB && scopedClosureLevel >= maxScopedClosurePriorityNeeded) break;
package/lib/engine.js CHANGED
@@ -484,6 +484,158 @@ function __varOccursElsewhereInPremise(premise, name, idx, field) {
484
484
  return false;
485
485
  }
486
486
 
487
+
488
+ function __scopedPriorityForTerm(t) {
489
+ if (t instanceof GraphTerm) return 0;
490
+ const p0 = __logNaturalPriorityFromTerm(t);
491
+ return p0 !== null && p0 >= 1 ? p0 : 1;
492
+ }
493
+
494
+ function __pushScopedQueryTriplesFromGraph(term, out) {
495
+ if (!(term instanceof GraphTerm)) return;
496
+ for (const tr of term.triples) out.push(tr);
497
+ }
498
+
499
+ function __analyzeForwardRuleScopedUse(rule) {
500
+ const out = {
501
+ needsSnap: false,
502
+ baseLevel: 0,
503
+ queryTriples: [],
504
+ hasScopedAggregate: false,
505
+ };
506
+
507
+ if (!rule || !Array.isArray(rule.premise)) return out;
508
+
509
+ for (let i = 0; i < rule.premise.length; i++) {
510
+ const tr = rule.premise[i];
511
+ if (!(tr && tr.p instanceof Iri)) continue;
512
+ const pv = tr.p.value;
513
+
514
+ if (pv === LOG_NS + 'includes' || pv === LOG_NS + 'notIncludes') {
515
+ // Explicit quoted scopes are local formulas, not snapshots of the global closure.
516
+ if (tr.s instanceof GraphTerm) continue;
517
+
518
+ // A scope variable that is bound by another premise may become an explicit GraphTerm.
519
+ // Still, if it remains unbound at runtime the builtin treats it as priority 1.
520
+ // We therefore record the scoped use, but only use quoted object triples for
521
+ // dependency analysis; variables inside the quoted object do not bind the scope.
522
+ out.needsSnap = true;
523
+ out.baseLevel = Math.max(out.baseLevel, __scopedPriorityForTerm(tr.s));
524
+ __pushScopedQueryTriplesFromGraph(tr.o, out.queryTriples);
525
+ continue;
526
+ }
527
+
528
+ if (pv === LOG_NS + 'collectAllIn') {
529
+ if (tr.o instanceof GraphTerm) continue;
530
+
531
+ out.needsSnap = true;
532
+ out.hasScopedAggregate = true;
533
+ out.baseLevel = Math.max(out.baseLevel, __scopedPriorityForTerm(tr.o));
534
+ if (tr.s instanceof ListTerm && tr.s.elems.length === 3) {
535
+ __pushScopedQueryTriplesFromGraph(tr.s.elems[1], out.queryTriples);
536
+ }
537
+ continue;
538
+ }
539
+
540
+ if (pv === LOG_NS + 'forAllIn') {
541
+ if (tr.o instanceof GraphTerm) continue;
542
+
543
+ out.needsSnap = true;
544
+ out.hasScopedAggregate = true;
545
+ out.baseLevel = Math.max(out.baseLevel, __scopedPriorityForTerm(tr.o));
546
+ if (tr.s instanceof ListTerm && tr.s.elems.length === 2) {
547
+ __pushScopedQueryTriplesFromGraph(tr.s.elems[0], out.queryTriples);
548
+ __pushScopedQueryTriplesFromGraph(tr.s.elems[1], out.queryTriples);
549
+ }
550
+ }
551
+ }
552
+
553
+ return out;
554
+ }
555
+
556
+ function __triplePatternsMayOverlap(a, b) {
557
+ return unifyTriple(a, b, __emptySubst()) !== null || unifyTriple(b, a, __emptySubst()) !== null;
558
+ }
559
+
560
+ function __ruleConclusionsMayFeedScopedQueries(producer, consumerMeta) {
561
+ if (!consumerMeta || !consumerMeta.queryTriples || consumerMeta.queryTriples.length === 0) return false;
562
+
563
+ // Dynamic conclusions are only known after a proof solution. Conservatively
564
+ // assume they can feed any scoped query if the producing rule is scoped.
565
+ if (producer && producer.__dynamicConclusionTerm) return true;
566
+
567
+ if (!producer || !Array.isArray(producer.conclusion) || producer.conclusion.length === 0) return false;
568
+ for (const q of consumerMeta.queryTriples) {
569
+ for (const h of producer.conclusion) {
570
+ if (__triplePatternsMayOverlap(q, h)) return true;
571
+ }
572
+ }
573
+ return false;
574
+ }
575
+
576
+ function __setForwardRuleScopedStratumInfo(rule, level) {
577
+ const value =
578
+ level > 0
579
+ ? { needsSnap: true, requiredLevel: level, exactLevel: true }
580
+ : { needsSnap: false, requiredLevel: 0, exactLevel: false };
581
+
582
+ if (!hasOwn.call(rule, '__scopedStratumInfo')) {
583
+ Object.defineProperty(rule, '__scopedStratumInfo', {
584
+ value,
585
+ enumerable: false,
586
+ writable: true,
587
+ configurable: true,
588
+ });
589
+ } else {
590
+ rule.__scopedStratumInfo = value;
591
+ }
592
+ }
593
+
594
+ function __computeForwardRuleScopedStrata(forwardRules) {
595
+ if (!Array.isArray(forwardRules) || forwardRules.length === 0) return 0;
596
+
597
+ const metas = forwardRules.map((r) => __analyzeForwardRuleScopedUse(r));
598
+ const levels = metas.map((m) => (m.needsSnap ? Math.max(1, m.baseLevel || 1) : 0));
599
+
600
+ let maxLevel = 0;
601
+ for (const lvl of levels) if (lvl > maxLevel) maxLevel = lvl;
602
+
603
+ // Stratify scoped rules by data dependency: if a scoped query can read facts
604
+ // produced by another scoped rule, the reader must run against a later frozen
605
+ // snapshot. This prevents non-monotonic scoped builtins such as collectAllIn
606
+ // from emitting an early result before lower strata have derived their facts.
607
+ const passLimit = Math.max(1, forwardRules.length) + maxLevel + 1;
608
+ for (let pass = 0; pass < passLimit; pass++) {
609
+ let changed = false;
610
+ for (let ci = 0; ci < forwardRules.length; ci++) {
611
+ if (!metas[ci].needsSnap || !metas[ci].hasScopedAggregate) continue;
612
+ let needed = levels[ci];
613
+ for (let pi = 0; pi < forwardRules.length; pi++) {
614
+ if (pi === ci) continue;
615
+ if (levels[pi] <= 0) continue;
616
+ if (metas[pi].hasScopedAggregate) continue;
617
+ if (!__ruleConclusionsMayFeedScopedQueries(forwardRules[pi], metas[ci])) continue;
618
+ needed = Math.max(needed, levels[pi] + 1);
619
+ }
620
+ if (needed !== levels[ci]) {
621
+ levels[ci] = needed;
622
+ if (needed > maxLevel) maxLevel = needed;
623
+ changed = true;
624
+ }
625
+ }
626
+ if (!changed) break;
627
+ }
628
+
629
+
630
+ maxLevel = 0;
631
+ for (let i = 0; i < forwardRules.length; i++) {
632
+ __setForwardRuleScopedStratumInfo(forwardRules[i], levels[i]);
633
+ if (levels[i] > maxLevel) maxLevel = levels[i];
634
+ }
635
+
636
+ return maxLevel;
637
+ }
638
+
487
639
  function __computeForwardRuleScopedSkipInfo(rule) {
488
640
  let needsSnap = false;
489
641
  let requiredLevel = 0;
@@ -1669,7 +1821,7 @@ function makeSinglePremiseAgendaIndex(forwardRules, backRules) {
1669
1821
  goalPredTid: goal.p instanceof Iri ? goal.p.__tid : null,
1670
1822
  goalSKey,
1671
1823
  goalOKey,
1672
- needsSkipCheck: !!r.__needsForwardSkipCheck,
1824
+ needsSkipCheck: !!(r.__needsForwardSkipCheck || (r.__scopedStratumInfo && r.__scopedStratumInfo.needsSnap)),
1673
1825
  fastSubjectVar,
1674
1826
  fastObjectVar,
1675
1827
  };
@@ -3117,7 +3269,10 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
3117
3269
  let scopedClosureLevel = 0;
3118
3270
 
3119
3271
  // Scan known rules for the maximum requested closure priority in scoped log:* goals.
3120
- let maxScopedClosurePriorityNeeded = __computeMaxScopedClosurePriorityNeeded(forwardRules, backRules);
3272
+ let maxScopedClosurePriorityNeeded = Math.max(
3273
+ __computeMaxScopedClosurePriorityNeeded(forwardRules, backRules),
3274
+ __computeForwardRuleScopedStrata(forwardRules),
3275
+ );
3121
3276
 
3122
3277
  // Conservative fast-skip for forward rules that cannot possibly succeed
3123
3278
  // until a scoped snapshot exists (or a given closure level is reached).
@@ -3171,12 +3326,16 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
3171
3326
  // until a snapshot exists (and a certain closure level is reached).
3172
3327
  // This prevents expensive proofs that will definitely fail in Phase A
3173
3328
  // and in early closure levels.
3174
- const info = r.__scopedSkipInfo;
3329
+ const info = r.__scopedStratumInfo || r.__scopedSkipInfo;
3175
3330
  if (info && info.needsSnap) {
3176
3331
  const snapHere = facts.__scopedSnapshot || null;
3177
3332
  const lvlHere = (facts && typeof facts.__scopedClosureLevel === 'number' && facts.__scopedClosureLevel) || 0;
3178
3333
  if (!snapHere) return true;
3179
- if (lvlHere < info.requiredLevel) return true;
3334
+ if (info.exactLevel) {
3335
+ if (lvlHere !== info.requiredLevel) return true;
3336
+ } else if (lvlHere < info.requiredLevel) {
3337
+ return true;
3338
+ }
3180
3339
  }
3181
3340
 
3182
3341
  // Optimization: if the rule head is **structurally ground** (no vars anywhere, even inside
@@ -3376,6 +3535,10 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
3376
3535
 
3377
3536
  const outcome = __emitForwardRuleSolution(r, entry.ruleIndex, s);
3378
3537
  if (outcome.rulesChanged) {
3538
+ maxScopedClosurePriorityNeeded = Math.max(
3539
+ __computeMaxScopedClosurePriorityNeeded(forwardRules, backRules),
3540
+ __computeForwardRuleScopedStrata(forwardRules),
3541
+ );
3379
3542
  agendaIndex = makeSinglePremiseAgendaIndex(forwardRules, backRules);
3380
3543
  agendaCursor = 0;
3381
3544
  }
@@ -3389,7 +3552,7 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
3389
3552
  for (let i = 0; i < forwardRules.length; i++) {
3390
3553
  const r = forwardRules[i];
3391
3554
  if (agendaIndex.indexed.has(r)) continue;
3392
- if (r.__needsForwardSkipCheck && __skipForwardRuleNow(r)) continue;
3555
+ if ((r.__needsForwardSkipCheck || (r.__scopedStratumInfo && r.__scopedStratumInfo.needsSnap)) && __skipForwardRuleNow(r)) continue;
3393
3556
 
3394
3557
  const headIsStrictGround = r.__headIsStrictGround;
3395
3558
  const maxSols = r.isFuse || headIsStrictGround ? 1 : undefined;
@@ -3410,6 +3573,10 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
3410
3573
  for (const s of sols) {
3411
3574
  const outcome = __emitForwardRuleSolution(r, i, s);
3412
3575
  if (outcome.rulesChanged) {
3576
+ maxScopedClosurePriorityNeeded = Math.max(
3577
+ __computeMaxScopedClosurePriorityNeeded(forwardRules, backRules),
3578
+ __computeForwardRuleScopedStrata(forwardRules),
3579
+ );
3413
3580
  agendaIndex = makeSinglePremiseAgendaIndex(forwardRules, backRules);
3414
3581
  agendaCursor = 0;
3415
3582
  }
@@ -3437,8 +3604,8 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
3437
3604
  // Rules may have been added dynamically (rule-producing triples), possibly
3438
3605
  // introducing scoped builtins and/or higher closure priorities.
3439
3606
  maxScopedClosurePriorityNeeded = Math.max(
3440
- maxScopedClosurePriorityNeeded,
3441
3607
  __computeMaxScopedClosurePriorityNeeded(forwardRules, backRules),
3608
+ __computeForwardRuleScopedStrata(forwardRules),
3442
3609
  );
3443
3610
 
3444
3611
  // If there are no scoped builtins in the entire program, Phase B is pure
@@ -3456,8 +3623,8 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */,
3456
3623
 
3457
3624
  // Phase B can also derive rule-producing triples.
3458
3625
  maxScopedClosurePriorityNeeded = Math.max(
3459
- maxScopedClosurePriorityNeeded,
3460
3626
  __computeMaxScopedClosurePriorityNeeded(forwardRules, backRules),
3627
+ __computeForwardRuleScopedStrata(forwardRules),
3461
3628
  );
3462
3629
 
3463
3630
  if (!changedA && !changedB && scopedClosureLevel >= maxScopedClosurePriorityNeeded) break;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eyeling",
3
- "version": "1.25.4",
3
+ "version": "1.25.5",
4
4
  "description": "A minimal Notation3 (N3) reasoner in JavaScript.",
5
5
  "main": "./index.js",
6
6
  "keywords": [
package/test/api.test.js CHANGED
@@ -2496,6 +2496,26 @@ _:b a ex:Person ; ex:name "B" .
2496
2496
  ],
2497
2497
  notExpect: [/:result\s+:sumX\s+"329"\^\^xsd:decimal\s*\./, /:result\s+:meanX\s+"47"\^\^xsd:decimal\s*\./],
2498
2498
  },
2499
+ {
2500
+ name: '244 regression: scoped collectAllIn is stratified after scoped notIncludes',
2501
+ opt: { proofComments: false },
2502
+ input: `@prefix : <http://example.org/> .
2503
+ @prefix log: <http://www.w3.org/2000/10/swap/log#> .
2504
+
2505
+ :a :x 1 .
2506
+ :b :x 1 .
2507
+
2508
+ { ?s :x 1 . ?X log:notIncludes { ?s :marked ?X } } => { ?s :marked true } .
2509
+ { ( ?s { ?s :marked true } ?all ) log:collectAllIn ?scope } => { :result :marked ?all } .
2510
+ `,
2511
+ expect: [
2512
+ /^:a\s+:marked\s+true\s*\./m,
2513
+ /^:b\s+:marked\s+true\s*\./m,
2514
+ /^:result\s+:marked\s+\(:a\s+:b\)\s*\./m,
2515
+ ],
2516
+ notExpect: [/^:result\s+:marked\s+\(\)\s*\./m],
2517
+ },
2518
+
2499
2519
  {
2500
2520
  name: '244 regression: log:dtlit recognizes shorthand numeric and boolean literals',
2501
2521
  opt: { proofComments: false },
@@ -1,499 +0,0 @@
1
- # ==========================================================================
2
- # The Library and the Path
3
- #
4
- # Source essay:
5
- # https://josd.github.io/the-library-and-the-path/
6
- #
7
- # What this file is trying to do
8
- # ------------------------------
9
- # The essay uses a philosophical image:
10
- #
11
- # • The "Library" is the space of many possible, internally coherent ways
12
- # things could be.
13
- # • The "Path" is the one realized history we actually inhabit.
14
- # • "Observership" is not a godlike view from outside the world. It is a
15
- # process inside the Path: systems make observations, leave records, and
16
- # those records constrain which earlier histories still make sense.
17
- #
18
- # This file translates that idea into semantic data and lightweight rules.
19
- # It is not a physics engine and not a proof of the essay. It is a readable,
20
- # machine-processable toy model inspired by the essay.
21
- #
22
- # Design choices
23
- # --------------
24
- # 1. The model uses five layers, echoing the essay's stacked descriptions:
25
- # quantum, thermodynamic, biological, cognitive, social
26
- # 2. The Library contains multiple possible states and branches.
27
- # 3. The realized Path selects one sequence of states.
28
- # 4. Observations create records.
29
- # 5. Records act as constraints on candidate histories.
30
- # ==========================================================================
31
-
32
- @prefix ex: <https://example.org/library-path#> .
33
- @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
34
- @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
35
- @prefix log: <http://www.w3.org/2000/10/swap/log#> .
36
-
37
- # ----------
38
- # Vocabulary
39
- # ----------
40
-
41
- # These are the main kinds of things in the model.
42
- ex:Library a rdfs:Class ; rdfs:label "Library" .
43
- ex:Layer a rdfs:Class ; rdfs:label "Layer of description" .
44
- ex:State a rdfs:Class ; rdfs:label "State" .
45
- ex:PossibleState a rdfs:Class ; rdfs:subClassOf ex:State ; rdfs:label "Possible state" .
46
- ex:RealizedState a rdfs:Class ; rdfs:subClassOf ex:State ; rdfs:label "Realized state" .
47
- ex:Path a rdfs:Class ; rdfs:label "Path" .
48
- ex:Transition a rdfs:Class ; rdfs:label "Transition" .
49
- ex:Observer a rdfs:Class ; rdfs:label "Observer" .
50
- ex:Observation a rdfs:Class ; rdfs:label "Observation" .
51
- ex:Record a rdfs:Class ; rdfs:label "Record" .
52
- ex:Constraint a rdfs:Class ; rdfs:label "Constraint" .
53
- ex:History a rdfs:Class ; rdfs:label "Candidate history" .
54
- ex:Theme a rdfs:Class ; rdfs:label "Theme" .
55
- ex:InterpretiveNote a rdfs:Class ; rdfs:label "Interpretive note" .
56
-
57
- # Properties. Their names are chosen to read like plain English.
58
- ex:containsLayer a rdf:Property .
59
- ex:containsState a rdf:Property .
60
- ex:containsHistory a rdf:Property .
61
- ex:throughLibrary a rdf:Property .
62
- ex:hasState a rdf:Property .
63
- ex:startsAt a rdf:Property .
64
- ex:endsAt a rdf:Property .
65
- ex:nextState a rdf:Property .
66
- ex:branchesTo a rdf:Property .
67
- ex:atLayer a rdf:Property .
68
- ex:timeIndex a rdf:Property .
69
- ex:describedByTheme a rdf:Property .
70
- ex:hasTransition a rdf:Property .
71
- ex:fromState a rdf:Property .
72
- ex:toState a rdf:Property .
73
- ex:transitionType a rdf:Property .
74
- ex:observedBy a rdf:Property .
75
- ex:makesObservation a rdf:Property .
76
- ex:atState a rdf:Property .
77
- ex:writesRecord a rdf:Property .
78
- ex:aboutState a rdf:Property .
79
- ex:hasRecord a rdf:Property .
80
- ex:hasConstraint a rdf:Property .
81
- ex:matchesRecord a rdf:Property .
82
- ex:supportsConstraint a rdf:Property .
83
- ex:compatibleWith a rdf:Property .
84
- ex:incompatibleWith a rdf:Property .
85
- ex:memberState a rdf:Property .
86
- ex:realizes a rdf:Property .
87
- ex:selectsFrom a rdf:Property .
88
- ex:plainEnglish a rdf:Property .
89
- ex:description a rdf:Property .
90
- ex:glosses a rdf:Property .
91
- ex:notes a rdf:Property .
92
- ex:focusesOn a rdf:Property .
93
-
94
- # -------------------
95
- # High-level concepts
96
- # -------------------
97
-
98
- ex:theLibrary a ex:Library ;
99
- rdfs:label "The Library" ;
100
- ex:description "The timeless space of structured possibilities." ;
101
- ex:plainEnglish "Think of this as the set of coherent ways the world could be, not yet reduced to one lived history." ;
102
- ex:containsLayer ex:quantum, ex:thermodynamic, ex:biological, ex:cognitive, ex:social .
103
-
104
- ex:realizedPath a ex:Path ;
105
- rdfs:label "The realized Path" ;
106
- ex:throughLibrary ex:theLibrary ;
107
- ex:selectsFrom ex:theLibrary ;
108
- ex:description "One actual history through the Library." ;
109
- ex:plainEnglish "Out of many possibilities, this is the single unfolding sequence that becomes lived reality." .
110
-
111
- ex:observer a ex:Observer ;
112
- rdfs:label "Observership" ;
113
- ex:description "Perspective from within the Path that creates records." ;
114
- ex:plainEnglish "The observer is not outside the universe. Observation happens inside the realized history and leaves traces behind." .
115
-
116
- # -----------------
117
- # Layers and themes
118
- # -----------------
119
-
120
- # Each layer is a way of describing the same broad reality at a different scale
121
- # or level of organization.
122
- ex:quantum a ex:Layer ;
123
- rdfs:label "Quantum" ;
124
- ex:plainEnglish "Microscopic possibilities and amplitudes." ;
125
- ex:describedByTheme ex:themePossibility .
126
-
127
- ex:thermodynamic a ex:Layer ;
128
- rdfs:label "Thermodynamic" ;
129
- ex:plainEnglish "Flows, gradients, dissipation, and the arrow of time." ;
130
- ex:describedByTheme ex:themeIrreversibility .
131
-
132
- ex:biological a ex:Layer ;
133
- rdfs:label "Biological" ;
134
- ex:plainEnglish "Living systems that preserve structure and memory across generations." ;
135
- ex:describedByTheme ex:themePersistence .
136
-
137
- ex:cognitive a ex:Layer ;
138
- rdfs:label "Cognitive" ;
139
- ex:plainEnglish "Internal models, recall, anticipation, and interpretation." ;
140
- ex:describedByTheme ex:themeModeling .
141
-
142
- ex:social a ex:Layer ;
143
- rdfs:label "Social" ;
144
- ex:plainEnglish "Language, institutions, archives, and public memory." ;
145
- ex:describedByTheme ex:themeSharedRecord .
146
-
147
- ex:themePossibility a ex:Theme ;
148
- rdfs:label "Possibility" ;
149
- ex:glosses "Many coherent alternatives can exist in description before one history is lived." .
150
-
151
- ex:themeIrreversibility a ex:Theme ;
152
- rdfs:label "Irreversibility" ;
153
- ex:glosses "Records and gradients make the past different from the future." .
154
-
155
- ex:themePersistence a ex:Theme ;
156
- rdfs:label "Persistence" ;
157
- ex:glosses "Some structures endure and transmit form." .
158
-
159
- ex:themeModeling a ex:Theme ;
160
- rdfs:label "Modeling" ;
161
- ex:glosses "Minds encode and use internal representations." .
162
-
163
- ex:themeSharedRecord a ex:Theme ;
164
- rdfs:label "Shared record" ;
165
- ex:glosses "Groups stabilize memory in symbols, texts, norms, and institutions." .
166
-
167
- # ---------------------
168
- # States in the Library
169
- # ---------------------
170
-
171
- # A state is not meant to be a complete physical description of everything.
172
- # It is a compact marker for a stage in a possible history.
173
-
174
- # ---- Realized states ----
175
- ex:s0 a ex:PossibleState, ex:RealizedState ;
176
- rdfs:label "s0" ;
177
- ex:timeIndex 0 ;
178
- ex:atLayer ex:quantum ;
179
- ex:description "Initial low-information possibility." ;
180
- ex:plainEnglish "A starting point containing many open possibilities." .
181
-
182
- ex:s1 a ex:PossibleState, ex:RealizedState ;
183
- rdfs:label "s1" ;
184
- ex:timeIndex 1 ;
185
- ex:atLayer ex:thermodynamic ;
186
- ex:description "Entropy gradient becomes a usable arrow." ;
187
- ex:plainEnglish "A directionality appears: some traces can now accumulate rather than instantly disappear." .
188
-
189
- ex:s2 a ex:PossibleState, ex:RealizedState ;
190
- rdfs:label "s2" ;
191
- ex:timeIndex 2 ;
192
- ex:atLayer ex:biological ;
193
- ex:description "A living lineage persists and stores traces." ;
194
- ex:plainEnglish "Something like heredity or durable adaptation now carries information forward." .
195
-
196
- ex:s3 a ex:PossibleState, ex:RealizedState ;
197
- rdfs:label "s3" ;
198
- ex:timeIndex 3 ;
199
- ex:atLayer ex:cognitive ;
200
- ex:description "A mind models the world and remembers." ;
201
- ex:plainEnglish "Memory becomes explicit enough to support interpretation and anticipation." .
202
-
203
- ex:s4 a ex:PossibleState, ex:RealizedState ;
204
- rdfs:label "s4" ;
205
- ex:timeIndex 4 ;
206
- ex:atLayer ex:social ;
207
- ex:description "Shared language stabilizes public records." ;
208
- ex:plainEnglish "Memory is no longer only private. It becomes collective and institutional." .
209
-
210
- # ---- Alternative states not taken on the realized Path ----
211
- ex:s1a a ex:PossibleState ;
212
- rdfs:label "s1a" ;
213
- ex:timeIndex 1 ;
214
- ex:atLayer ex:thermodynamic ;
215
- ex:description "Thermal structure dissipates too quickly." ;
216
- ex:plainEnglish "The world remains too fleeting to support durable traces." .
217
-
218
- ex:s2a a ex:PossibleState ;
219
- rdfs:label "s2a" ;
220
- ex:timeIndex 2 ;
221
- ex:atLayer ex:biological ;
222
- ex:description "No robust biological memory emerges." ;
223
- ex:plainEnglish "There are local structures, but nothing that preserves adaptive information well." .
224
-
225
- ex:s3a a ex:PossibleState ;
226
- rdfs:label "s3a" ;
227
- ex:timeIndex 3 ;
228
- ex:atLayer ex:cognitive ;
229
- ex:description "Local cognition forms, but no durable self-model." ;
230
- ex:plainEnglish "Some processing happens, but not enough stable reflective memory to anchor a long narrative." .
231
-
232
- ex:s4a a ex:PossibleState ;
233
- rdfs:label "s4a" ;
234
- ex:timeIndex 4 ;
235
- ex:atLayer ex:social ;
236
- ex:description "Agents fail to coordinate stable communal archives." ;
237
- ex:plainEnglish "There may be minds, but no lasting shared record of them." .
238
-
239
- ex:theLibrary
240
- ex:containsState ex:s0, ex:s1, ex:s2, ex:s3, ex:s4,
241
- ex:s1a, ex:s2a, ex:s3a, ex:s4a .
242
-
243
- # -------------------
244
- # Branching structure
245
- # -------------------
246
-
247
- # The Library contains branches. The Path selects one branch.
248
- ex:s0 ex:branchesTo ex:s1, ex:s1a .
249
- ex:s1 ex:branchesTo ex:s2, ex:s2a .
250
- ex:s1a ex:branchesTo ex:s2a .
251
- ex:s2 ex:branchesTo ex:s3, ex:s3a .
252
- ex:s2a ex:branchesTo ex:s3a .
253
- ex:s3 ex:branchesTo ex:s4, ex:s4a .
254
- ex:s3a ex:branchesTo ex:s4a .
255
-
256
- # The single realized history.
257
-
258
- ex:realizedPath
259
- ex:hasState ex:s0, ex:s1, ex:s2, ex:s3, ex:s4 ;
260
- ex:startsAt ex:s0 ;
261
- ex:endsAt ex:s4 ;
262
- ex:notes "This is the one branch counted as lived reality in the model." .
263
-
264
- ex:s0 ex:nextState ex:s1 .
265
- ex:s1 ex:nextState ex:s2 .
266
- ex:s2 ex:nextState ex:s3 .
267
- ex:s3 ex:nextState ex:s4 .
268
-
269
- # -----------
270
- # Transitions
271
- # -----------
272
-
273
- # Transitions make the path easier to read for humans.
274
- ex:t01 a ex:Transition ;
275
- ex:fromState ex:s0 ;
276
- ex:toState ex:s1 ;
277
- ex:transitionType "selection into durable asymmetry" ;
278
- ex:description "A world with usable directional structure emerges." .
279
-
280
- ex:t12 a ex:Transition ;
281
- ex:fromState ex:s1 ;
282
- ex:toState ex:s2 ;
283
- ex:transitionType "persistence into adaptation" ;
284
- ex:description "Energy gradients support living systems that keep information." .
285
-
286
- ex:t23 a ex:Transition ;
287
- ex:fromState ex:s2 ;
288
- ex:toState ex:s3 ;
289
- ex:transitionType "adaptation into modeling" ;
290
- ex:description "Biological persistence develops into explicit internal world-models." .
291
-
292
- ex:t34 a ex:Transition ;
293
- ex:fromState ex:s3 ;
294
- ex:toState ex:s4 ;
295
- ex:transitionType "private memory into public memory" ;
296
- ex:description "Symbols and institutions stabilize records beyond individual minds." .
297
-
298
- ex:realizedPath ex:hasTransition ex:t01, ex:t12, ex:t23, ex:t34 .
299
-
300
- # ------------------------
301
- # Observations and records
302
- # ------------------------
303
-
304
- # In this model, observations produce records. Records are important because
305
- # they constrain which histories remain plausible from the current standpoint.
306
- ex:obs1 a ex:Observation ;
307
- rdfs:label "Observation 1" ;
308
- ex:atState ex:s1 ;
309
- ex:writesRecord ex:rec1 ;
310
- ex:description "A trace of an irreversible thermodynamic arrow is registered." .
311
-
312
- ex:obs2 a ex:Observation ;
313
- rdfs:label "Observation 2" ;
314
- ex:atState ex:s2 ;
315
- ex:writesRecord ex:rec2 ;
316
- ex:description "A lineage preserves information across generations." .
317
-
318
- ex:obs3 a ex:Observation ;
319
- rdfs:label "Observation 3" ;
320
- ex:atState ex:s3 ;
321
- ex:writesRecord ex:rec3 ;
322
- ex:description "A system forms a recall-capable internal model." .
323
-
324
- ex:obs4 a ex:Observation ;
325
- rdfs:label "Observation 4" ;
326
- ex:atState ex:s4 ;
327
- ex:writesRecord ex:rec4 ;
328
- ex:description "A social archive stores memory in a shared symbolic form." .
329
-
330
- ex:observer ex:makesObservation ex:obs1, ex:obs2, ex:obs3, ex:obs4 .
331
-
332
- # The records themselves are also constraints.
333
- ex:rec1 a ex:Record, ex:Constraint ;
334
- rdfs:label "Record of thermodynamic arrow" ;
335
- ex:aboutState ex:s1 ;
336
- ex:description "There exists a stable thermodynamic arrow." ;
337
- ex:plainEnglish "The present contains traces that imply irreversible structure in the past." .
338
-
339
- ex:rec2 a ex:Record, ex:Constraint ;
340
- rdfs:label "Record of biological memory" ;
341
- ex:aboutState ex:s2 ;
342
- ex:description "There exists heritable biological memory." ;
343
- ex:plainEnglish "Current life carries evidence of prior successful information storage." .
344
-
345
- ex:rec3 a ex:Record, ex:Constraint ;
346
- rdfs:label "Record of cognitive memory" ;
347
- ex:aboutState ex:s3 ;
348
- ex:description "There exists an internal model with recall." ;
349
- ex:plainEnglish "A remembering mind implies a history rich enough to support its memory traces." .
350
-
351
- ex:rec4 a ex:Record, ex:Constraint ;
352
- rdfs:label "Record of social memory" ;
353
- ex:aboutState ex:s4 ;
354
- ex:description "There exists shared symbolic culture." ;
355
- ex:plainEnglish "Texts, rituals, institutions, and archives act as public memory." .
356
-
357
- # The realized path is constrained by the records visible from within it.
358
- ex:realizedPath ex:hasConstraint ex:rec1, ex:rec2, ex:rec3, ex:rec4 .
359
-
360
- # -------------------
361
- # Candidate histories
362
- # -------------------
363
-
364
- # These are simplified rival histories the present might consider.
365
- # The key idea is that not all candidate pasts fit the records we now have.
366
- ex:historyAlpha a ex:History ;
367
- rdfs:label "Alpha — realized history" ;
368
- ex:memberState ex:s0, ex:s1, ex:s2, ex:s3, ex:s4 ;
369
- ex:description "The full branch that supports all records in the model." ;
370
- ex:matchesRecord ex:rec1, ex:rec2, ex:rec3, ex:rec4 .
371
-
372
- ex:historyBeta a ex:History ;
373
- rdfs:label "Beta — dissipative branch" ;
374
- ex:memberState ex:s0, ex:s1a, ex:s2a, ex:s3a, ex:s4a ;
375
- ex:description "A branch where structure dissipates before rich memory can stabilize." ;
376
- ex:matchesRecord ex:rec1 .
377
-
378
- ex:historyGamma a ex:History ;
379
- rdfs:label "Gamma — biology without durable public memory" ;
380
- ex:memberState ex:s0, ex:s1, ex:s2, ex:s3a, ex:s4a ;
381
- ex:description "A branch with persistence and some adaptation, but no robust reflective or social archive." ;
382
- ex:matchesRecord ex:rec1, ex:rec2 .
383
-
384
- ex:historyDelta a ex:History ;
385
- rdfs:label "Delta — private minds without stable institutions" ;
386
- ex:memberState ex:s0, ex:s1, ex:s2, ex:s3, ex:s4a ;
387
- ex:description "A branch where memory reaches minds but does not stabilize socially." ;
388
- ex:matchesRecord ex:rec1, ex:rec2, ex:rec3 .
389
-
390
- ex:theLibrary ex:containsHistory ex:historyAlpha, ex:historyBeta, ex:historyGamma, ex:historyDelta .
391
-
392
- # ------------------
393
- # Interpretive notes
394
- # ------------------
395
-
396
- # These are not required for reasoning, but they make the file more readable.
397
- ex:note1 a ex:InterpretiveNote ;
398
- ex:focusesOn ex:theLibrary ;
399
- ex:plainEnglish "The Library is not 'before time' in a literal physical sense here; it is a conceptual space of possibilities." .
400
-
401
- ex:note2 a ex:InterpretiveNote ;
402
- ex:focusesOn ex:realizedPath ;
403
- ex:plainEnglish "The Path is the one sequence that becomes concrete from the inside." .
404
-
405
- ex:note3 a ex:InterpretiveNote ;
406
- ex:focusesOn ex:observer ;
407
- ex:plainEnglish "Observership is modeled as record production, not as magical outside access to all possibilities." .
408
-
409
- ex:note4 a ex:InterpretiveNote ;
410
- ex:focusesOn ex:rec1 ;
411
- ex:plainEnglish "A record is evidence now that narrows what could reasonably count as the past." .
412
-
413
- # -----
414
- # Rules
415
- # -----
416
-
417
- # These rules are intentionally simple and didactic.
418
-
419
- # Rule 1
420
- # If an observation is at a state and writes a record,
421
- # then that state has that record.
422
- { ?obs a ex:Observation ;
423
- ex:atState ?s ;
424
- ex:writesRecord ?r . }
425
- =>
426
- { ?s ex:hasRecord ?r .
427
- ?r a ex:Record . } .
428
-
429
- # Rule 2
430
- # If the observer makes an observation at a state that lies on the realized path,
431
- # then that state is observed by that observer.
432
- { ?who ex:makesObservation ?obs .
433
- ?obs ex:atState ?s .
434
- ex:realizedPath ex:hasState ?s . }
435
- =>
436
- { ?s ex:observedBy ?who . } .
437
-
438
- # Rule 3
439
- # If a history matches a record that is one of the realized path's constraints,
440
- # then the history supports that constraint.
441
- { ex:realizedPath ex:hasConstraint ?r .
442
- ?h a ex:History ;
443
- ex:matchesRecord ?r . }
444
- =>
445
- { ?h ex:supportsConstraint ?r . } .
446
-
447
- # Rule 4
448
- # Second-stage compatibility check.
449
- # Inspect closure gate 2, where record-level incompatibilities have already
450
- # been derived. A history is path-compatible only if no incompatibility is present.
451
- {
452
- ?h a ex:History .
453
- 2 log:notIncludes { ?h ex:incompatibleWith ?any } .
454
- }
455
- =>
456
- { ?h ex:compatibleWith ex:realizedPath . } .
457
-
458
- # Rule 5
459
- # First-stage record failure check.
460
- # Inspect closure gate 1 to see whether a history supports a realized-path
461
- # constraint. If it does not, derive incompatibility with that record.
462
- {
463
- ex:realizedPath ex:hasConstraint ?r .
464
- ?h a ex:History .
465
- 1 log:notIncludes { ?h ex:supportsConstraint ?r } .
466
- }
467
- =>
468
- { ?h ex:incompatibleWith ?r . } .
469
-
470
- # Rule 6
471
- # If a state has a record and belongs to the realized path,
472
- # then the realized path realizes that record-bearing state.
473
- { ex:realizedPath ex:hasState ?s .
474
- ?s ex:hasRecord ?r . }
475
- =>
476
- { ex:realizedPath ex:realizes ?s . } .
477
-
478
- # ----------------------------
479
- # Human-readable summary nodes
480
- # ----------------------------
481
-
482
- ex:summary a rdfs:Resource ;
483
- rdfs:label "Plain-language summary" ;
484
- ex:description "The Library contains many coherent alternatives. The Path is one realized sequence through them. Observation happens within the Path and leaves records. Those records narrow which candidate histories remain compatible with what is now observed." .
485
-
486
- ex:readingGuide a rdfs:Resource ;
487
- rdfs:label "Where to look first" ;
488
- ex:description "Start with ex:theLibrary, ex:realizedPath, ex:observer, then read the states s0..s4, then the records rec1..rec4, then the candidate histories." .
489
-
490
- {
491
- 3 log:includes {
492
- ?s a rdfs:Resource ;
493
- rdfs:label ?l ;
494
- ex:description ?d .
495
- }
496
- }
497
- =>
498
- { ?l ex:is ?d . } .
499
-
@@ -1,33 +0,0 @@
1
- @prefix ex: <https://example.org/library-path#> .
2
-
3
- ex:s1 ex:hasRecord ex:rec1 .
4
- ex:s2 ex:hasRecord ex:rec2 .
5
- ex:s3 ex:hasRecord ex:rec3 .
6
- ex:s4 ex:hasRecord ex:rec4 .
7
- ex:s1 ex:observedBy ex:observer .
8
- ex:s2 ex:observedBy ex:observer .
9
- ex:s3 ex:observedBy ex:observer .
10
- ex:s4 ex:observedBy ex:observer .
11
- ex:historyAlpha ex:supportsConstraint ex:rec1 .
12
- ex:historyBeta ex:supportsConstraint ex:rec1 .
13
- ex:historyGamma ex:supportsConstraint ex:rec1 .
14
- ex:historyDelta ex:supportsConstraint ex:rec1 .
15
- ex:historyAlpha ex:supportsConstraint ex:rec2 .
16
- ex:historyGamma ex:supportsConstraint ex:rec2 .
17
- ex:historyDelta ex:supportsConstraint ex:rec2 .
18
- ex:historyAlpha ex:supportsConstraint ex:rec3 .
19
- ex:historyDelta ex:supportsConstraint ex:rec3 .
20
- ex:historyAlpha ex:supportsConstraint ex:rec4 .
21
- ex:realizedPath ex:realizes ex:s1 .
22
- ex:realizedPath ex:realizes ex:s2 .
23
- ex:realizedPath ex:realizes ex:s3 .
24
- ex:realizedPath ex:realizes ex:s4 .
25
- ex:historyBeta ex:incompatibleWith ex:rec2 .
26
- ex:historyBeta ex:incompatibleWith ex:rec3 .
27
- ex:historyGamma ex:incompatibleWith ex:rec3 .
28
- ex:historyBeta ex:incompatibleWith ex:rec4 .
29
- ex:historyGamma ex:incompatibleWith ex:rec4 .
30
- ex:historyDelta ex:incompatibleWith ex:rec4 .
31
- ex:historyAlpha ex:compatibleWith ex:realizedPath .
32
- "Plain-language summary" ex:is "The Library contains many coherent alternatives. The Path is one realized sequence through them. Observation happens within the Path and leaves records. Those records narrow which candidate histories remain compatible with what is now observed." .
33
- "Where to look first" ex:is "Start with ex:theLibrary, ex:realizedPath, ex:observer, then read the states s0..s4, then the records rec1..rec4, then the candidate histories." .