@polintpro/proposit-core 0.8.6 → 0.8.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/README.md +2 -0
  2. package/dist/extensions/ieee/formatting.d.ts +2 -14
  3. package/dist/extensions/ieee/formatting.d.ts.map +1 -1
  4. package/dist/extensions/ieee/formatting.js +42 -1038
  5. package/dist/extensions/ieee/formatting.js.map +1 -1
  6. package/dist/extensions/ieee/index.d.ts +1 -0
  7. package/dist/extensions/ieee/index.d.ts.map +1 -1
  8. package/dist/extensions/ieee/index.js +1 -0
  9. package/dist/extensions/ieee/index.js.map +1 -1
  10. package/dist/extensions/ieee/segment-builder.d.ts +9 -0
  11. package/dist/extensions/ieee/segment-builder.d.ts.map +1 -0
  12. package/dist/extensions/ieee/segment-builder.js +98 -0
  13. package/dist/extensions/ieee/segment-builder.js.map +1 -0
  14. package/dist/extensions/ieee/segment-templates.d.ts +58 -0
  15. package/dist/extensions/ieee/segment-templates.d.ts.map +1 -0
  16. package/dist/extensions/ieee/segment-templates.js +1618 -0
  17. package/dist/extensions/ieee/segment-templates.js.map +1 -0
  18. package/dist/lib/core/argument-engine.d.ts +2 -19
  19. package/dist/lib/core/argument-engine.d.ts.map +1 -1
  20. package/dist/lib/core/argument-engine.js +46 -819
  21. package/dist/lib/core/argument-engine.js.map +1 -1
  22. package/dist/lib/core/argument-validation.d.ts +74 -0
  23. package/dist/lib/core/argument-validation.d.ts.map +1 -0
  24. package/dist/lib/core/argument-validation.js +315 -0
  25. package/dist/lib/core/argument-validation.js.map +1 -0
  26. package/dist/lib/core/evaluation/argument-evaluation.d.ts +53 -0
  27. package/dist/lib/core/evaluation/argument-evaluation.d.ts.map +1 -0
  28. package/dist/lib/core/evaluation/argument-evaluation.js +535 -0
  29. package/dist/lib/core/evaluation/argument-evaluation.js.map +1 -0
  30. package/dist/lib/core/parser/formula-gen.js +1 -1
  31. package/dist/lib/index.d.ts +4 -0
  32. package/dist/lib/index.d.ts.map +1 -1
  33. package/dist/lib/index.js +2 -0
  34. package/dist/lib/index.js.map +1 -1
  35. package/package.json +9 -9
@@ -1,13 +1,10 @@
1
- import { Value } from "typebox/value";
2
- import { CoreArgumentSchema, isClaimBound, isPremiseBound, } from "../schemata/index.js";
1
+ import { isClaimBound, isPremiseBound, } from "../schemata/index.js";
3
2
  import { DEFAULT_GRAMMAR_CONFIG, PERMISSIVE_GRAMMAR_CONFIG, } from "../types/grammar.js";
4
- import { ARG_SCHEMA_INVALID, ARG_OWNERSHIP_MISMATCH, ARG_CLAIM_REF_NOT_FOUND, ARG_PREMISE_REF_NOT_FOUND, ARG_CIRCULARITY_DETECTED, ARG_CONCLUSION_NOT_FOUND, ARG_CHECKSUM_MISMATCH, } from "../types/validation.js";
5
3
  import { DEFAULT_CHECKSUM_CONFIG, normalizeChecksumConfig, serializeChecksumConfig, } from "../consts.js";
6
- import { getOrCreate, sortedUnique } from "../utils/collections.js";
7
4
  import { ChangeCollector } from "./change-collector.js";
8
5
  import { canonicalSerialize, computeHash, entityChecksum } from "./checksum.js";
9
- import { kleeneAnd, kleeneNot, kleeneOr, kleeneImplies, kleeneIff, } from "./evaluation/kleene.js";
10
- import { makeErrorIssue, makeValidationResult, } from "./evaluation/validation.js";
6
+ import { evaluateArgument as evaluateArgumentStandalone, checkArgumentValidity as checkArgumentValidityStandalone, } from "./evaluation/argument-evaluation.js";
7
+ import { validateArgument as validateArgumentStandalone, validateArgumentAfterPremiseMutation as validateAfterPremiseMutationStandalone, validateArgumentEvaluability as validateArgumentEvaluabilityStandalone, collectArgumentReferencedVariables as collectArgumentReferencedVariablesStandalone, } from "./argument-validation.js";
11
8
  import { InvariantViolationError } from "./invariant-violation-error.js";
12
9
  import { PremiseEngine } from "./premise-engine.js";
13
10
  import { VariableManager } from "./variable-manager.js";
@@ -1181,833 +1178,63 @@ export class ArgumentEngine {
1181
1178
  };
1182
1179
  }
