@polintpro/proposit-core 0.5.1 → 0.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -7
- package/dist/cli/commands/sources.d.ts.map +1 -1
- package/dist/cli/commands/sources.js +13 -66
- package/dist/cli/commands/sources.js.map +1 -1
- package/dist/cli/commands/variables.d.ts.map +1 -1
- package/dist/cli/commands/variables.js +40 -4
- package/dist/cli/commands/variables.js.map +1 -1
- package/dist/cli/config.d.ts +0 -2
- package/dist/cli/config.d.ts.map +1 -1
- package/dist/cli/config.js +0 -6
- package/dist/cli/config.js.map +1 -1
- package/dist/cli/engine.d.ts.map +1 -1
- package/dist/cli/engine.js +8 -35
- package/dist/cli/engine.js.map +1 -1
- package/dist/cli/import.d.ts.map +1 -1
- package/dist/cli/import.js +7 -6
- package/dist/cli/import.js.map +1 -1
- package/dist/cli/schemata.d.ts +0 -8
- package/dist/cli/schemata.d.ts.map +1 -1
- package/dist/cli/schemata.js +0 -10
- package/dist/cli/schemata.js.map +1 -1
- package/dist/cli/storage/sources.d.ts +3 -10
- package/dist/cli/storage/sources.d.ts.map +1 -1
- package/dist/cli/storage/sources.js +11 -94
- package/dist/cli/storage/sources.js.map +1 -1
- package/dist/lib/consts.d.ts.map +1 -1
- package/dist/lib/consts.js +11 -19
- package/dist/lib/consts.js.map +1 -1
- package/dist/lib/core/argument-engine.d.ts +16 -28
- package/dist/lib/core/argument-engine.d.ts.map +1 -1
- package/dist/lib/core/argument-engine.js +241 -247
- package/dist/lib/core/argument-engine.js.map +1 -1
- package/dist/lib/core/change-collector.d.ts +0 -7
- package/dist/lib/core/change-collector.d.ts.map +1 -1
- package/dist/lib/core/change-collector.js +0 -18
- package/dist/lib/core/change-collector.js.map +1 -1
- package/dist/lib/core/claim-library.d.ts +27 -0
- package/dist/lib/core/claim-library.d.ts.map +1 -0
- package/dist/lib/core/{assertion-library.js → claim-library.js} +15 -15
- package/dist/lib/core/claim-library.js.map +1 -0
- package/dist/lib/core/claim-source-library.d.ts +27 -0
- package/dist/lib/core/claim-source-library.d.ts.map +1 -0
- package/dist/lib/core/claim-source-library.js +116 -0
- package/dist/lib/core/claim-source-library.js.map +1 -0
- package/dist/lib/core/diff.d.ts +3 -7
- package/dist/lib/core/diff.d.ts.map +1 -1
- package/dist/lib/core/diff.js +14 -79
- package/dist/lib/core/diff.js.map +1 -1
- package/dist/lib/core/interfaces/argument-engine.interfaces.d.ts +31 -10
- package/dist/lib/core/interfaces/argument-engine.interfaces.d.ts.map +1 -1
- package/dist/lib/core/interfaces/index.d.ts +1 -2
- package/dist/lib/core/interfaces/index.d.ts.map +1 -1
- package/dist/lib/core/interfaces/library.interfaces.d.ts +25 -8
- package/dist/lib/core/interfaces/library.interfaces.d.ts.map +1 -1
- package/dist/lib/core/interfaces/premise-engine.interfaces.d.ts +1 -29
- package/dist/lib/core/interfaces/premise-engine.interfaces.d.ts.map +1 -1
- package/dist/lib/core/premise-engine.d.ts +7 -8
- package/dist/lib/core/premise-engine.d.ts.map +1 -1
- package/dist/lib/core/premise-engine.js +66 -82
- package/dist/lib/core/premise-engine.js.map +1 -1
- package/dist/lib/core/variable-manager.d.ts +3 -4
- package/dist/lib/core/variable-manager.d.ts.map +1 -1
- package/dist/lib/core/variable-manager.js +11 -1
- package/dist/lib/core/variable-manager.js.map +1 -1
- package/dist/lib/index.d.ts +3 -4
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js +3 -3
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/schemata/claim.d.ts +9 -0
- package/dist/lib/schemata/claim.d.ts.map +1 -0
- package/dist/lib/schemata/{assertion.js → claim.js} +4 -4
- package/dist/lib/schemata/claim.js.map +1 -0
- package/dist/lib/schemata/index.d.ts +1 -1
- package/dist/lib/schemata/index.d.ts.map +1 -1
- package/dist/lib/schemata/index.js +1 -1
- package/dist/lib/schemata/index.js.map +1 -1
- package/dist/lib/schemata/propositional.d.ts +37 -3
- package/dist/lib/schemata/propositional.d.ts.map +1 -1
- package/dist/lib/schemata/propositional.js +32 -6
- package/dist/lib/schemata/propositional.js.map +1 -1
- package/dist/lib/schemata/source.d.ts +4 -16
- package/dist/lib/schemata/source.d.ts.map +1 -1
- package/dist/lib/schemata/source.js +7 -17
- package/dist/lib/schemata/source.js.map +1 -1
- package/dist/lib/types/checksum.d.ts +4 -6
- package/dist/lib/types/checksum.d.ts.map +1 -1
- package/dist/lib/types/diff.d.ts +1 -5
- package/dist/lib/types/diff.d.ts.map +1 -1
- package/dist/lib/types/evaluation.d.ts +1 -1
- package/dist/lib/types/evaluation.d.ts.map +1 -1
- package/dist/lib/types/mutation.d.ts +0 -3
- package/dist/lib/types/mutation.d.ts.map +1 -1
- package/dist/lib/types/reactive.d.ts +1 -3
- package/dist/lib/types/reactive.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/lib/core/assertion-library.d.ts +0 -27
- package/dist/lib/core/assertion-library.d.ts.map +0 -1
- package/dist/lib/core/assertion-library.js.map +0 -1
- package/dist/lib/core/interfaces/source-management.interfaces.d.ts +0 -63
- package/dist/lib/core/interfaces/source-management.interfaces.d.ts.map +0 -1
- package/dist/lib/core/interfaces/source-management.interfaces.js +0 -2
- package/dist/lib/core/interfaces/source-management.interfaces.js.map +0 -1
- package/dist/lib/core/source-manager.d.ts +0 -38
- package/dist/lib/core/source-manager.d.ts.map +0 -1
- package/dist/lib/core/source-manager.js +0 -266
- package/dist/lib/core/source-manager.js.map +0 -1
- package/dist/lib/schemata/assertion.d.ts +0 -9
- package/dist/lib/schemata/assertion.d.ts.map +0 -1
- package/dist/lib/schemata/assertion.js.map +0 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { isClaimBound, isPremiseBound, } from "../schemata/index.js";
|
|
2
3
|
import { DEFAULT_CHECKSUM_CONFIG } from "../consts.js";
|
|
3
4
|
import { getOrCreate, sortedUnique } from "../utils/collections.js";
|
|
4
5
|
import { ChangeCollector } from "./change-collector.js";
|
|
@@ -7,7 +8,6 @@ import { kleeneAnd, kleeneNot } from "./evaluation/kleene.js";
|
|
|
7
8
|
import { makeErrorIssue, makeValidationResult, } from "./evaluation/validation.js";
|
|
8
9
|
import { PremiseEngine } from "./premise-engine.js";
|
|
9
10
|
import { VariableManager } from "./variable-manager.js";
|
|
10
|
-
import { SourceManager } from "./source-manager.js";
|
|
11
11
|
/**
|
|
12
12
|
* Manages a propositional logic argument composed of premises, variable
|
|
13
13
|
* assignments, and logical roles (supporting premises and a conclusion).
|
|
@@ -19,9 +19,9 @@ export class ArgumentEngine {
|
|
|
19
19
|
argument;
|
|
20
20
|
premises;
|
|
21
21
|
variables;
|
|
22
|
-
|
|
23
|
-
assertionLibrary;
|
|
22
|
+
claimLibrary;
|
|
24
23
|
sourceLibrary;
|
|
24
|
+
claimSourceLibrary;
|
|
25
25
|
conclusionPremiseId;
|
|
26
26
|
checksumConfig;
|
|
27
27
|
positionConfig;
|
|
@@ -33,15 +33,15 @@ export class ArgumentEngine {
|
|
|
33
33
|
argument: true,
|
|
34
34
|
variables: true,
|
|
35
35
|
roles: true,
|
|
36
|
-
sources: true,
|
|
37
36
|
premiseIds: new Set(),
|
|
38
37
|
allPremises: true,
|
|
39
38
|
};
|
|
40
39
|
cachedReactiveSnapshot;
|
|
41
|
-
constructor(argument,
|
|
40
|
+
constructor(argument, claimLibrary, sourceLibrary, claimSourceLibrary, options) {
|
|
42
41
|
this.argument = { ...argument };
|
|
43
|
-
this.
|
|
42
|
+
this.claimLibrary = claimLibrary;
|
|
44
43
|
this.sourceLibrary = sourceLibrary;
|
|
44
|
+
this.claimSourceLibrary = claimSourceLibrary;
|
|
45
45
|
this.premises = new Map();
|
|
46
46
|
this.checksumConfig = options?.checksumConfig;
|
|
47
47
|
this.positionConfig = options?.positionConfig;
|
|
@@ -49,10 +49,54 @@ export class ArgumentEngine {
|
|
|
49
49
|
checksumConfig: this.checksumConfig,
|
|
50
50
|
positionConfig: this.positionConfig,
|
|
51
51
|
});
|
|
52
|
-
this.sourceManager = new SourceManager();
|
|
53
52
|
this.expressionIndex = new Map();
|
|
54
53
|
this.conclusionPremiseId = undefined;
|
|
55
54
|
}
|
|
55
|
+
createCircularityCheck() {
|
|
56
|
+
return (variableId, targetPremiseId) => {
|
|
57
|
+
return this.wouldCreateCycle(variableId, targetPremiseId, new Set());
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
wouldCreateCycle(variableId, targetPremiseId, visited) {
|
|
61
|
+
const variable = this.variables.getVariable(variableId);
|
|
62
|
+
if (!variable)
|
|
63
|
+
return false;
|
|
64
|
+
if (!isPremiseBound(variable))
|
|
65
|
+
return false;
|
|
66
|
+
const bound = variable;
|
|
67
|
+
if (bound.boundPremiseId === targetPremiseId)
|
|
68
|
+
return true;
|
|
69
|
+
if (visited.size >= this.premises.size) {
|
|
70
|
+
throw new Error(`Circularity check depth limit exceeded (visited ${visited.size} premises).`);
|
|
71
|
+
}
|
|
72
|
+
if (visited.has(bound.boundPremiseId))
|
|
73
|
+
return false;
|
|
74
|
+
visited.add(bound.boundPremiseId);
|
|
75
|
+
const boundPremise = this.premises.get(bound.boundPremiseId);
|
|
76
|
+
if (!boundPremise)
|
|
77
|
+
return false;
|
|
78
|
+
for (const expr of boundPremise.getExpressions()) {
|
|
79
|
+
if (expr.type === "variable") {
|
|
80
|
+
if (this.wouldCreateCycle(expr.variableId, targetPremiseId, visited)) {
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
wireCircularityCheck(pm) {
|
|
88
|
+
pm.setCircularityCheck(this.createCircularityCheck());
|
|
89
|
+
}
|
|
90
|
+
wireEmptyBoundPremiseCheck(pm) {
|
|
91
|
+
pm.setEmptyBoundPremiseCheck((variableId) => {
|
|
92
|
+
const v = this.variables.getVariable(variableId);
|
|
93
|
+
if (!v ||
|
|
94
|
+
!isPremiseBound(v))
|
|
95
|
+
return false;
|
|
96
|
+
const boundPremise = this.premises.get(v.boundPremiseId);
|
|
97
|
+
return !boundPremise?.getRootExpressionId();
|
|
98
|
+
});
|
|
99
|
+
}
|
|
56
100
|
subscribe = (listener) => {
|
|
57
101
|
this.listeners.add(listener);
|
|
58
102
|
return () => {
|
|
@@ -74,7 +118,6 @@ export class ArgumentEngine {
|
|
|
74
118
|
!dirty.argument &&
|
|
75
119
|
!dirty.variables &&
|
|
76
120
|
!dirty.roles &&
|
|
77
|
-
!dirty.sources &&
|
|
78
121
|
dirty.premiseIds.size === 0 &&
|
|
79
122
|
!dirty.allPremises) {
|
|
80
123
|
return prev;
|
|
@@ -113,36 +156,17 @@ export class ArgumentEngine {
|
|
|
113
156
|
}
|
|
114
157
|
}
|
|
115
158
|
}
|
|
116
|
-
let varAssocRecord;
|
|
117
|
-
let exprAssocRecord;
|
|
118
|
-
if (dirty.sources || !prev) {
|
|
119
|
-
varAssocRecord = {};
|
|
120
|
-
for (const a of this.sourceManager.getAllVariableSourceAssociations()) {
|
|
121
|
-
varAssocRecord[a.id] = a;
|
|
122
|
-
}
|
|
123
|
-
exprAssocRecord = {};
|
|
124
|
-
for (const a of this.sourceManager.getAllExpressionSourceAssociations()) {
|
|
125
|
-
exprAssocRecord[a.id] = a;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
else {
|
|
129
|
-
varAssocRecord = prev.variableSourceAssociations;
|
|
130
|
-
exprAssocRecord = prev.expressionSourceAssociations;
|
|
131
|
-
}
|
|
132
159
|
const snapshot = {
|
|
133
160
|
argument,
|
|
134
161
|
variables,
|
|
135
162
|
premises,
|
|
136
163
|
roles,
|
|
137
|
-
variableSourceAssociations: varAssocRecord,
|
|
138
|
-
expressionSourceAssociations: exprAssocRecord,
|
|
139
164
|
};
|
|
140
165
|
this.cachedReactiveSnapshot = snapshot;
|
|
141
166
|
this.reactiveDirty = {
|
|
142
167
|
argument: false,
|
|
143
168
|
variables: false,
|
|
144
169
|
roles: false,
|
|
145
|
-
sources: false,
|
|
146
170
|
premiseIds: new Set(),
|
|
147
171
|
allPremises: false,
|
|
148
172
|
};
|
|
@@ -202,10 +226,6 @@ export class ArgumentEngine {
|
|
|
202
226
|
this.reactiveDirty.premiseIds.add(p.id);
|
|
203
227
|
}
|
|
204
228
|
}
|
|
205
|
-
if (changes.variableSourceAssociations ||
|
|
206
|
-
changes.expressionSourceAssociations) {
|
|
207
|
-
this.reactiveDirty.sources = true;
|
|
208
|
-
}
|
|
209
229
|
}
|
|
210
230
|
getArgument() {
|
|
211
231
|
return { ...this.argument, checksum: this.checksum() };
|
|
@@ -249,12 +269,13 @@ export class ArgumentEngine {
|
|
|
249
269
|
argument: this.argument,
|
|
250
270
|
variables: this.variables,
|
|
251
271
|
expressionIndex: this.expressionIndex,
|
|
252
|
-
sourceManager: this.sourceManager,
|
|
253
272
|
}, {
|
|
254
273
|
checksumConfig: this.checksumConfig,
|
|
255
274
|
positionConfig: this.positionConfig,
|
|
256
275
|
});
|
|
257
276
|
this.premises.set(id, pm);
|
|
277
|
+
this.wireCircularityCheck(pm);
|
|
278
|
+
this.wireEmptyBoundPremiseCheck(pm);
|
|
258
279
|
pm.setOnMutate(() => {
|
|
259
280
|
this.reactiveDirty.premiseIds.add(id);
|
|
260
281
|
this.notifySubscribers();
|
|
@@ -280,13 +301,9 @@ export class ArgumentEngine {
|
|
|
280
301
|
return { result: undefined, changes: {} };
|
|
281
302
|
const data = pm.toPremiseData();
|
|
282
303
|
const collector = new ChangeCollector();
|
|
283
|
-
// Clean up expression index
|
|
304
|
+
// Clean up expression index for removed premise's expressions
|
|
284
305
|
for (const expr of pm.getExpressions()) {
|
|
285
306
|
this.expressionIndex.delete(expr.id);
|
|
286
|
-
const sourceResult = this.sourceManager.removeAssociationsForExpression(expr.id);
|
|
287
|
-
for (const assoc of sourceResult.removedExpressionAssociations) {
|
|
288
|
-
collector.removedExpressionSourceAssociation(assoc);
|
|
289
|
-
}
|
|
290
307
|
}
|
|
291
308
|
this.premises.delete(premiseId);
|
|
292
309
|
collector.removedPremise(data);
|
|
@@ -294,6 +311,21 @@ export class ArgumentEngine {
|
|
|
294
311
|
this.conclusionPremiseId = undefined;
|
|
295
312
|
collector.setRoles(this.getRoleState());
|
|
296
313
|
}
|
|
314
|
+
// Cascade: remove variables bound to the deleted premise
|
|
315
|
+
const boundVars = this.getVariablesBoundToPremise(premiseId);
|
|
316
|
+
for (const v of boundVars) {
|
|
317
|
+
const removeResult = this.removeVariable(v.id);
|
|
318
|
+
if (removeResult.changes.variables) {
|
|
319
|
+
for (const rv of removeResult.changes.variables.removed) {
|
|
320
|
+
collector.removedVariable(rv);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
if (removeResult.changes.expressions) {
|
|
324
|
+
for (const re of removeResult.changes.expressions.removed) {
|
|
325
|
+
collector.removedExpression(re);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
297
329
|
this.markDirty();
|
|
298
330
|
const changes = collector.toChangeset();
|
|
299
331
|
this.markReactiveDirty(changes);
|
|
@@ -318,17 +350,53 @@ export class ArgumentEngine {
|
|
|
318
350
|
.filter((pm) => pm !== undefined);
|
|
319
351
|
}
|
|
320
352
|
addVariable(variable) {
|
|
353
|
+
// Only claim-bound variables may be added via addVariable.
|
|
354
|
+
// Premise-bound variables must use bindVariableToPremise.
|
|
355
|
+
if (!isClaimBound(variable)) {
|
|
356
|
+
throw new Error("addVariable only accepts claim-bound variables. Use bindVariableToPremise for premise-bound variables.");
|
|
357
|
+
}
|
|
321
358
|
if (variable.argumentId !== this.argument.id) {
|
|
322
359
|
throw new Error(`Variable argumentId "${variable.argumentId}" does not match engine argument ID "${this.argument.id}".`);
|
|
323
360
|
}
|
|
324
361
|
if (variable.argumentVersion !== this.argument.version) {
|
|
325
362
|
throw new Error(`Variable argumentVersion "${variable.argumentVersion}" does not match engine argument version "${this.argument.version}".`);
|
|
326
363
|
}
|
|
327
|
-
// Validate
|
|
328
|
-
if (!this.
|
|
329
|
-
throw new Error(`
|
|
364
|
+
// Validate claim reference
|
|
365
|
+
if (!this.claimLibrary.get(variable.claimId, variable.claimVersion)) {
|
|
366
|
+
throw new Error(`Claim "${variable.claimId}" version ${variable.claimVersion} does not exist in the claim library.`);
|
|
330
367
|
}
|
|
331
|
-
const withChecksum = this.attachVariableChecksum({
|
|
368
|
+
const withChecksum = this.attachVariableChecksum({
|
|
369
|
+
...variable,
|
|
370
|
+
});
|
|
371
|
+
this.variables.addVariable(withChecksum);
|
|
372
|
+
const collector = new ChangeCollector();
|
|
373
|
+
collector.addedVariable(withChecksum);
|
|
374
|
+
this.markDirty();
|
|
375
|
+
this.markAllPremisesDirty();
|
|
376
|
+
const changes = collector.toChangeset();
|
|
377
|
+
this.markReactiveDirty(changes);
|
|
378
|
+
this.notifySubscribers();
|
|
379
|
+
return {
|
|
380
|
+
result: withChecksum,
|
|
381
|
+
changes,
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
bindVariableToPremise(variable) {
|
|
385
|
+
if (variable.argumentId !== this.argument.id) {
|
|
386
|
+
throw new Error(`Variable argumentId "${variable.argumentId}" does not match engine argument ID "${this.argument.id}".`);
|
|
387
|
+
}
|
|
388
|
+
if (variable.argumentVersion !== this.argument.version) {
|
|
389
|
+
throw new Error(`Variable argumentVersion "${variable.argumentVersion}" does not match engine argument version "${this.argument.version}".`);
|
|
390
|
+
}
|
|
391
|
+
if (variable.boundArgumentId !== this.argument.id) {
|
|
392
|
+
throw new Error(`Cross-argument bindings are not supported. boundArgumentId "${variable.boundArgumentId}" does not match engine argument ID "${this.argument.id}".`);
|
|
393
|
+
}
|
|
394
|
+
if (!this.premises.has(variable.boundPremiseId)) {
|
|
395
|
+
throw new Error(`Bound premise "${variable.boundPremiseId}" does not exist in this argument.`);
|
|
396
|
+
}
|
|
397
|
+
const withChecksum = this.attachVariableChecksum({
|
|
398
|
+
...variable,
|
|
399
|
+
});
|
|
332
400
|
this.variables.addVariable(withChecksum);
|
|
333
401
|
const collector = new ChangeCollector();
|
|
334
402
|
collector.addedVariable(withChecksum);
|
|
@@ -343,16 +411,50 @@ export class ArgumentEngine {
|
|
|
343
411
|
};
|
|
344
412
|
}
|
|
345
413
|
updateVariable(variableId, updates) {
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
//
|
|
353
|
-
if (
|
|
354
|
-
|
|
355
|
-
|
|
414
|
+
const existing = this.variables.getVariable(variableId);
|
|
415
|
+
if (!existing) {
|
|
416
|
+
return { result: undefined, changes: {} };
|
|
417
|
+
}
|
|
418
|
+
const existingVar = existing;
|
|
419
|
+
const updatesObj = updates;
|
|
420
|
+
// Reject binding-type conversion
|
|
421
|
+
if (isClaimBound(existingVar)) {
|
|
422
|
+
const premiseBoundFields = [
|
|
423
|
+
"boundPremiseId",
|
|
424
|
+
"boundArgumentId",
|
|
425
|
+
"boundArgumentVersion",
|
|
426
|
+
];
|
|
427
|
+
for (const f of premiseBoundFields) {
|
|
428
|
+
if (updatesObj[f] !== undefined) {
|
|
429
|
+
throw new Error(`Cannot set "${f}" on a claim-bound variable. Delete and re-create to change binding type.`);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
// Validate: claimId and claimVersion must be provided together
|
|
433
|
+
const hasClaimId = updatesObj.claimId !== undefined;
|
|
434
|
+
const hasClaimVersion = updatesObj.claimVersion !== undefined;
|
|
435
|
+
if (hasClaimId !== hasClaimVersion) {
|
|
436
|
+
throw new Error("claimId and claimVersion must be provided together.");
|
|
437
|
+
}
|
|
438
|
+
// Validate claim reference if provided
|
|
439
|
+
if (hasClaimId && hasClaimVersion) {
|
|
440
|
+
if (!this.claimLibrary.get(updatesObj.claimId, updatesObj.claimVersion)) {
|
|
441
|
+
throw new Error(`Claim "${String(updatesObj.claimId)}" version ${String(updatesObj.claimVersion)} does not exist in the claim library.`);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
else if (isPremiseBound(existingVar)) {
|
|
446
|
+
const claimBoundFields = ["claimId", "claimVersion"];
|
|
447
|
+
for (const f of claimBoundFields) {
|
|
448
|
+
if (updatesObj[f] !== undefined) {
|
|
449
|
+
throw new Error(`Cannot set "${f}" on a premise-bound variable. Delete and re-create to change binding type.`);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
// Validate boundPremiseId if provided
|
|
453
|
+
if (updatesObj.boundPremiseId !== undefined) {
|
|
454
|
+
const newPremiseId = updatesObj.boundPremiseId;
|
|
455
|
+
if (!this.premises.has(newPremiseId)) {
|
|
456
|
+
throw new Error(`Bound premise "${newPremiseId}" does not exist in this argument.`);
|
|
457
|
+
}
|
|
356
458
|
}
|
|
357
459
|
}
|
|
358
460
|
const updated = this.variables.updateVariable(variableId, updates);
|
|
@@ -386,8 +488,6 @@ export class ArgumentEngine {
|
|
|
386
488
|
}
|
|
387
489
|
const collector = new ChangeCollector();
|
|
388
490
|
// Cascade: delete referencing expressions in every premise
|
|
389
|
-
// (PremiseEngine.removeExpression already cascades expression-source
|
|
390
|
-
// associations via the Task 12 logic)
|
|
391
491
|
for (const pm of this.listPremises()) {
|
|
392
492
|
const { changes } = pm.deleteExpressionsUsingVariable(variableId);
|
|
393
493
|
if (changes.expressions) {
|
|
@@ -395,16 +495,6 @@ export class ArgumentEngine {
|
|
|
395
495
|
collector.removedExpression(e);
|
|
396
496
|
}
|
|
397
497
|
}
|
|
398
|
-
if (changes.expressionSourceAssociations) {
|
|
399
|
-
for (const a of changes.expressionSourceAssociations.removed) {
|
|
400
|
-
collector.removedExpressionSourceAssociation(a);
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
// Cascade: remove variable-source associations
|
|
405
|
-
const varAssocResult = this.sourceManager.removeAssociationsForVariable(variableId);
|
|
406
|
-
for (const assoc of varAssocResult.removedVariableAssociations) {
|
|
407
|
-
collector.removedVariableSourceAssociation(assoc);
|
|
408
498
|
}
|
|
409
499
|
this.variables.removeVariable(variableId);
|
|
410
500
|
collector.removedVariable(variable);
|
|
@@ -437,6 +527,12 @@ export class ArgumentEngine {
|
|
|
437
527
|
}
|
|
438
528
|
return map;
|
|
439
529
|
}
|
|
530
|
+
getVariablesBoundToPremise(premiseId) {
|
|
531
|
+
return this.variables.toArray().filter((v) => {
|
|
532
|
+
const base = v;
|
|
533
|
+
return isPremiseBound(base) && base.boundPremiseId === premiseId;
|
|
534
|
+
});
|
|
535
|
+
}
|
|
440
536
|
getExpression(expressionId) {
|
|
441
537
|
const premiseId = this.expressionIndex.get(expressionId);
|
|
442
538
|
if (premiseId === undefined)
|
|
@@ -534,129 +630,6 @@ export class ArgumentEngine {
|
|
|
534
630
|
listSupportingPremises() {
|
|
535
631
|
return this.listPremises().filter((pm) => pm.isInference() && pm.getId() !== this.conclusionPremiseId);
|
|
536
632
|
}
|
|
537
|
-
// -------------------------------------------------------------------------
|
|
538
|
-
// Source association management
|
|
539
|
-
// -------------------------------------------------------------------------
|
|
540
|
-
addVariableSourceAssociation(sourceId, sourceVersion, variableId) {
|
|
541
|
-
if (!this.sourceLibrary.get(sourceId, sourceVersion)) {
|
|
542
|
-
throw new Error(`Source "${sourceId}" version ${sourceVersion} does not exist in the source library.`);
|
|
543
|
-
}
|
|
544
|
-
if (!this.variables.hasVariable(variableId)) {
|
|
545
|
-
throw new Error(`Variable "${variableId}" does not exist.`);
|
|
546
|
-
}
|
|
547
|
-
const assoc = {
|
|
548
|
-
id: randomUUID(),
|
|
549
|
-
sourceId,
|
|
550
|
-
sourceVersion,
|
|
551
|
-
variableId,
|
|
552
|
-
argumentId: this.argument.id,
|
|
553
|
-
argumentVersion: this.argument.version,
|
|
554
|
-
checksum: "",
|
|
555
|
-
};
|
|
556
|
-
const fields = this.checksumConfig?.variableSourceAssociationFields ??
|
|
557
|
-
DEFAULT_CHECKSUM_CONFIG.variableSourceAssociationFields;
|
|
558
|
-
const assocWithChecksum = {
|
|
559
|
-
...assoc,
|
|
560
|
-
checksum: entityChecksum(assoc, fields),
|
|
561
|
-
};
|
|
562
|
-
this.sourceManager.addVariableSourceAssociation(assocWithChecksum);
|
|
563
|
-
const collector = new ChangeCollector();
|
|
564
|
-
collector.addedVariableSourceAssociation(assocWithChecksum);
|
|
565
|
-
this.markDirty();
|
|
566
|
-
const changes = collector.toChangeset();
|
|
567
|
-
this.markReactiveDirty(changes);
|
|
568
|
-
this.notifySubscribers();
|
|
569
|
-
return { result: assocWithChecksum, changes };
|
|
570
|
-
}
|
|
571
|
-
removeVariableSourceAssociation(associationId) {
|
|
572
|
-
const allVarAssocs = this.sourceManager.getAllVariableSourceAssociations();
|
|
573
|
-
if (!allVarAssocs.some((a) => a.id === associationId)) {
|
|
574
|
-
return { result: undefined, changes: {} };
|
|
575
|
-
}
|
|
576
|
-
const removalResult = this.sourceManager.removeVariableSourceAssociation(associationId);
|
|
577
|
-
const collector = new ChangeCollector();
|
|
578
|
-
for (const assoc of removalResult.removedVariableAssociations) {
|
|
579
|
-
collector.removedVariableSourceAssociation(assoc);
|
|
580
|
-
}
|
|
581
|
-
this.markDirty();
|
|
582
|
-
const changes = collector.toChangeset();
|
|
583
|
-
this.markReactiveDirty(changes);
|
|
584
|
-
this.notifySubscribers();
|
|
585
|
-
return {
|
|
586
|
-
result: removalResult.removedVariableAssociations[0],
|
|
587
|
-
changes,
|
|
588
|
-
};
|
|
589
|
-
}
|
|
590
|
-
addExpressionSourceAssociation(sourceId, sourceVersion, expressionId, premiseId) {
|
|
591
|
-
if (!this.sourceLibrary.get(sourceId, sourceVersion)) {
|
|
592
|
-
throw new Error(`Source "${sourceId}" version ${sourceVersion} does not exist in the source library.`);
|
|
593
|
-
}
|
|
594
|
-
const pm = this.premises.get(premiseId);
|
|
595
|
-
if (!pm) {
|
|
596
|
-
throw new Error(`Premise "${premiseId}" does not exist.`);
|
|
597
|
-
}
|
|
598
|
-
if (!pm.getExpression(expressionId)) {
|
|
599
|
-
throw new Error(`Expression "${expressionId}" does not exist in premise "${premiseId}".`);
|
|
600
|
-
}
|
|
601
|
-
const assoc = {
|
|
602
|
-
id: randomUUID(),
|
|
603
|
-
sourceId,
|
|
604
|
-
sourceVersion,
|
|
605
|
-
expressionId,
|
|
606
|
-
premiseId,
|
|
607
|
-
argumentId: this.argument.id,
|
|
608
|
-
argumentVersion: this.argument.version,
|
|
609
|
-
checksum: "",
|
|
610
|
-
};
|
|
611
|
-
const fields = this.checksumConfig?.expressionSourceAssociationFields ??
|
|
612
|
-
DEFAULT_CHECKSUM_CONFIG.expressionSourceAssociationFields;
|
|
613
|
-
const assocWithChecksum = {
|
|
614
|
-
...assoc,
|
|
615
|
-
checksum: entityChecksum(assoc, fields),
|
|
616
|
-
};
|
|
617
|
-
this.sourceManager.addExpressionSourceAssociation(assocWithChecksum);
|
|
618
|
-
const collector = new ChangeCollector();
|
|
619
|
-
collector.addedExpressionSourceAssociation(assocWithChecksum);
|
|
620
|
-
this.markDirty();
|
|
621
|
-
const changes = collector.toChangeset();
|
|
622
|
-
this.markReactiveDirty(changes);
|
|
623
|
-
this.notifySubscribers();
|
|
624
|
-
return { result: assocWithChecksum, changes };
|
|
625
|
-
}
|
|
626
|
-
removeExpressionSourceAssociation(associationId) {
|
|
627
|
-
const allExprAssocs = this.sourceManager.getAllExpressionSourceAssociations();
|
|
628
|
-
if (!allExprAssocs.some((a) => a.id === associationId)) {
|
|
629
|
-
return { result: undefined, changes: {} };
|
|
630
|
-
}
|
|
631
|
-
const removalResult = this.sourceManager.removeExpressionSourceAssociation(associationId);
|
|
632
|
-
const collector = new ChangeCollector();
|
|
633
|
-
for (const assoc of removalResult.removedExpressionAssociations) {
|
|
634
|
-
collector.removedExpressionSourceAssociation(assoc);
|
|
635
|
-
}
|
|
636
|
-
this.markDirty();
|
|
637
|
-
const changes = collector.toChangeset();
|
|
638
|
-
this.markReactiveDirty(changes);
|
|
639
|
-
this.notifySubscribers();
|
|
640
|
-
return {
|
|
641
|
-
result: removalResult.removedExpressionAssociations[0],
|
|
642
|
-
changes,
|
|
643
|
-
};
|
|
644
|
-
}
|
|
645
|
-
getAssociationsForSource(sourceId) {
|
|
646
|
-
return this.sourceManager.getAssociationsForSource(sourceId);
|
|
647
|
-
}
|
|
648
|
-
getAssociationsForVariable(variableId) {
|
|
649
|
-
return this.sourceManager.getAssociationsForVariable(variableId);
|
|
650
|
-
}
|
|
651
|
-
getAssociationsForExpression(expressionId) {
|
|
652
|
-
return this.sourceManager.getAssociationsForExpression(expressionId);
|
|
653
|
-
}
|
|
654
|
-
getAllVariableSourceAssociations() {
|
|
655
|
-
return this.sourceManager.getAllVariableSourceAssociations();
|
|
656
|
-
}
|
|
657
|
-
getAllExpressionSourceAssociations() {
|
|
658
|
-
return this.sourceManager.getAllExpressionSourceAssociations();
|
|
659
|
-
}
|
|
660
633
|
snapshot() {
|
|
661
634
|
return {
|
|
662
635
|
argument: { ...this.argument },
|
|
@@ -669,30 +642,34 @@ export class ArgumentEngine {
|
|
|
669
642
|
checksumConfig: this.checksumConfig,
|
|
670
643
|
positionConfig: this.positionConfig,
|
|
671
644
|
},
|
|
672
|
-
sources: this.sourceManager.snapshot(),
|
|
673
645
|
};
|
|
674
646
|
}
|
|
675
647
|
/** Creates a new ArgumentEngine from a previously captured snapshot. */
|
|
676
|
-
static fromSnapshot(snapshot,
|
|
677
|
-
const engine = new ArgumentEngine(snapshot.argument,
|
|
678
|
-
// Restore variables
|
|
679
|
-
for (const v of snapshot.variables.variables) {
|
|
680
|
-
engine.addVariable(v);
|
|
681
|
-
}
|
|
682
|
-
// Restore source manager (before premises, so PremiseEngines get the correct reference)
|
|
683
|
-
if (snapshot.sources) {
|
|
684
|
-
engine.sourceManager = SourceManager.fromSnapshot(snapshot.sources);
|
|
685
|
-
}
|
|
686
|
-
// Restore premises using PremiseEngine.fromSnapshot
|
|
648
|
+
static fromSnapshot(snapshot, claimLibrary, sourceLibrary, claimSourceLibrary) {
|
|
649
|
+
const engine = new ArgumentEngine(snapshot.argument, claimLibrary, sourceLibrary, claimSourceLibrary, snapshot.config);
|
|
650
|
+
// Restore premises first (premise-bound variables reference them)
|
|
687
651
|
for (const premiseSnap of snapshot.premises) {
|
|
688
|
-
const pe = PremiseEngine.fromSnapshot(premiseSnap, snapshot.argument, engine.variables, engine.expressionIndex
|
|
652
|
+
const pe = PremiseEngine.fromSnapshot(premiseSnap, snapshot.argument, engine.variables, engine.expressionIndex);
|
|
689
653
|
engine.premises.set(pe.getId(), pe);
|
|
654
|
+
engine.wireCircularityCheck(pe);
|
|
655
|
+
engine.wireEmptyBoundPremiseCheck(pe);
|
|
690
656
|
const premiseId = pe.getId();
|
|
691
657
|
pe.setOnMutate(() => {
|
|
692
658
|
engine.reactiveDirty.premiseIds.add(premiseId);
|
|
693
659
|
engine.notifySubscribers();
|
|
694
660
|
});
|
|
695
661
|
}
|
|
662
|
+
// Restore claim-bound variables first, then premise-bound variables
|
|
663
|
+
for (const v of snapshot.variables.variables) {
|
|
664
|
+
if (isClaimBound(v)) {
|
|
665
|
+
engine.addVariable(v);
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
for (const v of snapshot.variables.variables) {
|
|
669
|
+
if (isPremiseBound(v)) {
|
|
670
|
+
engine.bindVariableToPremise(v);
|
|
671
|
+
}
|
|
672
|
+
}
|
|
696
673
|
// Restore conclusion role (don't use setConclusionPremise to avoid auto-assign logic)
|
|
697
674
|
engine.conclusionPremiseId = snapshot.conclusionPremiseId;
|
|
698
675
|
return engine;
|
|
@@ -703,11 +680,26 @@ export class ArgumentEngine {
|
|
|
703
680
|
* `premiseId` field and loaded in BFS order (roots first, then children
|
|
704
681
|
* of already-added nodes) to satisfy parent-existence requirements.
|
|
705
682
|
*/
|
|
706
|
-
static fromData(argument,
|
|
707
|
-
const engine = new ArgumentEngine(argument,
|
|
708
|
-
// Register variables
|
|
683
|
+
static fromData(argument, claimLibrary, sourceLibrary, claimSourceLibrary, variables, premises, expressions, roles, config) {
|
|
684
|
+
const engine = new ArgumentEngine(argument, claimLibrary, sourceLibrary, claimSourceLibrary, config);
|
|
685
|
+
// Register claim-bound variables first (no dependencies)
|
|
686
|
+
for (const v of variables) {
|
|
687
|
+
if (isClaimBound(v)) {
|
|
688
|
+
engine.addVariable(v);
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
// Create premises (premise-bound variables reference them)
|
|
692
|
+
const premiseEngines = new Map();
|
|
693
|
+
for (const premise of premises) {
|
|
694
|
+
const { id: _id, argumentId: _argumentId, argumentVersion: _argumentVersion, checksum: _checksum, ...extras } = premise;
|
|
695
|
+
const { result: pe } = engine.createPremiseWithId(premise.id, extras);
|
|
696
|
+
premiseEngines.set(premise.id, pe);
|
|
697
|
+
}
|
|
698
|
+
// Register premise-bound variables (depend on premises)
|
|
709
699
|
for (const v of variables) {
|
|
710
|
-
|
|
700
|
+
if (isPremiseBound(v)) {
|
|
701
|
+
engine.bindVariableToPremise(v);
|
|
702
|
+
}
|
|
711
703
|
}
|
|
712
704
|
// Group expressions by premiseId
|
|
713
705
|
const exprsByPremise = new Map();
|
|
@@ -721,12 +713,9 @@ export class ArgumentEngine {
|
|
|
721
713
|
}
|
|
722
714
|
group.push(expr);
|
|
723
715
|
}
|
|
724
|
-
//
|
|
725
|
-
for (const
|
|
726
|
-
const
|
|
727
|
-
const { result: pe } = engine.createPremiseWithId(premise.id, extras);
|
|
728
|
-
// Add expressions in BFS order (roots first, then children)
|
|
729
|
-
const premiseExprs = exprsByPremise.get(premise.id) ?? [];
|
|
716
|
+
// Add expressions in BFS order (roots first, then children)
|
|
717
|
+
for (const [premiseId, pe] of premiseEngines) {
|
|
718
|
+
const premiseExprs = exprsByPremise.get(premiseId) ?? [];
|
|
730
719
|
const pending = new Map(premiseExprs.map((e) => [e.id, e]));
|
|
731
720
|
let progressed = true;
|
|
732
721
|
while (pending.size > 0 && progressed) {
|
|
@@ -757,17 +746,16 @@ export class ArgumentEngine {
|
|
|
757
746
|
this.checksumConfig = snapshot.config?.checksumConfig;
|
|
758
747
|
this.positionConfig = snapshot.config?.positionConfig;
|
|
759
748
|
this.variables = VariableManager.fromSnapshot(snapshot.variables);
|
|
760
|
-
this.sourceManager = snapshot.sources
|
|
761
|
-
? SourceManager.fromSnapshot(snapshot.sources)
|
|
762
|
-
: new SourceManager();
|
|
763
749
|
this.premises = new Map();
|
|
764
750
|
this.expressionIndex = new Map();
|
|
765
751
|
for (const premiseSnap of snapshot.premises) {
|
|
766
|
-
const pe = PremiseEngine.fromSnapshot(premiseSnap, this.argument, this.variables, this.expressionIndex
|
|
752
|
+
const pe = PremiseEngine.fromSnapshot(premiseSnap, this.argument, this.variables, this.expressionIndex);
|
|
767
753
|
this.premises.set(pe.getId(), pe);
|
|
768
754
|
}
|
|
769
755
|
this.conclusionPremiseId = snapshot.conclusionPremiseId;
|
|
770
756
|
for (const pe of this.premises.values()) {
|
|
757
|
+
this.wireCircularityCheck(pe);
|
|
758
|
+
this.wireEmptyBoundPremiseCheck(pe);
|
|
771
759
|
const premiseId = pe.getId();
|
|
772
760
|
pe.setOnMutate(() => {
|
|
773
761
|
this.reactiveDirty.premiseIds.add(premiseId);
|
|
@@ -779,7 +767,6 @@ export class ArgumentEngine {
|
|
|
779
767
|
argument: true,
|
|
780
768
|
variables: true,
|
|
781
769
|
roles: true,
|
|
782
|
-
sources: true,
|
|
783
770
|
premiseIds: new Set(),
|
|
784
771
|
allPremises: true,
|
|
785
772
|
};
|
|
@@ -807,13 +794,6 @@ export class ArgumentEngine {
|
|
|
807
794
|
for (const pe of this.listPremises()) {
|
|
808
795
|
checksumMap[pe.getId()] = pe.checksum();
|
|
809
796
|
}
|
|
810
|
-
// Association checksums
|
|
811
|
-
for (const a of this.sourceManager.getAllVariableSourceAssociations()) {
|
|
812
|
-
checksumMap[a.id] = a.checksum;
|
|
813
|
-
}
|
|
814
|
-
for (const a of this.sourceManager.getAllExpressionSourceAssociations()) {
|
|
815
|
-
checksumMap[a.id] = a.checksum;
|
|
816
|
-
}
|
|
817
797
|
return computeHash(canonicalSerialize(checksumMap));
|
|
818
798
|
}
|
|
819
799
|
markDirty() {
|
|
@@ -929,30 +909,6 @@ export class ArgumentEngine {
|
|
|
929
909
|
const premiseValidation = premise.validateEvaluability();
|
|
930
910
|
issues.push(...premiseValidation.issues);
|
|
931
911
|
}
|
|
932
|
-
// Source validation
|
|
933
|
-
for (const assoc of this.sourceManager.getAllVariableSourceAssociations()) {
|
|
934
|
-
if (!this.variables.hasVariable(assoc.variableId)) {
|
|
935
|
-
issues.push(makeErrorIssue({
|
|
936
|
-
code: "SOURCE_VARIABLE_ASSOCIATION_INVALID_VARIABLE",
|
|
937
|
-
message: `Variable-source association "${assoc.id}" references non-existent variable "${assoc.variableId}".`,
|
|
938
|
-
}));
|
|
939
|
-
}
|
|
940
|
-
}
|
|
941
|
-
for (const assoc of this.sourceManager.getAllExpressionSourceAssociations()) {
|
|
942
|
-
const premise = this.premises.get(assoc.premiseId);
|
|
943
|
-
if (!premise) {
|
|
944
|
-
issues.push(makeErrorIssue({
|
|
945
|
-
code: "SOURCE_EXPRESSION_ASSOCIATION_INVALID_PREMISE",
|
|
946
|
-
message: `Expression-source association "${assoc.id}" references non-existent premise "${assoc.premiseId}".`,
|
|
947
|
-
}));
|
|
948
|
-
}
|
|
949
|
-
else if (!premise.getExpression(assoc.expressionId)) {
|
|
950
|
-
issues.push(makeErrorIssue({
|
|
951
|
-
code: "SOURCE_EXPRESSION_ASSOCIATION_INVALID_EXPRESSION",
|
|
952
|
-
message: `Expression-source association "${assoc.id}" references non-existent expression "${assoc.expressionId}" in premise "${assoc.premiseId}".`,
|
|
953
|
-
}));
|
|
954
|
-
}
|
|
955
|
-
}
|
|
956
912
|
return makeValidationResult(issues);
|
|
957
913
|
}
|
|
958
914
|
evaluate(assignment, options) {
|
|
@@ -987,15 +943,47 @@ export class ArgumentEngine {
|
|
|
987
943
|
...supportingPremises,
|
|
988
944
|
...constraintPremises,
|
|
989
945
|
];
|
|
990
|
-
const
|
|
946
|
+
const allVariableIds = [
|
|
991
947
|
...new Set(allRelevantPremises.flatMap((pm) => pm
|
|
992
948
|
.getExpressions()
|
|
993
949
|
.filter((expr) => expr.type === "variable")
|
|
994
950
|
.map((expr) => expr.variableId))),
|
|
995
951
|
].sort();
|
|
952
|
+
// Only claim-bound variables get truth-table columns;
|
|
953
|
+
// premise-bound variables are resolved lazily from their bound premise.
|
|
954
|
+
const referencedVariableIds = allVariableIds.filter((vid) => {
|
|
955
|
+
const v = this.variables.getVariable(vid);
|
|
956
|
+
return v != null && isClaimBound(v);
|
|
957
|
+
});
|
|
996
958
|
try {
|
|
959
|
+
// Build a resolver that lazily evaluates premise-bound variables
|
|
960
|
+
// by evaluating their bound premise's expression tree under the
|
|
961
|
+
// same assignment. Results are cached per-variable per-evaluate call.
|
|
962
|
+
const resolverCache = new Map();
|
|
963
|
+
const resolver = (variableId) => {
|
|
964
|
+
if (resolverCache.has(variableId)) {
|
|
965
|
+
return resolverCache.get(variableId);
|
|
966
|
+
}
|
|
967
|
+
const variable = this.variables.getVariable(variableId);
|
|
968
|
+
if (!variable || !isPremiseBound(variable)) {
|
|
969
|
+
return assignment.variables[variableId] ?? null;
|
|
970
|
+
}
|
|
971
|
+
const boundPremiseId = variable.boundPremiseId;
|
|
972
|
+
const boundPremise = this.premises.get(boundPremiseId);
|
|
973
|
+
if (!boundPremise) {
|
|
974
|
+
resolverCache.set(variableId, null);
|
|
975
|
+
return null;
|
|
976
|
+
}
|
|
977
|
+
const premiseResult = boundPremise.evaluate(assignment, {
|
|
978
|
+
resolver,
|
|
979
|
+
});
|
|
980
|
+
const value = premiseResult?.rootValue ?? null;
|
|
981
|
+
resolverCache.set(variableId, value);
|
|
982
|
+
return value;
|
|
983
|
+
};
|
|
997
984
|
const evalOpts = {
|
|
998
985
|
strictUnknownKeys: options?.strictUnknownAssignmentKeys ?? false,
|
|
986
|
+
resolver,
|
|
999
987
|
};
|
|
1000
988
|
const conclusionEvaluation = conclusion.evaluate(assignment, evalOpts);
|
|
1001
989
|
const supportingEvaluations = supportingPremises.map((pm) => pm.evaluate(assignment, evalOpts));
|
|
@@ -1075,7 +1063,7 @@ export class ArgumentEngine {
|
|
|
1075
1063
|
const supportingIds = new Set(supportingPremises.map((pm) => pm.getId()));
|
|
1076
1064
|
const constraintPremises = this.listPremises().filter((pm) => pm.getId() !== this.conclusionPremiseId &&
|
|
1077
1065
|
!supportingIds.has(pm.getId()));
|
|
1078
|
-
const
|
|
1066
|
+
const allVariableIdsForCheck = [
|
|
1079
1067
|
...new Set([
|
|
1080
1068
|
conclusion,
|
|
1081
1069
|
...supportingPremises,
|
|
@@ -1085,6 +1073,12 @@ export class ArgumentEngine {
|
|
|
1085
1073
|
.filter((expr) => expr.type === "variable")
|
|
1086
1074
|
.map((expr) => expr.variableId))),
|
|
1087
1075
|
].sort();
|
|
1076
|
+
// Only claim-bound variables get truth-table columns;
|
|
1077
|
+
// premise-bound variables are resolved lazily from their bound premise.
|
|
1078
|
+
const checkedVariableIds = allVariableIdsForCheck.filter((vid) => {
|
|
1079
|
+
const v = this.variables.getVariable(vid);
|
|
1080
|
+
return v != null && isClaimBound(v);
|
|
1081
|
+
});
|
|
1088
1082
|
if (options?.maxVariables !== undefined &&
|
|
1089
1083
|
checkedVariableIds.length > options.maxVariables) {
|
|
1090
1084
|
return {
|