1183
1180
  collectReferencedVariables() {
1184
- const byIdTmp = new Map();
1185
- const bySymbolTmp = new Map();
1186
- for (const premise of this.listPremises()) {
1187
- const premiseId = premise.getId();
1188
- const varsById = new Map(premise.getVariables().map((v) => [v.id, v]));
1189
- for (const expr of premise.getExpressions()) {
1190
- if (expr.type !== "variable")
1191
- continue;
1192
- const variable = varsById.get(expr.variableId);
1193
- if (!variable)
1194
- continue;
1195
- const byIdEntry = getOrCreate(byIdTmp, variable.id, () => ({
1196
- symbols: new Set(),
1197
- premiseIds: new Set(),
1198
- }));
1199
- byIdEntry.symbols.add(variable.symbol);
1200
- byIdEntry.premiseIds.add(premiseId);
1201
- const bySymbolEntry = getOrCreate(bySymbolTmp, variable.symbol, () => ({
1202
- variableIds: new Set(),
1203
- premiseIds: new Set(),
1204
- }));
1205
- bySymbolEntry.variableIds.add(variable.id);
1206
- bySymbolEntry.premiseIds.add(premiseId);
1207
- }
1208
- }
1209
- const byId = {};
1210
- for (const [variableId, entry] of Array.from(byIdTmp.entries()).sort((a, b) => a[0].localeCompare(b[0]))) {
1211
- byId[variableId] = {
1212
- symbol: sortedUnique(entry.symbols)[0] ?? "",
1213
- premiseIds: sortedUnique(entry.premiseIds),
1214
- };
1215
- }
1216
- const bySymbol = {};
1217
- for (const [symbol, entry] of Array.from(bySymbolTmp.entries()).sort((a, b) => a[0].localeCompare(b[0]))) {
1218
- bySymbol[symbol] = {
1219
- variableIds: sortedUnique(entry.variableIds),
1220
- premiseIds: sortedUnique(entry.premiseIds),
1221
- };
1222
- }
1223
- return {
1224
- variableIds: sortedUnique(byIdTmp.keys()),
1225
- byId,
1226
- bySymbol,
1227
- };
1181
+ return collectArgumentReferencedVariablesStandalone(this.asValidationContext());
1228
1182
  }
1229
- /**
1230
- * Validates after a PremiseEngine mutation. Identical to `validate()` but
1231
- * clears cached argument-level checksums first so the checksum-stability
1232
- * check is skipped (checksums are known to be dirty after a premise
1233
- * mutation).
1234
- */
1235
- /**
1236
- * Lightweight validation triggered after a PremiseEngine mutation.
1237
- * Skips per-premise deep validation (which is O(n) over all premises)
1238
- * and argument-level checksum stability checks (checksums are known to
1239
- * be dirty). Only checks argument-level cross-references that a
1240
- * PremiseEngine mutation could affect.
1241
- */
1242
1183
  validateAfterPremiseMutation() {
1243
- const violations = [];
1244
- // Variable references: ensure all variable expressions in the
1245
- // mutated premise still reference known variables (this is the main
1246
- // cross-cutting invariant a premise mutation can break).
1247
- for (const v of this.variables.toArray()) {
1248
- const base = v;
1249
- if (isPremiseBound(base)) {
1250
- const pb = base;
1251
- if (pb.boundArgumentId === this.argument.id) {
1252
- if (!this.premises.has(pb.boundPremiseId)) {
1253
- violations.push({
1254
- code: ARG_PREMISE_REF_NOT_FOUND,
1255
- message: `Premise-bound variable "${pb.id}" references non-existent premise "${pb.boundPremiseId}".`,
1256
- entityType: "variable",
1257
- entityId: pb.id,
1258
- });
1259
- }
1260
- }
1261
- }
1262
- }
1263
- // Conclusion premise reference
1264
- if (this.conclusionPremiseId !== undefined &&
1265
- !this.premises.has(this.conclusionPremiseId)) {
1266
- violations.push({
1267
- code: ARG_CONCLUSION_NOT_FOUND,
1268
- message: `Conclusion premise "${this.conclusionPremiseId}" does not exist in this argument.`,
1269
- entityType: "argument",
1270
- entityId: this.argument.id,
1271
- });
1272
- }
1273
- return {
1274
- ok: violations.length === 0,
1275
- violations,
1276
- };
1184
+ return validateAfterPremiseMutationStandalone(this.asValidationContext());
1277
1185
  }
1278
1186
  validate() {
1279
- const violations = [];
1280
- // 1. Schema check — flush checksums first so fields are populated
1281
- const savedMeta = this.cachedMetaChecksum;
1282
- const savedDescendant = this.cachedDescendantChecksum;
1283
- const savedCombined = this.cachedCombinedChecksum;
1284
- this.flushChecksums();
1285
- const arg = this.getArgument();
1286
- if (!Value.Check(CoreArgumentSchema, arg)) {
1287
- violations.push({
1288
- code: ARG_SCHEMA_INVALID,
1289
- message: `Argument "${arg.id}" does not conform to CoreArgumentSchema.`,
1290
- entityType: "argument",
1291
- entityId: arg.id,
1292
- });
1293
- }
1294
- // 2. Delegate to VariableManager.validate()
1295
- const varResult = this.variables.validate();
1296
- violations.push(...varResult.violations);
1297
- // 3. Delegate to each PremiseEngine.validate()
1298
- for (const pe of this.listPremises()) {
1299
- const premiseResult = pe.validate();
1300
- violations.push(...premiseResult.violations);
1301
- }
1302
- // 4. Variable ownership: all variables must belong to this argument
1303
- for (const v of this.variables.toArray()) {
1304
- const base = v;
1305
- if (base.argumentId !== this.argument.id ||
1306
- base.argumentVersion !== this.argument.version) {
1307
- violations.push({
1308
- code: ARG_OWNERSHIP_MISMATCH,
1309
- message: `Variable "${base.id}" has argumentId/version "${base.argumentId}/${base.argumentVersion}" but engine is "${this.argument.id}/${this.argument.version}".`,
1310
- entityType: "variable",
1311
- entityId: base.id,
1312
- });
1313
- }
1314
- }
1315
- // 5. Claim-bound variable references
1316
- for (const v of this.variables.toArray()) {
1317
- const base = v;
1318
- if (isClaimBound(base)) {
1319
- const cb = base;
1320
- if (!this.claimLibrary.get(cb.claimId, cb.claimVersion)) {
1321
- violations.push({
1322
- code: ARG_CLAIM_REF_NOT_FOUND,
1323
- message: `Variable "${cb.id}" references claim "${cb.claimId}" version ${cb.claimVersion} which does not exist in the claim library.`,
1324
- entityType: "variable",
1325
- entityId: cb.id,
1326
- });
1327
- }
1328
- }
1329
- }
1330
- // 6. Premise-bound internal variable references
1331
- for (const v of this.variables.toArray()) {
1332
- const base = v;
1333
- if (isPremiseBound(base)) {
1334
- const pb = base;
1335
- if (pb.boundArgumentId === this.argument.id) {
1336
- if (!this.premises.has(pb.boundPremiseId)) {
1337
- violations.push({
1338
- code: ARG_PREMISE_REF_NOT_FOUND,
1339
- message: `Premise-bound variable "${pb.id}" references non-existent premise "${pb.boundPremiseId}".`,
1340
- entityType: "variable",
1341
- entityId: pb.id,
1342
- });
1343
- }
1344
- }
1345
- }
1346
- }
1347
- // 7. Circularity detection for internal premise-bound variables.
1348
- // A cycle exists when a premise-bound variable's bound premise
1349
- // transitively references back to itself through other
1350
- // premise-bound variables.
1351
- for (const v of this.variables.toArray()) {
1352
- const base = v;
1353
- if (isPremiseBound(base)) {
1354
- const pb = base;
1355
- if (pb.boundArgumentId === this.argument.id) {
1356
- // Trace from the bound premise through expressions'
1357
- // variable references to see if we reach back to the
1358
- // same premise.
1359
- const boundPremise = this.premises.get(pb.boundPremiseId);
1360
- if (boundPremise) {
1361
- let hasCycle = false;
1362
- for (const expr of boundPremise.getExpressions()) {
1363
- if (expr.type === "variable") {
1364
- try {
1365
- if (this.wouldCreateCycle(expr.variableId, pb.boundPremiseId, new Set())) {
1366
- hasCycle = true;
1367
- break;
1368
- }
1369
- }
1370
- catch {
1371
- hasCycle = true;
1372
- break;
1373
- }
1374
- }
1375
- }
1376
- if (hasCycle) {
1377
- violations.push({
1378
- code: ARG_CIRCULARITY_DETECTED,
1379
- message: `Premise-bound variable "${pb.id}" creates a circular dependency through premise "${pb.boundPremiseId}".`,
1380
- entityType: "variable",
1381
- entityId: pb.id,
1382
- });
1383
- }
1384
- }
1385
- }
1386
- }
1387
- }
1388
- // 8. Conclusion premise reference
1389
- if (this.conclusionPremiseId !== undefined &&
1390
- !this.premises.has(this.conclusionPremiseId)) {
1391
- violations.push({
1392
- code: ARG_CONCLUSION_NOT_FOUND,
1393
- message: `Conclusion premise "${this.conclusionPremiseId}" does not exist in this argument.`,
1394
- entityType: "argument",
1395
- entityId: this.argument.id,
1396
- });
1397
- }
1398
- // 9. Argument-level checksum verification
1399
- if (savedMeta !== undefined && savedMeta !== this.cachedMetaChecksum) {
1400
- violations.push({
1401
- code: ARG_CHECKSUM_MISMATCH,
1402
- message: `Argument "${this.argument.id}" meta checksum changed after flush: "${savedMeta}" → "${this.cachedMetaChecksum}".`,
1403
- entityType: "argument",
1404
- entityId: this.argument.id,
1405
- });
1406
- }
1407
- if (savedDescendant !== undefined &&
1408
- savedDescendant !== this.cachedDescendantChecksum) {
1409
- violations.push({
1410
- code: ARG_CHECKSUM_MISMATCH,
1411
- message: `Argument "${this.argument.id}" descendant checksum changed after flush: "${String(savedDescendant)}" → "${String(this.cachedDescendantChecksum)}".`,
1412
- entityType: "argument",
1413
- entityId: this.argument.id,
1414
- });
1415
- }
1416
- if (savedCombined !== undefined &&
1417
- savedCombined !== this.cachedCombinedChecksum) {
1418
- violations.push({
1419
- code: ARG_CHECKSUM_MISMATCH,
1420
- message: `Argument "${this.argument.id}" combined checksum changed after flush: "${savedCombined}" → "${this.cachedCombinedChecksum}".`,
1421
- entityType: "argument",
1422
- entityId: this.argument.id,
1423
- });
1424
- }
1425
- return {
1426
- ok: violations.length === 0,
1427
- violations,
1428
- };
1187
+ return validateArgumentStandalone(this.asValidationContext());
1429
1188
  }
1430
1189
  validateEvaluability() {
1431
- const issues = [];
1432
- if (this.conclusionPremiseId === undefined) {
1433
- issues.push(makeErrorIssue({
1434
- code: "ARGUMENT_NO_CONCLUSION",
1435
- message: "Argument has no designated conclusion premise.",
1436
- }));
1437
- }
1438
- else if (!this.premises.has(this.conclusionPremiseId)) {
1439
- issues.push(makeErrorIssue({
1440
- code: "ARGUMENT_CONCLUSION_NOT_FOUND",
1441
- message: `Conclusion premise "${this.conclusionPremiseId}" does not exist.`,
1442
- premiseId: this.conclusionPremiseId,
1443
- }));
1444
- }
1445
- const idToSymbols = new Map();
1446
- const symbolToIds = new Map();
1447
- for (const premise of this.listPremises()) {
1448
- const varById = new Map(premise.getVariables().map((v) => [v.id, v]));
1449
- for (const expr of premise.getExpressions()) {
1450
- if (expr.type !== "variable")
1451
- continue;
1452
- const variable = varById.get(expr.variableId);
1453
- if (!variable)
1454
- continue;
1455
- getOrCreate(idToSymbols, variable.id, () => new Set()).add(variable.symbol);
1456
- getOrCreate(symbolToIds, variable.symbol, () => new Set()).add(variable.id);
1457
- }
1458
- }
1459
- for (const [variableId, symbols] of idToSymbols) {
1460
- if (symbols.size > 1) {
1461
- issues.push(makeErrorIssue({
1462
- code: "ARGUMENT_VARIABLE_ID_SYMBOL_MISMATCH",
1463
- message: `Variable ID "${variableId}" is used with multiple symbols: ${sortedUnique(symbols).join(", ")}.`,
1464
- variableId,
1465
- }));
1466
- }
1467
- }
1468
- for (const [symbol, ids] of symbolToIds) {
1469
- if (ids.size > 1) {
1470
- issues.push(makeErrorIssue({
1471
- code: "ARGUMENT_VARIABLE_SYMBOL_AMBIGUOUS",
1472
- message: `Variable symbol "${symbol}" is used with multiple IDs: ${sortedUnique(ids).join(", ")}.`,
1473
- }));
1474
- }
1475
- }
1476
- for (const premise of this.listPremises()) {
1477
- const premiseValidation = premise.validateEvaluability();
1478
- issues.push(...premiseValidation.issues);
1479
- }
1480
- return makeValidationResult(issues);
1481
- }
1482
- /**
1483
- * Run fixed-point constraint propagation over accepted operators.
1484
- * Fills unknown (null) variable values based on operator semantics.
1485
- * Never overwrites user-assigned values (true/false).
1486
- */
1487
- propagateOperatorConstraints(assignment) {
1488
- const vars = { ...assignment.variables };
1489
- const opAssignments = assignment.operatorAssignments;
1490
- // Collect all expressions across all premises, indexed by id
1491
- const exprById = new Map();
1492
- // Children lookup: parentId -> sorted children
1493
- const childrenOf = new Map();
1494
- for (const pm of this.listPremises()) {
1495
- for (const expr of pm.getExpressions()) {
1496
- exprById.set(expr.id, expr);
1497
- }
1498
- // Build children map using getChildExpressions for each operator/formula
1499
- for (const expr of pm.getExpressions()) {
1500
- if (expr.type === "operator" || expr.type === "formula") {
1501
- childrenOf.set(expr.id, pm.getChildExpressions(expr.id));
1502
- }
1503
- }
1504
- }
1505
- /**
1506
- * Resolve the current Kleene value of an expression subtree
1507
- * given the current variable assignments. Does not force-accept
1508
- * nested operators — evaluates them normally via Kleene logic.
1509
- */
1510
- const resolveValue = (exprId) => {
1511
- const expr = exprById.get(exprId);
1512
- if (!expr)
1513
- return null;
1514
- if (expr.type === "variable") {
1515
- return (vars[expr
1516
- .variableId] ?? null);
1517
- }
1518
- if (expr.type === "formula") {
1519
- const children = childrenOf.get(expr.id) ?? [];
1520
- return children.length > 0 ? resolveValue(children[0].id) : null;
1521
- }
1522
- // operator
1523
- const op = expr
1524
- .operator;
1525
- const children = childrenOf.get(expr.id) ?? [];
1526
- switch (op) {
1527
- case "not":
1528
- return kleeneNot(resolveValue(children[0].id));
1529
- case "and":
1530
- return children.reduce((acc, child) => kleeneAnd(acc, resolveValue(child.id)), true);
1531
- case "or":
1532
- return children.reduce((acc, child) => kleeneOr(acc, resolveValue(child.id)), false);
1533
- case "implies": {
1534
- return kleeneImplies(resolveValue(children[0].id), resolveValue(children[1].id));
1535
- }
1536
- case "iff": {
1537
- return kleeneIff(resolveValue(children[0].id), resolveValue(children[1].id));
1538
- }
1539
- }
1540
- };
1541
- /**
1542
- * Unwrap formula wrappers to find the leaf variable expression.
1543
- * Returns the variableId if the leaf is a variable, otherwise null.
1544
- */
1545
- const resolveLeafVariableId = (expr) => {
1546
- if (expr.type === "variable") {
1547
- return expr.variableId;
1548
- }
1549
- if (expr.type === "formula") {
1550
- const children = childrenOf.get(expr.id) ?? [];
1551
- if (children.length > 0) {
1552
- return resolveLeafVariableId(children[0]);
1553
- }
1554
- }
1555
- return null;
1556
- };
1557
- // Track which variable IDs were explicitly set by the user
1558
- // (true or false). These are never overwritten by propagation.
1559
- const userAssigned = new Set();
1560
- for (const [varId, val] of Object.entries(vars)) {
1561
- if (val !== null && val !== undefined)
1562
- userAssigned.add(varId);
1563
- }
1564
- /**
1565
- * Try to set a child expression's variable to a value.
1566
- * Never overwrites user-assigned values.
1567
- * False overrides propagated true (rejection wins).
1568
- * Returns true if a value changed.
1569
- */
1570
- const trySetChild = (child, value) => {
1571
- const varId = resolveLeafVariableId(child);
1572
- if (varId == null || userAssigned.has(varId))
1573
- return false;
1574
- const current = vars[varId] ?? null;
1575
- if (current === null) {
1576
- vars[varId] = value;
1577
- return true;
1578
- }
1579
- // False overrides propagated true
1580
- if (value === false && current === true) {
1581
- vars[varId] = false;
1582
- return true;
1583
- }
1584
- return false;
1585
- };
1586
- // Two-phase propagation: rejections first (to establish false values),
1587
- // then acceptances (which only fill remaining unknowns).
1588
- // This prevents acceptance from deriving values through chains that
1589
- // are later invalidated by rejection.
1590
- for (const phase of ["rejected", "accepted"]) {
1591
- let changed = true;
1592
- while (changed) {
1593
- changed = false;
1594
- for (const [exprId, expr] of exprById) {
1595
- if (expr.type !== "operator")
1596
- continue;
1597
- const state = opAssignments[exprId];
1598
- if (state !== phase)
1599
- continue;
1600
- const op = expr.operator;
1601
- const children = childrenOf.get(exprId) ?? [];
1602
- if (state === "accepted") {
1603
- switch (op) {
1604
- case "not": {
1605
- // ¬A accepted (= true) => child must be false
1606
- if (children.length > 0) {
1607
- if (trySetChild(children[0], false))
1608
- changed = true;
1609
- }
1610
- break;
1611
- }
1612
- case "and": {
1613
- // A ∧ B accepted => all children must be true
1614
- for (const child of children) {
1615
- if (trySetChild(child, true))
1616
- changed = true;
1617
- }
1618
- break;
1619
- }
1620
- case "or": {
1621
- // A ∨ B accepted: if all-but-one are false, remaining must be true
1622
- const unknownChildren = [];
1623
- let allOthersAreFalse = true;
1624
- for (const child of children) {
1625
- const childValue = resolveValue(child.id);
1626
- if (childValue === null) {
1627
- unknownChildren.push(child);
1628
- }
1629
- else if (childValue !== false) {
1630
- allOthersAreFalse = false;
1631
- }
1632
- }
1633
- if (unknownChildren.length === 1 &&
1634
- allOthersAreFalse) {
1635
- if (trySetChild(unknownChildren[0], true))
1636
- changed = true;
1637
- }
1638
- break;
1639
- }
1640
- case "implies": {
1641
- // A → B accepted: if A=true => B=true; if B=false => A=false
1642
- if (children.length >= 2) {
1643
- const leftValue = resolveValue(children[0].id);
1644
- const rightValue = resolveValue(children[1].id);
1645
- if (leftValue === true) {
1646
- if (trySetChild(children[1], true))
1647
- changed = true;
1648
- }
1649
- if (rightValue === false) {
1650
- if (trySetChild(children[0], false))
1651
- changed = true;
1652
- }
1653
- }
1654
- break;
1655
- }
1656
- case "iff": {
1657
- // A ↔ B accepted: if A known => B matches; if B known => A matches
1658
- if (children.length >= 2) {
1659
- const leftValue = resolveValue(children[0].id);
1660
- const rightValue = resolveValue(children[1].id);
1661
- if (leftValue !== null) {
1662
- if (trySetChild(children[1], leftValue))
1663
- changed = true;
1664
- }
1665
- if (rightValue !== null) {
1666
- if (trySetChild(children[0], rightValue))
1667
- changed = true;
1668
- }
1669
- }
1670
- break;
1671
- }
1672
- }
1673
- }
1674
- else {
1675
- // state === "rejected" — expression forced false
1676
- switch (op) {
1677
- case "not": {
1678
- // ¬A rejected (= false) => child must be true
1679
- if (children.length > 0) {
1680
- if (trySetChild(children[0], true))
1681
- changed = true;
1682
- }
1683
- break;
1684
- }
1685
- case "and": {
1686
- // A ∧ B rejected (= false): if all-but-one are true, remaining must be false
1687
- const unknownChildren = [];
1688
- let allOthersAreTrue = true;
1689
- for (const child of children) {
1690
- const childValue = resolveValue(child.id);
1691
- if (childValue === null) {
1692
- unknownChildren.push(child);
1693
- }
1694
- else if (childValue !== true) {
1695
- allOthersAreTrue = false;
1696
- }
1697
- }
1698
- if (unknownChildren.length === 1 &&
1699
- allOthersAreTrue) {
1700
- if (trySetChild(unknownChildren[0], false))
1701
- changed = true;
1702
- }
1703
- break;
1704
- }
1705
- case "or": {
1706
- // A ∨ B rejected (= false) => all children must be false
1707
- for (const child of children) {
1708
- if (trySetChild(child, false))
1709
- changed = true;
1710
- }
1711
- break;
1712
- }
1713
- case "implies": {
1714
- // A → B rejected (= false) => A must be true, B must be false
1715
- if (children.length >= 2) {
1716
- if (trySetChild(children[0], true))
1717
- changed = true;
1718
- if (trySetChild(children[1], false))
1719
- changed = true;
1720
- }
1721
- break;
1722
- }
1723
- case "iff": {
1724
- // A ↔ B rejected (= false): if A known => B is opposite; if B known => A is opposite
1725
- if (children.length >= 2) {
1726
- const leftValue = resolveValue(children[0].id);
1727
- const rightValue = resolveValue(children[1].id);
1728
- if (leftValue !== null) {
1729
- if (trySetChild(children[1], !leftValue))
1730
- changed = true;
1731
- }
1732
- if (rightValue !== null) {
1733
- if (trySetChild(children[0], !rightValue))
1734
- changed = true;
1735
- }
1736
- }
1737
- break;
1738
- }
1739
- }
1740
- }
1741
- }
1742
- }
1743
- }
1744
- return vars;
1190
+ return validateArgumentEvaluabilityStandalone(this.asValidationContext());
1745
1191
  }
1746
- evaluate(assignment, options) {
1747
- const validateFirst = options?.validateFirst ?? true;
1748
- if (validateFirst) {
1749
- const validation = this.validateEvaluability();
1750
- if (!validation.ok) {
1192
+ asValidationContext() {
1193
+ return {
1194
+ argumentId: this.argument.id,
1195
+ argumentVersion: this.argument.version,
1196
+ conclusionPremiseId: this.conclusionPremiseId,
1197
+ getArgument: () => this.getArgument(),
1198
+ getVariables: () => this.variables.toArray(),
1199
+ listPremises: () => this.listPremises(),
1200
+ hasPremise: (premiseId) => this.premises.has(premiseId),
1201
+ lookupClaim: (claimId, claimVersion) => this.claimLibrary.get(claimId, claimVersion),
1202
+ flushAndGetChecksumDeltas: () => {
1203
+ const savedMeta = this.cachedMetaChecksum;
1204
+ const savedDescendant = this.cachedDescendantChecksum;
1205
+ const savedCombined = this.cachedCombinedChecksum;
1206
+ this.flushChecksums();
1751
1207
  return {
1752
- ok: false,
1753
- validation,
1208
+ savedMeta,
1209
+ savedDescendant,
1210
+ savedCombined,
1211
+ currentMeta: this.cachedMetaChecksum,
1212
+ currentDescendant: this.cachedDescendantChecksum,
1213
+ currentCombined: this.cachedCombinedChecksum,
1754
1214
  };
1755
- }
1756
- }
1757
- const conclusion = this.getConclusionPremise();
1758
- if (!conclusion) {
1759
- return {
1760
- ok: false,
1761
- validation: makeValidationResult([
1762
- makeErrorIssue({
1763
- code: "ARGUMENT_NO_CONCLUSION",
1764
- message: "Argument has no designated conclusion premise.",
1765
- }),
1766
- ]),
1767
- };
1768
- }
1769
- const supportingPremises = this.listSupportingPremises();
1770
- const supportingIds = new Set(supportingPremises.map((pm) => pm.getId()));
1771
- const constraintPremises = this.listPremises().filter((pm) => pm.getId() !== this.conclusionPremiseId &&
1772
- !supportingIds.has(pm.getId()));
1773
- const allRelevantPremises = [
1774
- conclusion,
1775
- ...supportingPremises,
1776
- ...constraintPremises,
1777
- ];
1778
- const allVariableIds = [
1779
- ...new Set(allRelevantPremises.flatMap((pm) => pm
1780
- .getExpressions()
1781
- .filter((expr) => expr.type === "variable")
1782
- .map((expr) => expr.variableId))),
1783
- ].sort();
1784
- // Claim-bound and externally-bound premise variables get truth-table columns;
1785
- // internally-bound premise variables are resolved lazily.
1786
- const referencedVariableIds = allVariableIds.filter((vid) => {
1787
- const v = this.variables.getVariable(vid);
1788
- if (v == null)
1789
- return false;
1790
- if (isClaimBound(v))
1791
- return true;
1792
- if (isPremiseBound(v) && v.boundArgumentId !== this.argument.id)
1793
- return true;
1794
- return false;
1795
- });
1796
- // Run operator constraint propagation
1797
- const propagatedVars = this.propagateOperatorConstraints(assignment);
1798
- const propagatedAssignment = {
1799
- variables: propagatedVars,
1800
- operatorAssignments: assignment.operatorAssignments,
1215
+ },
1216
+ validateVariables: () => this.variables.validate(),
1217
+ wouldCreateCycle: (variableId, premiseId, visited) => this.wouldCreateCycle(variableId, premiseId, visited),
1801
1218
  };
1802
- try {
1803
- // Build a resolver that lazily evaluates premise-bound variables
1804
- // by evaluating their bound premise's expression tree under the
1805
- // same assignment. Results are cached per-variable per-evaluate call.
1806
- const resolverCache = new Map();
1807
- const resolver = (variableId) => {
1808
- if (resolverCache.has(variableId)) {
1809
- return resolverCache.get(variableId);
1810
- }
1811
- const variable = this.variables.getVariable(variableId);
1812
- if (!variable ||
1813
- !isPremiseBound(variable) ||
1814
- variable.boundArgumentId !== this.argument.id) {
1815
- // Claim-bound or externally-bound: read from assignment
1816
- return propagatedAssignment.variables[variableId] ?? null;
1817
- }
1818
- // Internal premise-bound: lazy resolution
1819
- const boundPremiseId = variable.boundPremiseId;
1820
- const boundPremise = this.premises.get(boundPremiseId);
1821
- if (!boundPremise) {
1822
- resolverCache.set(variableId, null);
1823
- return null;
1824
- }
1825
- const premiseResult = boundPremise.evaluate(propagatedAssignment, {
1826
- resolver,
1827
- });
1828
- const value = premiseResult?.rootValue ?? null;
1829
- resolverCache.set(variableId, value);
1830
- return value;
1831
- };
1832
- const evalOpts = {
1833
- strictUnknownKeys: options?.strictUnknownAssignmentKeys ?? false,
1834
- resolver,
1835
- };
1836
- const conclusionEvaluation = conclusion.evaluate(propagatedAssignment, evalOpts);
1837
- const supportingEvaluations = supportingPremises.map((pm) => pm.evaluate(propagatedAssignment, evalOpts));
1838
- const constraintEvaluations = constraintPremises.map((pm) => pm.evaluate(propagatedAssignment, evalOpts));
1839
- const isAdmissibleAssignment = constraintEvaluations.reduce((acc, result) => kleeneAnd(acc, result.rootValue ?? null), true);
1840
- const allSupportingPremisesTrue = supportingEvaluations.reduce((acc, result) => kleeneAnd(acc, result.rootValue ?? null), true);
1841
- const conclusionTrue = conclusionEvaluation.rootValue ?? null;
1842
- const isCounterexample = kleeneAnd(isAdmissibleAssignment, kleeneAnd(allSupportingPremisesTrue, kleeneNot(conclusionTrue)));
1843
- const includeExpressionValues = options?.includeExpressionValues ?? true;
1844
- const includeDiagnostics = options?.includeDiagnostics ?? true;
1845
- const strip = (result) => ({
1846
- ...result,
1847
- expressionValues: includeExpressionValues
1848
- ? result.expressionValues
1849
- : {},
1850
- inferenceDiagnostic: includeDiagnostics
1851
- ? result.inferenceDiagnostic
1852
- : undefined,
1853
- });
1854
- return {
1855
- ok: true,
1856
- assignment: {
1857
- variables: { ...propagatedAssignment.variables },
1858
- operatorAssignments: {
1859
- ...propagatedAssignment.operatorAssignments,
1860
- },
1861
- },
1862
- referencedVariableIds,
1863
- conclusion: strip(conclusionEvaluation),
1864
- supportingPremises: supportingEvaluations.map(strip),
1865
- constraintPremises: constraintEvaluations.map(strip),
1866
- isAdmissibleAssignment,
1867
- allSupportingPremisesTrue,
1868
- conclusionTrue,
1869
- isCounterexample,
1870
- preservesTruthUnderAssignment: kleeneNot(isCounterexample),
1871
- };
1872
- }
1873
- catch (error) {
1874
- return {
1875
- ok: false,
1876
- validation: makeValidationResult([
1877
- makeErrorIssue({
1878
- code: "ASSIGNMENT_MISSING_VARIABLE",
1879
- message: error instanceof Error
1880
- ? error.message
1881
- : "Argument evaluation failed.",
1882
- }),
1883
- ]),
1884
- };
1885
- }
1886
1219
  }
1887
- checkValidity(options) {
1888
- const validateFirst = options?.validateFirst ?? true;
1889
- if (validateFirst) {
1890
- const validation = this.validateEvaluability();
1891
- if (!validation.ok) {
1892
- return {
1893
- ok: false,
1894
- validation,
1895
- };
1896
- }
1897
- }
1898
- const conclusion = this.getConclusionPremise();
1899
- if (!conclusion) {
1900
- return {
1901
- ok: false,
1902
- validation: makeValidationResult([
1903
- makeErrorIssue({
1904
- code: "ARGUMENT_NO_CONCLUSION",
1905
- message: "Argument has no designated conclusion premise.",
1906
- }),
1907
- ]),
1908
- };
1909
- }
1910
- const supportingPremises = this.listSupportingPremises();
1911
- const supportingIds = new Set(supportingPremises.map((pm) => pm.getId()));
1912
- const constraintPremises = this.listPremises().filter((pm) => pm.getId() !== this.conclusionPremiseId &&
1913
- !supportingIds.has(pm.getId()));
1914
- const allVariableIdsForCheck = [
1915
- ...new Set([
1916
- conclusion,
1917
- ...supportingPremises,
1918
- ...constraintPremises,
1919
- ].flatMap((pm) => pm
1920
- .getExpressions()
1921
- .filter((expr) => expr.type === "variable")
1922
- .map((expr) => expr.variableId))),
1923
- ].sort();
1924
- // Claim-bound and externally-bound premise variables get truth-table columns;
1925
- // internally-bound premise variables are resolved lazily.
1926
- const checkedVariableIds = allVariableIdsForCheck.filter((vid) => {
1927
- const v = this.variables.getVariable(vid);
1928
- if (v == null)
1929
- return false;
1930
- if (isClaimBound(v))
1931
- return true;
1932
- if (isPremiseBound(v) && v.boundArgumentId !== this.argument.id)
1933
- return true;
1934
- return false;
1935
- });
1936
- if (options?.maxVariables !== undefined &&
1937
- checkedVariableIds.length > options.maxVariables) {
1938
- return {
1939
- ok: false,
1940
- validation: makeValidationResult([
1941
- makeErrorIssue({
1942
- code: "ASSIGNMENT_UNKNOWN_VARIABLE",
1943
- message: `Validity check requires ${checkedVariableIds.length} variables, exceeding limit ${options.maxVariables}.`,
1944
- }),
1945
- ]),
1946
- };
1947
- }
1948
- const mode = options?.mode ?? "firstCounterexample";
1949
- const maxAssignmentsChecked = options?.maxAssignmentsChecked;
1950
- const counterexamples = [];
1951
- let numAssignmentsChecked = 0;
1952
- let numAdmissibleAssignments = 0;
1953
- let truncated = false;
1954
- const totalAssignments = 2 ** checkedVariableIds.length;
1955
- for (let mask = 0; mask < totalAssignments; mask++) {
1956
- if (maxAssignmentsChecked !== undefined &&
1957
- numAssignmentsChecked >= maxAssignmentsChecked) {
1958
- truncated = true;
1959
- break;
1960
- }
1961
- const assignment = {
1962
- variables: {},
1963
- operatorAssignments: {},
1964
- };
1965
- for (let i = 0; i < checkedVariableIds.length; i++) {
1966
- assignment.variables[checkedVariableIds[i]] = Boolean(mask & (1 << i));
1967
- }
1968
- const result = this.evaluate(assignment, {
1969
- validateFirst: false,
1970
- includeExpressionValues: options?.includeCounterexampleEvaluations ?? false,
1971
- includeDiagnostics: options?.includeCounterexampleEvaluations ?? false,
1972
- });
1973
- if (!result.ok) {
1974
- return {
1975
- ok: false,
1976
- validation: result.validation,
1977
- };
1978
- }
1979
- numAssignmentsChecked += 1;
1980
- if (result.isAdmissibleAssignment === true) {
1981
- numAdmissibleAssignments += 1;
1982
- }
1983
- if (result.isCounterexample === true) {
1984
- counterexamples.push({
1985
- assignment: result.assignment,
1986
- result,
1987
- });
1988
- if (mode === "firstCounterexample") {
1989
- break;
1990
- }
1991
- }
1992
- }
1993
- const foundCounterexample = counterexamples.length > 0;
1994
- const fullyChecked = !truncated &&
1995
- (mode === "exhaustive" ||
1996
- (mode === "firstCounterexample" && !foundCounterexample));
1220
+ asEvaluationContext() {
1997
1221
  return {
1998
- ok: true,
1999
- isValid: foundCounterexample
2000
- ? false
2001
- : fullyChecked
2002
- ? true
2003
- : undefined,
2004
- checkedVariableIds,
2005
- numAssignmentsChecked,
2006
- numAdmissibleAssignments,
2007
- counterexamples,
2008
- truncated,
1222
+ argumentId: this.argument.id,
1223
+ conclusionPremiseId: this.conclusionPremiseId,
1224
+ getConclusionPremise: () => this.getConclusionPremise(),
1225
+ listSupportingPremises: () => this.listSupportingPremises(),
1226
+ listPremises: () => this.listPremises(),
1227
+ getVariable: (id) => this.variables.getVariable(id),
1228
+ getPremise: (id) => this.premises.get(id),
1229
+ validateEvaluability: () => this.validateEvaluability(),
2009
1230
  };
2010
1231
  }
1232
+ evaluate(assignment, options) {
1233
+ return evaluateArgumentStandalone(this.asEvaluationContext(), assignment, options);
1234
+ }
1235
+ checkValidity(options) {
1236
+ return checkArgumentValidityStandalone(this.asEvaluationContext(), options);
1237
+ }
2011
1238
  // -----------------------------------------------------------------
2012
1239
  // Forking
2013
1240
  // -----------------------------------------------------------------