js-rigor: reliability flows through every rule kind; multi_hop value collection fixed

Systemic reliability gap found by the probe sweep: the compiled evaluation
paths never emitted the reliability the engine computes.

- Compiled _evaluateDirect omitted the relation's reliability, and the
  chain/multi_hop rules hardcoded reliability: 1.0 — so check() results
  reported 1.0 for any rule whose decision came through a chain, multi_hop,
  union, intersection, exclusion, or defeasible combination.
- The chain and multi_hop traversals now track per-path reliability (product
  of edge reliabilities) and report the winning path's value; the compiled
  and fallback logical operators (union/intersection/exclusion, direct_list
  fast path, early exits) report the selected child's reliability
  (max/min child or OWA trace index; exclusion multiplies both legs), and
  normal-mode defeasible combines base x requires x defeater reliabilities.
- The checker's logical fast path dropped collectedValues from union/
  intersection/exclusion results; it now passes them through.
- MultiHopRule.valueManager was read off relationManager where the real
  arbiter keeps it on the arbiter — collectValues: true on a multi_hop rule
  with a value-carrying edge crashed the evaluation (error result, silent
  denial). Now resolved at the arbiter level with a relationManager
  fallback for stubs.

Campaign pins: reliability per kind (chain/multi_hop product, union/intersection
selected child, exclusion/defeasible product), and multi_hop value collection
through persistent and partial contexts.
This commit is contained in:
John Dvorak
2026-08-01 09:52:31 -07:00
parent f0dc14fb72
commit 4fd4e20bd0
53 changed files with 361 additions and 187 deletions
@@ -339,6 +339,7 @@ export class AuthorizationChecker {
const finalResult = { const finalResult = {
possibility: resPossibility || 0, possibility: resPossibility || 0,
reliability: res.reliability !== undefined ? res.reliability : 1.0, reliability: res.reliability !== undefined ? res.reliability : 1.0,
...(collectValues && res.collectedValues && Array.isArray(res.collectedValues) && { collectedValues: res.collectedValues }),
...(includeMeta && { ...(includeMeta && {
meta: { meta: {
...restMeta, // Spread meta without allow/deny ...restMeta, // Spread meta without allow/deny
+51
View File
@@ -142,6 +142,7 @@ export class CompiledEvaluator {
}; };
const result = { const result = {
possibility: relationStrength, possibility: relationStrength,
reliability: directRel.reliability !== undefined ? directRel.reliability : 1.0,
possibility_allow: relationStrength, possibility_allow: relationStrength,
possibility_deny: 0, possibility_deny: 0,
...(includeMeta && { ...(includeMeta && {
@@ -404,6 +405,7 @@ export class CompiledEvaluator {
const children = compiled.children || [compiled]; const children = compiled.children || [compiled];
const possibilities = []; const possibilities = [];
const reliabilities = [];
const metas = []; const metas = [];
const remediationOptions = []; const remediationOptions = [];
const allCollectedValues = collectValues ? [] : null; const allCollectedValues = collectValues ? [] : null;
@@ -418,6 +420,7 @@ export class CompiledEvaluator {
mergeRemediationOptions(remediationOptions, extractRemediation(res)); mergeRemediationOptions(remediationOptions, extractRemediation(res));
possibilities.push(res.possibility); possibilities.push(res.possibility);
reliabilities.push(res.reliability !== undefined ? res.reliability : 1.0);
metas.push(includeMeta ? res.meta : null); metas.push(includeMeta ? res.meta : null);
if (collectValues && res.collectedValues && Array.isArray(res.collectedValues)) { if (collectValues && res.collectedValues && Array.isArray(res.collectedValues)) {
@@ -431,6 +434,7 @@ export class CompiledEvaluator {
const remediation = buildRemediation(extractRemediation(res)); const remediation = buildRemediation(extractRemediation(res));
return { return {
possibility: res.possibility, possibility: res.possibility,
reliability: res.reliability !== undefined ? res.reliability : 1.0,
...(collectValues && { collectedValues: allCollectedValues }), ...(collectValues && { collectedValues: allCollectedValues }),
...(includeMeta && { meta: res.meta }), ...(includeMeta && { meta: res.meta }),
...(remediation ? { remediation } : {}) ...(remediation ? { remediation } : {})
@@ -441,6 +445,7 @@ export class CompiledEvaluator {
if (!possibilities.length) { if (!possibilities.length) {
return { return {
possibility: 0, possibility: 0,
reliability: 1.0,
...(collectValues && { collectedValues: allCollectedValues }), ...(collectValues && { collectedValues: allCollectedValues }),
...(includeMeta && { meta: { operation: 'union', childCount: 0 } }) ...(includeMeta && { meta: { operation: 'union', childCount: 0 } })
}; };
@@ -459,8 +464,19 @@ export class CompiledEvaluator {
const remediation = result.value === 0 const remediation = result.value === 0
? buildRemediation(remediationOptions.length > 0 ? { status: 'required', options: remediationOptions } : null, { status: 'required' }) ? buildRemediation(remediationOptions.length > 0 ? { status: 'required', options: remediationOptions } : null, { status: 'required' })
: null; : null;
let unionReliability = 1.0;
if (includeOwaTrace && result.trace && typeof result.trace.selectedIndex === 'number') {
unionReliability = reliabilities[result.trace.selectedIndex] ?? 1.0;
} else if ((compiled.aggregator || 'max') === 'min') {
const minP = Math.min(...possibilities);
unionReliability = reliabilities[possibilities.indexOf(minP)] ?? 1.0;
} else {
const maxP = Math.max(...possibilities);
unionReliability = reliabilities[possibilities.indexOf(maxP)] ?? 1.0;
}
return { return {
possibility: result.value, possibility: result.value,
reliability: unionReliability,
...(collectValues && { collectedValues: allCollectedValues }), ...(collectValues && { collectedValues: allCollectedValues }),
...(includeMeta && { ...(includeMeta && {
meta: { meta: {
@@ -495,6 +511,7 @@ export class CompiledEvaluator {
return this._evaluateDirectList(compiled, userId, userKey, effectiveObjectId, effectiveObjectKey, visited, currentRelation, options, 'intersection'); return this._evaluateDirectList(compiled, userId, userKey, effectiveObjectId, effectiveObjectKey, visited, currentRelation, options, 'intersection');
} }
const possibilities = []; const possibilities = [];
const reliabilities = [];
const metas = []; const metas = [];
const remediationOptions = []; const remediationOptions = [];
const allCollectedValues = collectValues ? [] : null; const allCollectedValues = collectValues ? [] : null;
@@ -508,6 +525,7 @@ export class CompiledEvaluator {
const childVisited = new Set(visited); const childVisited = new Set(visited);
const res = this.evaluate(child, userId, userKey, objectId, objectKey, childVisited, currentRelation, options); const res = this.evaluate(child, userId, userKey, objectId, objectKey, childVisited, currentRelation, options);
possibilities.push(res.possibility); possibilities.push(res.possibility);
reliabilities.push(res.reliability !== undefined ? res.reliability : 1.0);
metas.push(includeMeta ? res.meta : null); metas.push(includeMeta ? res.meta : null);
mergeRemediationOptions(remediationOptions, extractRemediation(res)); mergeRemediationOptions(remediationOptions, extractRemediation(res));
@@ -522,6 +540,7 @@ export class CompiledEvaluator {
if (fastPath && minPossibility !== null && res.possibility < minPossibility) { if (fastPath && minPossibility !== null && res.possibility < minPossibility) {
return { return {
possibility: res.possibility, possibility: res.possibility,
reliability: res.reliability !== undefined ? res.reliability : 1.0,
...(collectValues && { collectedValues: allCollectedValues }), ...(collectValues && { collectedValues: allCollectedValues }),
...(includeMeta && { meta: res.meta }) ...(includeMeta && { meta: res.meta })
}; };
@@ -531,6 +550,7 @@ export class CompiledEvaluator {
if (!possibilities.length) { if (!possibilities.length) {
return { return {
possibility: 0, possibility: 0,
reliability: 1.0,
...(collectValues && { collectedValues: allCollectedValues }), ...(collectValues && { collectedValues: allCollectedValues }),
...(includeMeta && { meta: { operation: 'intersection', childCount: 0 } }) ...(includeMeta && { meta: { operation: 'intersection', childCount: 0 } })
}; };
@@ -554,8 +574,19 @@ export class CompiledEvaluator {
const remediation = result.value === 0 const remediation = result.value === 0
? buildRemediation(remediationOptions.length > 0 ? { status: 'required', options: remediationOptions } : null, { status: 'required' }) ? buildRemediation(remediationOptions.length > 0 ? { status: 'required', options: remediationOptions } : null, { status: 'required' })
: null; : null;
let intersectionReliability = 1.0;
if (includeOwaTrace && result.trace && typeof result.trace.selectedIndex === 'number') {
intersectionReliability = reliabilities[result.trace.selectedIndex] ?? 1.0;
} else if ((compiled.aggregator || 'min') === 'max') {
const maxP = Math.max(...possibilities);
intersectionReliability = reliabilities[possibilities.indexOf(maxP)] ?? 1.0;
} else {
const minP = Math.min(...possibilities);
intersectionReliability = reliabilities[possibilities.indexOf(minP)] ?? 1.0;
}
return { return {
possibility: result.value, possibility: result.value,
reliability: intersectionReliability,
...(collectValues && { collectedValues: allCollectedValues }), ...(collectValues && { collectedValues: allCollectedValues }),
...(includeMeta && { ...(includeMeta && {
meta: { meta: {
@@ -624,6 +655,9 @@ export class CompiledEvaluator {
return { return {
possibility, possibility,
// Both legs contribute to the decision, so their reliabilities
// multiply (consistent with TTU/chain path semantics).
reliability: (a.reliability !== undefined ? a.reliability : 1.0) * (b.reliability !== undefined ? b.reliability : 1.0),
...(collectValues && { collectedValues: allCollectedValues }), ...(collectValues && { collectedValues: allCollectedValues }),
...(includeMeta && { ...(includeMeta && {
meta: { meta: {
@@ -652,6 +686,7 @@ export class CompiledEvaluator {
const owaTraceOptions = includeOwaTrace ? { includeTrace: true } : null; const owaTraceOptions = includeOwaTrace ? { includeTrace: true } : null;
const possibilities = []; const possibilities = [];
const reliabilities = [];
const metas = []; const metas = [];
const allCollectedValues = collectValues ? [] : null; const allCollectedValues = collectValues ? [] : null;
const directRules = compiled._optimized.direct; const directRules = compiled._optimized.direct;
@@ -666,6 +701,7 @@ export class CompiledEvaluator {
const possibility = directRel ? directRel.possibility : 0; const possibility = directRel ? directRel.possibility : 0;
possibilities.push(possibility); possibilities.push(possibility);
reliabilities.push(directRel ? (directRel.reliability !== undefined ? directRel.reliability : 1.0) : 1.0);
if (includeMeta) { if (includeMeta) {
const _src = directRel ? (directRel.source || 'persistent') : null; const _src = directRel ? (directRel.source || 'persistent') : null;
const _allowMeta = directRel ? { const _allowMeta = directRel ? {
@@ -717,6 +753,7 @@ export class CompiledEvaluator {
if (op === 'union' && possibility >= minPossibility) { if (op === 'union' && possibility >= minPossibility) {
return { return {
possibility, possibility,
reliability: directRel ? (directRel.reliability !== undefined ? directRel.reliability : 1.0) : 1.0,
...(collectValues && { collectedValues: allCollectedValues }), ...(collectValues && { collectedValues: allCollectedValues }),
...(includeMeta && { meta: metas[metas.length - 1] }) ...(includeMeta && { meta: metas[metas.length - 1] })
}; };
@@ -724,6 +761,7 @@ export class CompiledEvaluator {
if (op === 'intersection' && possibility < minPossibility) { if (op === 'intersection' && possibility < minPossibility) {
return { return {
possibility, possibility,
reliability: directRel ? (directRel.reliability !== undefined ? directRel.reliability : 1.0) : 1.0,
...(collectValues && { collectedValues: allCollectedValues }), ...(collectValues && { collectedValues: allCollectedValues }),
...(includeMeta && { meta: metas[metas.length - 1] }) ...(includeMeta && { meta: metas[metas.length - 1] })
}; };
@@ -734,6 +772,7 @@ export class CompiledEvaluator {
if (!possibilities.length) { if (!possibilities.length) {
return { return {
possibility: 0, possibility: 0,
reliability: 1.0,
...(collectValues && { collectedValues: allCollectedValues }), ...(collectValues && { collectedValues: allCollectedValues }),
...(includeMeta && { meta: { operation: op, childCount: 0 } }) ...(includeMeta && { meta: { operation: op, childCount: 0 } })
}; };
@@ -745,8 +784,20 @@ export class CompiledEvaluator {
const aggregator = op === 'intersection' ? (compiled.aggregator || 'min') : (compiled.aggregator || 'max'); const aggregator = op === 'intersection' ? (compiled.aggregator || 'min') : (compiled.aggregator || 'max');
const result = OWAFusion.fuseWithMeta(possibilities, metas, weights, aggregator, true, owaTraceOptions); const result = OWAFusion.fuseWithMeta(possibilities, metas, weights, aggregator, true, owaTraceOptions);
let listReliability = 1.0;
if (includeOwaTrace && result.trace && typeof result.trace.selectedIndex === 'number') {
listReliability = reliabilities[result.trace.selectedIndex] ?? 1.0;
} else if (aggregator === 'min') {
const minP = Math.min(...possibilities);
listReliability = reliabilities[possibilities.indexOf(minP)] ?? 1.0;
} else {
const maxP = Math.max(...possibilities);
listReliability = reliabilities[possibilities.indexOf(maxP)] ?? 1.0;
}
return { return {
possibility: result.value, possibility: result.value,
reliability: listReliability,
...(collectValues && { collectedValues: allCollectedValues }), ...(collectValues && { collectedValues: allCollectedValues }),
...(includeMeta && { ...(includeMeta && {
meta: { meta: {
+15 -4
View File
@@ -239,13 +239,16 @@ export class ChainRule extends BaseRule {
// Deduplicate: keep best path per node // Deduplicate: keep best path per node
const existing = pathMap.get(nextId); const existing = pathMap.get(nextId);
if (existing && existing.possibility >= nextPossibility) continue; const nextReliability = (currentPath.reliability ?? 1.0) * (rel.reliability ?? 1.0);
if (existing && existing.possibility > nextPossibility) continue;
if (existing && existing.possibility === nextPossibility && (existing.reliability ?? 1.0) >= nextReliability) continue;
// Create extended path // Create extended path
const extendedPath = { const extendedPath = {
id: nextId, id: nextId,
key: nextKey, key: nextKey,
possibility: nextPossibility, possibility: nextPossibility,
reliability: nextReliability,
path: [...currentPath.path, nextKey], path: [...currentPath.path, nextKey],
pathEntities: [...currentPath.pathEntities, { id: nextId, key: nextKey, source: rel.source || 'persistent' }] pathEntities: [...currentPath.pathEntities, { id: nextId, key: nextKey, source: rel.source || 'persistent' }]
}; };
@@ -269,10 +272,18 @@ export class ChainRule extends BaseRule {
const targetId = reverse ? userIdNum : objectIdNum; const targetId = reverse ? userIdNum : objectIdNum;
const targetPaths = currentPaths.filter(path => path.id === targetId); const targetPaths = currentPaths.filter(path => path.id === targetId);
let finalPossibility = 0; let finalPossibility = 0;
let finalReliability = 1.0;
if (targetPaths.length > 0) { if (targetPaths.length > 0) {
// Use MAX across paths (disjunctive) // Use MAX across paths (disjunctive); the winning path's reliability
finalPossibility = Math.max(...targetPaths.map(path => path.possibility)); // is the product of its edges' reliabilities (MIN along the chain).
const best = targetPaths.reduce((a, b) =>
(b.possibility > a.possibility ||
(b.possibility === a.possibility && (b.reliability ?? 1.0) > (a.reliability ?? 1.0)))
? b : a
);
finalPossibility = best.possibility;
finalReliability = best.reliability ?? 1.0;
} }
if (fastPath && finalPossibility < minPossibility) { if (fastPath && finalPossibility < minPossibility) {
@@ -338,7 +349,7 @@ export class ChainRule extends BaseRule {
// Build authorization result with single possibility value // Build authorization result with single possibility value
const authResult = { const authResult = {
possibility: finalPossibility, possibility: finalPossibility,
reliability: 1.0, reliability: finalReliability,
...(includeMeta && { ...(includeMeta && {
meta: finalPossibility > 0 ? { meta: finalPossibility > 0 ? {
ruleType: 'chain', ruleType: 'chain',
+47 -2
View File
@@ -791,13 +791,19 @@ export class LogicalOperators extends BaseRule {
let possibility = 0; let possibility = 0;
let resultMeta = {}; let resultMeta = {};
// Reliability mirrors the possibility combination: the base leg's
// reliability is eroded by requirements and defeaters (both legs of
// each combination matter), consistent with TTU path semantics.
let reliability = 1.0;
// NEVER rules override everything when their evidence possibility exceeds threshold (0.5 default) // NEVER rules override everything when their evidence possibility exceeds threshold (0.5 default)
if (neverResult && neverResult.possibility >= 0.5) { if (neverResult && neverResult.possibility >= 0.5) {
possibility = 0; possibility = 0;
reliability = neverResult.reliability !== undefined ? neverResult.reliability : 1.0;
resultMeta = { ...neverResult.meta, never: true }; resultMeta = { ...neverResult.meta, never: true };
return { return {
possibility: 0, possibility: 0,
reliability,
collectedValues: allCollectedValues, collectedValues: allCollectedValues,
meta: { meta: {
...resultMeta, ...resultMeta,
@@ -817,6 +823,7 @@ export class LogicalOperators extends BaseRule {
// Start with defeasible rules as base // Start with defeasible rules as base
if (defeasibleResult) { if (defeasibleResult) {
possibility = defeasibleResult.possibility; possibility = defeasibleResult.possibility;
reliability = defeasibleResult.reliability !== undefined ? defeasibleResult.reliability : 1.0;
resultMeta = { ...defeasibleResult.meta }; resultMeta = { ...defeasibleResult.meta };
} }
@@ -824,6 +831,7 @@ export class LogicalOperators extends BaseRule {
if (strictResult) { if (strictResult) {
possibility = Math.max(possibility, strictResult.possibility); possibility = Math.max(possibility, strictResult.possibility);
if (strictResult.possibility > (defeasibleResult?.possibility || 0)) { if (strictResult.possibility > (defeasibleResult?.possibility || 0)) {
reliability = strictResult.reliability !== undefined ? strictResult.reliability : 1.0;
resultMeta = { ...strictResult.meta }; resultMeta = { ...strictResult.meta };
} }
} }
@@ -832,6 +840,7 @@ export class LogicalOperators extends BaseRule {
if (requiresResult) { if (requiresResult) {
const reqStrength = requiresResult.possibility; const reqStrength = requiresResult.possibility;
possibility = possibility * reqStrength; // Requirements failure reduces possibility possibility = possibility * reqStrength; // Requirements failure reduces possibility
reliability = reliability * (requiresResult.reliability !== undefined ? requiresResult.reliability : 1.0);
if (reqStrength < 0.5) { if (reqStrength < 0.5) {
resultMeta = { ...requiresResult.meta, requirementsFailed: requiresResult.meta }; resultMeta = { ...requiresResult.meta, requirementsFailed: requiresResult.meta };
} }
@@ -841,6 +850,7 @@ export class LogicalOperators extends BaseRule {
if (defeatersResult) { if (defeatersResult) {
const defeatStrength = defeatersResult.possibility; const defeatStrength = defeatersResult.possibility;
possibility = possibility * (1 - defeatStrength); // Defeaters erode possibility possibility = possibility * (1 - defeatStrength); // Defeaters erode possibility
reliability = reliability * (defeatersResult.reliability !== undefined ? defeatersResult.reliability : 1.0);
if (defeatStrength > 0.5) { if (defeatStrength > 0.5) {
resultMeta = { ...defeatersResult.meta, defeatedBy: defeatersResult.meta }; resultMeta = { ...defeatersResult.meta, defeatedBy: defeatersResult.meta };
} }
@@ -848,6 +858,7 @@ export class LogicalOperators extends BaseRule {
return { return {
possibility: Math.max(0, Math.min(1, possibility)), possibility: Math.max(0, Math.min(1, possibility)),
reliability,
collectedValues: allCollectedValues, collectedValues: allCollectedValues,
meta: { meta: {
...resultMeta, ...resultMeta,
@@ -874,7 +885,7 @@ export class LogicalOperators extends BaseRule {
const unionConfig = normalizedRule.union; const unionConfig = normalizedRule.union;
const childRules = unionConfig.rules || []; const childRules = unionConfig.rules || [];
let possibilities = [], metas = [], reasons = []; let possibilities = [], reliabilities = [], metas = [], reasons = [];
const remediationOptions = []; const remediationOptions = [];
const { fastPath = false, minPossibility = null, valueContext = null, collectValues = false, includeMeta = true, trackEvaluation = false } = options; const { fastPath = false, minPossibility = null, valueContext = null, collectValues = false, includeMeta = true, trackEvaluation = false } = options;
const allCollectedValues = collectValues ? [] : null; // Track collected values from all child rules const allCollectedValues = collectValues ? [] : null; // Track collected values from all child rules
@@ -893,6 +904,7 @@ export class LogicalOperators extends BaseRule {
if (res.reason === 'cycle') reasons.push('cycle'); if (res.reason === 'cycle') reasons.push('cycle');
mergeRemediationOptions(remediationOptions, extractRemediation(res)); mergeRemediationOptions(remediationOptions, extractRemediation(res));
possibilities.push(res.possibility); possibilities.push(res.possibility);
reliabilities.push(res.reliability !== undefined ? res.reliability : 1.0);
metas.push(includeMeta ? res.meta : null); metas.push(includeMeta ? res.meta : null);
// Collect values from child rule results // Collect values from child rule results
@@ -917,6 +929,7 @@ export class LogicalOperators extends BaseRule {
const remediation = buildRemediation(extractRemediation(res)); const remediation = buildRemediation(extractRemediation(res));
return { return {
possibility: res.possibility, possibility: res.possibility,
reliability: res.reliability !== undefined ? res.reliability : 1.0,
...(collectValues && { collectedValues: allCollectedValues }), // Include all collected values so far ...(collectValues && { collectedValues: allCollectedValues }), // Include all collected values so far
...(includeMeta && { meta: res.meta }), ...(includeMeta && { meta: res.meta }),
...(remediation ? { remediation } : {}), ...(remediation ? { remediation } : {}),
@@ -929,6 +942,7 @@ export class LogicalOperators extends BaseRule {
if (!possibilities.length) { if (!possibilities.length) {
return { return {
possibility: 0, possibility: 0,
reliability: 1.0,
...(collectValues && { collectedValues: allCollectedValues }), ...(collectValues && { collectedValues: allCollectedValues }),
...(includeMeta && { meta: { operation: 'union', childCount: 0 } }), ...(includeMeta && { meta: { operation: 'union', childCount: 0 } }),
reason: reasons.includes('cycle') ? 'cycle' : undefined reason: reasons.includes('cycle') ? 'cycle' : undefined
@@ -1002,8 +1016,22 @@ export class LogicalOperators extends BaseRule {
const unionRemediation = result.value === 0 const unionRemediation = result.value === 0
? buildRemediation(remediationOptions.length > 0 ? { status: 'required', options: remediationOptions } : null, { status: 'required' }) ? buildRemediation(remediationOptions.length > 0 ? { status: 'required', options: remediationOptions } : null, { status: 'required' })
: null; : null;
// Reliability of the child driving the fused value: the OWA selected
// source when traced, else the max/min possibility child (max is the
// default union aggregator).
let unionReliability = 1.0;
if (includeOwaTrace && result.trace && typeof result.trace.selectedIndex === 'number') {
unionReliability = reliabilities[result.trace.selectedIndex] ?? 1.0;
} else if ((unionConfig.aggregator || 'max') === 'min') {
const minP = Math.min(...possibilities);
unionReliability = reliabilities[possibilities.indexOf(minP)] ?? 1.0;
} else {
const maxP = Math.max(...possibilities);
unionReliability = reliabilities[possibilities.indexOf(maxP)] ?? 1.0;
}
return { return {
possibility: result.value, possibility: result.value,
reliability: unionReliability,
...(collectValues && { collectedValues: allCollectedValues }), // Include all collected values from child rules ...(collectValues && { collectedValues: allCollectedValues }), // Include all collected values from child rules
...(includeMeta && { ...(includeMeta && {
meta: { meta: {
@@ -1039,7 +1067,7 @@ export class LogicalOperators extends BaseRule {
const intersectionConfig = normalizedRule.intersection; const intersectionConfig = normalizedRule.intersection;
const childRules = intersectionConfig.rules || []; const childRules = intersectionConfig.rules || [];
let possibilities = [], metas = [], reasons = []; let possibilities = [], reliabilities = [], metas = [], reasons = [];
const remediationOptions = []; const remediationOptions = [];
const { fastPath = false, minPossibility = null, valueContext = null, collectValues = false, includeMeta = true, trackEvaluation = false } = options; const { fastPath = false, minPossibility = null, valueContext = null, collectValues = false, includeMeta = true, trackEvaluation = false } = options;
const allCollectedValues = collectValues ? [] : null; // Track collected values from all child rules const allCollectedValues = collectValues ? [] : null; // Track collected values from all child rules
@@ -1059,6 +1087,7 @@ export class LogicalOperators extends BaseRule {
const res = this.ruleEvaluator.evaluateRule(userId, userKey, objectId, objectKey, child, visited, currentRelation, options); const res = this.ruleEvaluator.evaluateRule(userId, userKey, objectId, objectKey, child, visited, currentRelation, options);
if (res.reason === 'cycle') reasons.push('cycle'); if (res.reason === 'cycle') reasons.push('cycle');
possibilities.push(res.possibility); possibilities.push(res.possibility);
reliabilities.push(res.reliability !== undefined ? res.reliability : 1.0);
metas.push(includeMeta ? res.meta : null); metas.push(includeMeta ? res.meta : null);
mergeRemediationOptions(remediationOptions, extractRemediation(res)); mergeRemediationOptions(remediationOptions, extractRemediation(res));
@@ -1128,8 +1157,21 @@ export class LogicalOperators extends BaseRule {
const intersectionRemediation = result.value === 0 const intersectionRemediation = result.value === 0
? buildRemediation(remediationOptions.length > 0 ? { status: 'required', options: remediationOptions } : null, { status: 'required' }) ? buildRemediation(remediationOptions.length > 0 ? { status: 'required', options: remediationOptions } : null, { status: 'required' })
: null; : null;
// Reliability of the child driving the fused value (min is the
// default intersection aggregator).
let intersectionReliability = 1.0;
if (includeOwaTrace && result.trace && typeof result.trace.selectedIndex === 'number') {
intersectionReliability = reliabilities[result.trace.selectedIndex] ?? 1.0;
} else if ((intersectionConfig.aggregator || 'min') === 'max') {
const maxP = Math.max(...possibilities);
intersectionReliability = reliabilities[possibilities.indexOf(maxP)] ?? 1.0;
} else {
const minP = Math.min(...possibilities);
intersectionReliability = reliabilities[possibilities.indexOf(minP)] ?? 1.0;
}
return { return {
possibility: result.value, possibility: result.value,
reliability: intersectionReliability,
...(collectValues && { collectedValues: allCollectedValues }), // Include all collected values from child rules ...(collectValues && { collectedValues: allCollectedValues }), // Include all collected values from child rules
...(includeMeta && { ...(includeMeta && {
meta: { meta: {
@@ -1251,6 +1293,9 @@ export class LogicalOperators extends BaseRule {
return { return {
possibility, possibility,
// Both legs contribute to the decision, so their reliabilities
// multiply (consistent with TTU/chain path semantics).
reliability: (a.reliability !== undefined ? a.reliability : 1.0) * (b.reliability !== undefined ? b.reliability : 1.0),
// In binary mode the negated child's strength is the deny side of the // In binary mode the negated child's strength is the deny side of the
// dual-threshold contract: deny fires when P(B) >= maxDenyPossibility. // dual-threshold contract: deny fires when P(B) >= maxDenyPossibility.
...(options.binary && { possibility_deny: b.possibility ?? 0 }), ...(options.binary && { possibility_deny: b.possibility ?? 0 }),
+15 -7
View File
@@ -121,6 +121,7 @@ export class MultiHopRule extends BaseRule {
new Set(), new Set(),
[], [],
1.0, 1.0,
1.0,
reverse, reverse,
collectValuesEnabled, collectValuesEnabled,
trackPaths, trackPaths,
@@ -162,7 +163,7 @@ export class MultiHopRule extends BaseRule {
} }
// Aggregate paths to get final possibility // Aggregate paths to get final possibility
const { finalPossibility, bestPath } = this._aggregatePaths( const { finalPossibility, finalReliability = 1.0, bestPath } = this._aggregatePaths(
pathsWithValues, pathAggregation, owaWeights, evaluation, options.trackEvaluation pathsWithValues, pathAggregation, owaWeights, evaluation, options.trackEvaluation
); );
@@ -215,7 +216,7 @@ export class MultiHopRule extends BaseRule {
const authResult = { const authResult = {
possibility: finalPossibility, possibility: finalPossibility,
reliability: 1.0, reliability: finalReliability,
...(includeMeta && { ...(includeMeta && {
meta: allowMeta meta: allowMeta
}), }),
@@ -231,7 +232,7 @@ export class MultiHopRule extends BaseRule {
* @private * @private
*/ */
_findPathsAndCollectValues(startId, endId, relation, maxDepth, _findPathsAndCollectValues(startId, endId, relation, maxDepth,
visited, currentPath = [], currentPoss = 1.0, visited, currentPath = [], currentPoss = 1.0, currentReliability = 1.0,
reverse = false, collectValues = true, reverse = false, collectValues = true,
trackPaths = true, trackPaths = true,
stopSignal = null, stopSignal = null,
@@ -250,6 +251,7 @@ export class MultiHopRule extends BaseRule {
nodeIds: trackPaths ? [...currentPath.map(step => step.nodeId), endId] : [endId], nodeIds: trackPaths ? [...currentPath.map(step => step.nodeId), endId] : [endId],
hops: currentPath.length, hops: currentPath.length,
possibility: currentPoss, possibility: currentPoss,
reliability: currentReliability,
collectedValues: [], collectedValues: [],
pathSteps: trackPaths ? [...currentPath] : null pathSteps: trackPaths ? [...currentPath] : null
}; };
@@ -300,6 +302,7 @@ export class MultiHopRule extends BaseRule {
if (!nextKey) continue; if (!nextKey) continue;
const nextPoss = Math.min(currentPoss, edge.possibility ?? 1.0); const nextPoss = Math.min(currentPoss, edge.possibility ?? 1.0);
const nextReliability = currentReliability * (edge.reliability !== undefined ? edge.reliability : 1.0);
if (fastPath && nextPoss < minPossibility) continue; if (fastPath && nextPoss < minPossibility) continue;
const pathStep = (collectValues || trackPaths) ? { const pathStep = (collectValues || trackPaths) ? {
@@ -321,6 +324,7 @@ export class MultiHopRule extends BaseRule {
visited, visited,
nextPath, nextPath,
nextPoss, nextPoss,
nextReliability,
reverse, reverse,
collectValues, collectValues,
trackPaths, trackPaths,
@@ -346,6 +350,9 @@ export class MultiHopRule extends BaseRule {
*/ */
_collectValuesFromPath(pathSteps, defaultRelation, valueFilters, valueContext) { _collectValuesFromPath(pathSteps, defaultRelation, valueFilters, valueContext) {
const collectedValues = []; const collectedValues = [];
// Get blurred interval from ValueManager (arbiter-level; some stubs and
// older layouts keep it on the relation manager)
const valueManager = this.arbiter.valueManager || this.arbiter.relationManager?.valueManager;
for (let stepIndex = 0; stepIndex < pathSteps.length; stepIndex++) { for (let stepIndex = 0; stepIndex < pathSteps.length; stepIndex++) {
const step = pathSteps[stepIndex]; const step = pathSteps[stepIndex];
@@ -375,8 +382,7 @@ export class MultiHopRule extends BaseRule {
continue; continue;
} }
// Get blurred interval from ValueManager const blurred = valueManager.getBlurredValue(step.edge);
const blurred = this.arbiter.relationManager.valueManager.getBlurredValue(step.edge);
if (blurred.interval) { if (blurred.interval) {
const collectedValue = this._createCollectedValue( const collectedValue = this._createCollectedValue(
@@ -439,7 +445,7 @@ export class MultiHopRule extends BaseRule {
changed_last_at: contextValue.timestamp changed_last_at: contextValue.timestamp
}; };
const blurred = this.arbiter.relationManager.valueManager.getBlurredValue(tempRelation); const blurred = valueManager.getBlurredValue(tempRelation);
if (blurred.interval) { if (blurred.interval) {
const collectedValue = this._createCollectedValue( const collectedValue = this._createCollectedValue(
@@ -503,6 +509,7 @@ export class MultiHopRule extends BaseRule {
const path = pathsWithValues[0]; const path = pathsWithValues[0];
return { return {
finalPossibility: path.possibility, finalPossibility: path.possibility,
finalReliability: path.reliability !== undefined ? path.reliability : 1.0,
bestPath: path bestPath: path
}; };
} }
@@ -597,7 +604,8 @@ export class MultiHopRule extends BaseRule {
} }
return { return {
finalPossibility: possibilityResult.value, finalPossibility: possibilityResult.value,
finalReliability: bestPath ? (bestPath.reliability !== undefined ? bestPath.reliability : 1.0) : 1.0,
bestPath bestPath
}; };
} }
+1 -1
View File
@@ -165,7 +165,7 @@ describe('Advanced rule kinds through check() (rigor)', () => {
}), }),
rigor.invariant('no action errors', (ctx) => ctx.error === null) rigor.invariant('no action errors', (ctx) => ctx.error === null)
]) ])
).run({ effort: 500, seed: 'advanced-rule-kinds', maxTraceLength: 30 }); ).run({ effort: 500, seed: 'advanced-rule-kinds', maxTraceLength: 30 , artifacts: { dir: '', persist: 'never' }});
const inv = result.crucibleVerdict; const inv = result.crucibleVerdict;
assert.equal(inv.passed, true, [ assert.equal(inv.passed, true, [
@@ -77,7 +77,7 @@ describe('Authorization config consistency (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('path-parity', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('path-parity', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 300, seed: 'authz-config-parity' }); ).run({ effort: 300, seed: 'authz-config-parity' , artifacts: { dir: '', persist: 'never' }});
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'path-parity'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'path-parity');
assert.ok(inv); assert.ok(inv);
@@ -122,7 +122,7 @@ describe('Authorization config consistency (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('override-honored', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('override-honored', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 300, seed: 'authz-config-override' }); ).run({ effort: 300, seed: 'authz-config-override' , artifacts: { dir: '', persist: 'never' }});
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'override-honored'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'override-honored');
assert.ok(inv); assert.ok(inv);
@@ -170,7 +170,7 @@ describe('Authorization config consistency (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('remediation-contract', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('remediation-contract', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 300, seed: 'authz-config-remediation' }); ).run({ effort: 300, seed: 'authz-config-remediation' , artifacts: { dir: '', persist: 'never' }});
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'remediation-contract'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'remediation-contract');
assert.ok(inv); assert.ok(inv);
+6 -6
View File
@@ -68,7 +68,7 @@ describe('Authorization graph semantics (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('direct-exact', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('direct-exact', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 400, seed: 'authz-graph-direct' }); ).run({ effort: 400, seed: 'authz-graph-direct' , artifacts: { dir: '', persist: 'never' }});
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'direct-exact'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'direct-exact');
assert.ok(inv); assert.ok(inv);
@@ -103,7 +103,7 @@ describe('Authorization graph semantics (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('absent-denies', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('absent-denies', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 300, seed: 'authz-graph-absent' }); ).run({ effort: 300, seed: 'authz-graph-absent' , artifacts: { dir: '', persist: 'never' }});
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'absent-denies'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'absent-denies');
assert.ok(inv); assert.ok(inv);
@@ -146,7 +146,7 @@ describe('Authorization graph semantics (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('weakest-link', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('weakest-link', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 400, seed: 'authz-graph-chain' }); ).run({ effort: 400, seed: 'authz-graph-chain' , artifacts: { dir: '', persist: 'never' }});
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'weakest-link'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'weakest-link');
assert.ok(inv); assert.ok(inv);
@@ -196,7 +196,7 @@ describe('Authorization graph semantics (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('disjunctive-max', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('disjunctive-max', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 500, seed: 'authz-graph-multipath' }); ).run({ effort: 500, seed: 'authz-graph-multipath' , artifacts: { dir: '', persist: 'never' }});
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'disjunctive-max'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'disjunctive-max');
assert.ok(inv); assert.ok(inv);
@@ -248,7 +248,7 @@ describe('Authorization graph semantics (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('tus-weakest-link', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('tus-weakest-link', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 400, seed: 'authz-graph-tus' }); ).run({ effort: 400, seed: 'authz-graph-tus' , artifacts: { dir: '', persist: 'never' }});
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'tus-weakest-link'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'tus-weakest-link');
assert.ok(inv); assert.ok(inv);
@@ -287,7 +287,7 @@ describe('Authorization graph semantics (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('revoke-invalidates', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('revoke-invalidates', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 300, seed: 'authz-graph-mutation' }); ).run({ effort: 300, seed: 'authz-graph-mutation' , artifacts: { dir: '', persist: 'never' }});
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'revoke-invalidates'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'revoke-invalidates');
assert.ok(inv); assert.ok(inv);
+2 -2
View File
@@ -131,7 +131,7 @@ describe('Batch loading consistency (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('batch-parity', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('batch-parity', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 400, seed: 'batch-parity' }); ).run({ effort: 400, seed: 'batch-parity' , artifacts: { dir: '', persist: 'never' }});
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'batch-parity'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'batch-parity');
assert.ok(inv); assert.ok(inv);
@@ -179,7 +179,7 @@ describe('Batch loading consistency (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('batch-mutation-parity', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('batch-mutation-parity', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 400, seed: 'batch-mutation-parity' }); ).run({ effort: 400, seed: 'batch-mutation-parity' , artifacts: { dir: '', persist: 'never' }});
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'batch-mutation-parity'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'batch-mutation-parity');
assert.ok(inv); assert.ok(inv);
+1 -1
View File
@@ -142,7 +142,7 @@ describe('Batch update ordering semantics (rigor)', () => {
}), }),
rigor.invariant('no action errors', (ctx) => ctx.error === null) rigor.invariant('no action errors', (ctx) => ctx.error === null)
]) ])
).run({ effort: 400, seed: 'batch-order-parity', maxTraceLength: 25 }); ).run({ effort: 400, seed: 'batch-order-parity', maxTraceLength: 25 , artifacts: { dir: '', persist: 'never' }});
const inv = result.crucibleVerdict; const inv = result.crucibleVerdict;
assert.equal(inv.passed, true, [ assert.equal(inv.passed, true, [
+2 -2
View File
@@ -292,7 +292,7 @@ describe('Binary (threshold) mode parity (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('binary-normal-agreement', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('binary-normal-agreement', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 1200, seed: 'binary-mode-config-matrix' }); ).run({ effort: 1200, seed: 'binary-mode-config-matrix' , artifacts: { dir: '', persist: 'never' }});
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'binary-normal-agreement'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'binary-normal-agreement');
assert.ok(inv, 'invariant missing'); assert.ok(inv, 'invariant missing');
@@ -350,7 +350,7 @@ describe('Binary (threshold) mode parity (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('mutation-freshness', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('mutation-freshness', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800, seed: 'binary-mode-mutation-parity' }); ).run({ effort: 800, seed: 'binary-mode-mutation-parity' , artifacts: { dir: '', persist: 'never' }});
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'mutation-freshness'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'mutation-freshness');
assert.ok(inv, 'invariant missing'); assert.ok(inv, 'invariant missing');
+1 -1
View File
@@ -151,7 +151,7 @@ describe('Binary mode with partial graphs (rigor)', () => {
}), }),
rigor.invariant('no action errors', (ctx) => ctx.error === null) rigor.invariant('no action errors', (ctx) => ctx.error === null)
]) ])
).run({ effort: 400, seed: 'binary-partial-parity', maxTraceLength: 25 }); ).run({ effort: 400, seed: 'binary-partial-parity', maxTraceLength: 25 , artifacts: { dir: '', persist: 'never' }});
const inv = result.crucibleVerdict; const inv = result.crucibleVerdict;
assert.equal(inv.passed, true, [ assert.equal(inv.passed, true, [
+3 -3
View File
@@ -115,7 +115,7 @@ describe('Cache correctness under mutation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('cache-parity', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('cache-parity', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 600, seed: 'cache-onoff-parity' }); ).run({ effort: 600, seed: 'cache-onoff-parity' , artifacts: { dir: '', persist: 'never' }});
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'cache-parity'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'cache-parity');
assert.ok(inv); assert.ok(inv);
@@ -161,7 +161,7 @@ describe('Cache correctness under mutation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('override-cache-fresh', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('override-cache-fresh', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 400, seed: 'cache-override-freshness' }); ).run({ effort: 400, seed: 'cache-override-freshness' , artifacts: { dir: '', persist: 'never' }});
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'override-cache-fresh'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'override-cache-fresh');
assert.ok(inv); assert.ok(inv);
@@ -207,7 +207,7 @@ describe('Cache correctness under mutation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('ttl-contract', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('ttl-contract', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 300, seed: 'cache-ttl-contract' }); ).run({ effort: 300, seed: 'cache-ttl-contract' , artifacts: { dir: '', persist: 'never' }});
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'ttl-contract'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'ttl-contract');
assert.ok(inv); assert.ok(inv);
+5 -5
View File
@@ -47,7 +47,7 @@ describe('ChainRule evaluation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('empty-steps', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('empty-steps', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 200 }); ).run({ effort: 200 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'empty-steps'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'empty-steps');
@@ -84,7 +84,7 @@ describe('ChainRule evaluation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('possibility-bounded', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('possibility-bounded', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800 }); ).run({ effort: 800 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'possibility-bounded'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'possibility-bounded');
@@ -119,7 +119,7 @@ describe('ChainRule evaluation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('no-path', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('no-path', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 500 }); ).run({ effort: 500 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'no-path'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'no-path');
@@ -156,7 +156,7 @@ describe('ChainRule evaluation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('one-step-pos', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('one-step-pos', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800 }); ).run({ effort: 800 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'one-step-pos'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'one-step-pos');
@@ -206,7 +206,7 @@ describe('ChainRule evaluation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('two-step-chain', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('two-step-chain', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800 }); ).run({ effort: 800 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'two-step-chain'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'two-step-chain');
+4 -4
View File
@@ -111,7 +111,7 @@ describe('PartialGraphContext.getChallengeProof (rigor)', () => {
({ error, errorMessage }) => !error && !errorMessage ({ error, errorMessage }) => !error && !errorMessage
) )
]) ])
).run({ effort: 1500 }); ).run({ effort: 1500 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') { if (process.env.TEST_DEBUG === '1') {
console.log('TAP:', report.toTAP()); console.log('TAP:', report.toTAP());
@@ -152,7 +152,7 @@ describe('PartialGraphContext.getChallengeProof (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('no-expired', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('no-expired', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800 }); ).run({ effort: 800 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') { if (process.env.TEST_DEBUG === '1') {
console.log('TAP:', report.toTAP()); console.log('TAP:', report.toTAP());
@@ -210,7 +210,7 @@ describe('PartialGraphContext.getChallengeProof (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('most-recent', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('most-recent', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800 }); ).run({ effort: 800 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') { if (process.env.TEST_DEBUG === '1') {
console.log('TAP:', report.toTAP()); console.log('TAP:', report.toTAP());
@@ -265,7 +265,7 @@ describe('PartialGraphContext.getChallengeProof (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('within-window', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('within-window', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 1500 }); ).run({ effort: 1500 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') { if (process.env.TEST_DEBUG === '1') {
console.log('TAP:', report.toTAP()); console.log('TAP:', report.toTAP());
+6 -6
View File
@@ -69,7 +69,7 @@ describe('ChallengeRule._resolveSubjectKey (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('subjectKey-wins', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('subjectKey-wins', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 1000 }); ).run({ effort: 1000 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'subjectKey-wins'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'subjectKey-wins');
@@ -118,7 +118,7 @@ describe('ChallengeRule._resolveSubjectKey (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('subject-mapping', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('subject-mapping', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 1500 }); ).run({ effort: 1500 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'subject-mapping'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'subject-mapping');
@@ -199,7 +199,7 @@ describe('ChallengeRule._resolveWithinMs (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('within-units', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('within-units', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800 }); ).run({ effort: 800 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'within-units'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'within-units');
@@ -262,7 +262,7 @@ describe('ChallengeRule._resolveWithinMs (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('within-priority', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('within-priority', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800 }); ).run({ effort: 800 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'within-priority'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'within-priority');
@@ -295,7 +295,7 @@ describe('ChallengeRule._resolveWithinMs (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('null-when-absent', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('null-when-absent', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 500 }); ).run({ effort: 500 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'null-when-absent'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'null-when-absent');
@@ -335,7 +335,7 @@ describe('ChallengeRule._buildRequirement (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('buildRequirement', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('buildRequirement', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800 }); ).run({ effort: 800 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'buildRequirement'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'buildRequirement');
+3 -3
View File
@@ -80,7 +80,7 @@ describe('check/explain agreement (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('check-explain-agree', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('check-explain-agree', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 500, seed: 'explain-agreement' }); ).run({ effort: 500, seed: 'explain-agreement' , artifacts: { dir: '', persist: 'never' }});
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'check-explain-agree'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'check-explain-agree');
assert.ok(inv); assert.ok(inv);
@@ -150,7 +150,7 @@ describe('check/explain agreement (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('used-facts-consistent', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('used-facts-consistent', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 600, seed: 'explain-used-facts' }); ).run({ effort: 600, seed: 'explain-used-facts' , artifacts: { dir: '', persist: 'never' }});
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'used-facts-consistent'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'used-facts-consistent');
assert.ok(inv); assert.ok(inv);
@@ -198,7 +198,7 @@ describe('check/explain agreement (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('remediation-consistent', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('remediation-consistent', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 400, seed: 'explain-remediation' }); ).run({ effort: 400, seed: 'explain-remediation' , artifacts: { dir: '', persist: 'never' }});
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'remediation-consistent'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'remediation-consistent');
assert.ok(inv); assert.ok(inv);
+2 -2
View File
@@ -119,7 +119,7 @@ describe('Relational comparator full-path parity (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('comparator-full-path', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('comparator-full-path', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 1200, seed: 'comparator-full-path-parity' }); ).run({ effort: 1200, seed: 'comparator-full-path-parity' , artifacts: { dir: '', persist: 'never' }});
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'comparator-full-path'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'comparator-full-path');
assert.ok(inv, 'invariant missing'); assert.ok(inv, 'invariant missing');
@@ -168,7 +168,7 @@ describe('Relational comparator full-path parity (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('comparator-aggregation', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('comparator-aggregation', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 500, seed: 'comparator-aggregation' }); ).run({ effort: 500, seed: 'comparator-aggregation' , artifacts: { dir: '', persist: 'never' }});
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'comparator-aggregation'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'comparator-aggregation');
assert.ok(inv, 'invariant missing'); assert.ok(inv, 'invariant missing');
+1 -1
View File
@@ -173,7 +173,7 @@ describe('Compiled vs rule-path parity (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('compiled-rule-parity', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('compiled-rule-parity', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 2000, seed: 'compiled-rule-config-matrix' }); ).run({ effort: 2000, seed: 'compiled-rule-config-matrix' , artifacts: { dir: '', persist: 'never' }});
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'compiled-rule-parity'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'compiled-rule-parity');
assert.ok(inv, 'invariant missing'); assert.ok(inv, 'invariant missing');
+7 -7
View File
@@ -72,7 +72,7 @@ describe('ComputedRule evaluation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('possibility-passthrough', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('possibility-passthrough', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800 }); ).run({ effort: 800 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'possibility-passthrough'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'possibility-passthrough');
@@ -108,7 +108,7 @@ describe('ComputedRule evaluation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('reason-default', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('reason-default', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 500 }); ).run({ effort: 500 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'reason-default'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'reason-default');
@@ -145,7 +145,7 @@ describe('ComputedRule evaluation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('reason-passthrough', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('reason-passthrough', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800 }); ).run({ effort: 800 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'reason-passthrough'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'reason-passthrough');
@@ -188,7 +188,7 @@ describe('ComputedRule evaluation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('meta-contract', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('meta-contract', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800 }); ).run({ effort: 800 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'meta-contract'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'meta-contract');
@@ -237,7 +237,7 @@ describe('ComputedRule evaluation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('collected-values-passthrough', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('collected-values-passthrough', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800 }); ).run({ effort: 800 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'collected-values-passthrough'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'collected-values-passthrough');
@@ -273,7 +273,7 @@ describe('ComputedRule evaluation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('possibility-fallback-zero', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('possibility-fallback-zero', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 500 }); ).run({ effort: 500 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'possibility-fallback-zero'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'possibility-fallback-zero');
@@ -311,7 +311,7 @@ describe('ComputedRule evaluation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('options-passthrough', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('options-passthrough', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800 }); ).run({ effort: 800 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'options-passthrough'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'options-passthrough');
+2 -2
View File
@@ -147,7 +147,7 @@ describe('Config redefinition semantics (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('redefine-parity', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('redefine-parity', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 1200, seed: 'config-redefinition-parity' }); ).run({ effort: 1200, seed: 'config-redefinition-parity' , artifacts: { dir: '', persist: 'never' }});
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'redefine-parity'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'redefine-parity');
assert.ok(inv, 'invariant missing'); assert.ok(inv, 'invariant missing');
@@ -194,7 +194,7 @@ describe('Config redefinition semantics (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('redefine-binary-fastpath', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('redefine-binary-fastpath', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800, seed: 'config-redefinition-binary' }); ).run({ effort: 800, seed: 'config-redefinition-binary' , artifacts: { dir: '', persist: 'never' }});
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'redefine-binary-fastpath'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'redefine-binary-fastpath');
assert.ok(inv, 'invariant missing'); assert.ok(inv, 'invariant missing');
+8 -8
View File
@@ -84,7 +84,7 @@ describe('DirectRule evaluation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('no-relation-fallback', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('no-relation-fallback', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800 }); ).run({ effort: 800 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'no-relation-fallback'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'no-relation-fallback');
@@ -138,7 +138,7 @@ describe('DirectRule evaluation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('relation-strength-preserved', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('relation-strength-preserved', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800 }); ).run({ effort: 800 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'relation-strength-preserved'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'relation-strength-preserved');
@@ -186,7 +186,7 @@ describe('DirectRule evaluation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('reverse-routing', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('reverse-routing', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800 }); ).run({ effort: 800 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'reverse-routing'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'reverse-routing');
@@ -236,7 +236,7 @@ describe('DirectRule evaluation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('fastPath-early-exit', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('fastPath-early-exit', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800 }); ).run({ effort: 800 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'fastPath-early-exit'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'fastPath-early-exit');
@@ -278,7 +278,7 @@ describe('DirectRule evaluation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('collectValues-disabled', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('collectValues-disabled', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800 }); ).run({ effort: 800 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'collectValues-disabled'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'collectValues-disabled');
@@ -332,7 +332,7 @@ describe('DirectRule evaluation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('collectValues-default', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('collectValues-default', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800 }); ).run({ effort: 800 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'collectValues-default'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'collectValues-default');
@@ -393,7 +393,7 @@ describe('DirectRule evaluation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('relation-precedence', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('relation-precedence', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 1500 }); ).run({ effort: 1500 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'relation-precedence'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'relation-precedence');
@@ -449,7 +449,7 @@ describe('DirectRule evaluation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('result-shape-stable', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('result-shape-stable', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800 }); ).run({ effort: 800 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'result-shape-stable'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'result-shape-stable');
+8 -8
View File
@@ -93,7 +93,7 @@ describe('DSLCompiler → engine rule mapping (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('direct-emission', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('direct-emission', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800 }); ).run({ effort: 800 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'direct-emission'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'direct-emission');
@@ -128,7 +128,7 @@ describe('DSLCompiler → engine rule mapping (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('tus-emission', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('tus-emission', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 200 }); ).run({ effort: 200 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'tus-emission'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'tus-emission');
@@ -160,7 +160,7 @@ describe('DSLCompiler → engine rule mapping (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('parent-emission', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('parent-emission', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 200 }); ).run({ effort: 200 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'parent-emission'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'parent-emission');
@@ -197,7 +197,7 @@ describe('DSLCompiler → engine rule mapping (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('chain-emission', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('chain-emission', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 200 }); ).run({ effort: 200 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'chain-emission'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'chain-emission');
@@ -246,7 +246,7 @@ describe('DSLCompiler → engine rule mapping (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('multi_hop-emission', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('multi_hop-emission', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 200 }); ).run({ effort: 200 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'multi_hop-emission'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'multi_hop-emission');
@@ -288,7 +288,7 @@ describe('DSLCompiler → engine rule mapping (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('logical-emission', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('logical-emission', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800 }); ).run({ effort: 800 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'logical-emission'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'logical-emission');
@@ -323,7 +323,7 @@ describe('DSLCompiler → engine rule mapping (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('relational-comparator-emission', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('relational-comparator-emission', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800 }); ).run({ effort: 800 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'relational-comparator-emission'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'relational-comparator-emission');
@@ -371,7 +371,7 @@ describe('DSLCompiler → engine rule mapping (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('mapping-consistency', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('mapping-consistency', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800 }); ).run({ effort: 800 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'mapping-consistency'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'mapping-consistency');
+2 -2
View File
@@ -131,7 +131,7 @@ describe('DSL-compiled vs hand-written parity under mutation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('sequence-parity', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('sequence-parity', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 400, seed: 'dsl-mutation-parity' }); ).run({ effort: 400, seed: 'dsl-mutation-parity' , artifacts: { dir: '', persist: 'never' }});
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'sequence-parity'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'sequence-parity');
assert.ok(inv); assert.ok(inv);
@@ -181,7 +181,7 @@ describe('DSL-compiled vs hand-written parity under mutation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('grant-revoke-cycles', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('grant-revoke-cycles', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 300, seed: 'dsl-grant-revoke-cycles' }); ).run({ effort: 300, seed: 'dsl-grant-revoke-cycles' , artifacts: { dir: '', persist: 'never' }});
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'grant-revoke-cycles'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'grant-revoke-cycles');
assert.ok(inv); assert.ok(inv);
+8 -8
View File
@@ -182,7 +182,7 @@ describe('GraphIndices indexes (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('getDirectRelation-matches-oracle', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('getDirectRelation-matches-oracle', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 1500, seed: 'graph-indices-direct-a' }); ).run({ effort: 1500, seed: 'graph-indices-direct-a' , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'getDirectRelation-matches-oracle'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'getDirectRelation-matches-oracle');
@@ -245,7 +245,7 @@ describe('GraphIndices indexes (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('getRelationsFromSrc-matches-oracle', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('getRelationsFromSrc-matches-oracle', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 1500, seed: 'graph-indices-direct-b' }); ).run({ effort: 1500, seed: 'graph-indices-direct-b' , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'getRelationsFromSrc-matches-oracle'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'getRelationsFromSrc-matches-oracle');
@@ -305,7 +305,7 @@ describe('GraphIndices indexes (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('getRelationsToDst-matches-oracle', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('getRelationsToDst-matches-oracle', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 1500, seed: 'graph-indices-direct-c' }); ).run({ effort: 1500, seed: 'graph-indices-direct-c' , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'getRelationsToDst-matches-oracle'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'getRelationsToDst-matches-oracle');
@@ -365,7 +365,7 @@ describe('GraphIndices indexes (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('getRelationsByName-matches-oracle', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('getRelationsByName-matches-oracle', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 1500, seed: 'graph-indices-direct-d' }); ).run({ effort: 1500, seed: 'graph-indices-direct-d' , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'getRelationsByName-matches-oracle'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'getRelationsByName-matches-oracle');
@@ -421,7 +421,7 @@ describe('GraphIndices indexes (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('addRelation-tuple-idempotent', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('addRelation-tuple-idempotent', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800, seed: 'graph-indices-src' }); ).run({ effort: 800, seed: 'graph-indices-src' , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'addRelation-tuple-idempotent'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'addRelation-tuple-idempotent');
@@ -459,7 +459,7 @@ describe('GraphIndices indexes (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('addRelation-idempotent', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('addRelation-idempotent', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800, seed: 'graph-indices-dst' }); ).run({ effort: 800, seed: 'graph-indices-dst' , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'addRelation-idempotent'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'addRelation-idempotent');
@@ -502,7 +502,7 @@ describe('GraphIndices indexes (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('clear-empties-indexes', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('clear-empties-indexes', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800, seed: 'graph-indices-name' }); ).run({ effort: 800, seed: 'graph-indices-name' , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'clear-empties-indexes'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'clear-empties-indexes');
@@ -549,7 +549,7 @@ describe('GraphIndices indexes (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('add-remove-cycle', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('add-remove-cycle', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800, seed: 'graph-indices-cycle' }); ).run({ effort: 800, seed: 'graph-indices-cycle' , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'add-remove-cycle'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'add-remove-cycle');
+1 -1
View File
@@ -167,7 +167,7 @@ describe('Possibility write-boundary validation (rigor)', () => {
return ctx.actual.engine >= 0 && ctx.actual.engine <= 1; return ctx.actual.engine >= 0 && ctx.actual.engine <= 1;
}) })
]) ])
).run({ effort: 400, seed: 'input-range-contract' }); ).run({ effort: 400, seed: 'input-range-contract' , artifacts: { dir: '', persist: 'never' }});
const inv = result.crucibleVerdict; const inv = result.crucibleVerdict;
assert.equal(inv.passed, true, [ assert.equal(inv.passed, true, [
+6 -6
View File
@@ -65,7 +65,7 @@ describe('LogicalOperators evaluation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('union-max', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('union-max', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 1500 }); ).run({ effort: 1500 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'union-max'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'union-max');
@@ -104,7 +104,7 @@ describe('LogicalOperators evaluation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('intersection-min', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('intersection-min', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 1500 }); ).run({ effort: 1500 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'intersection-min'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'intersection-min');
@@ -138,7 +138,7 @@ describe('LogicalOperators evaluation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('union-mean', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('union-mean', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 1500 }); ).run({ effort: 1500 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'union-mean'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'union-mean');
@@ -181,7 +181,7 @@ describe('LogicalOperators evaluation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('exclusion', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('exclusion', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 1500 }); ).run({ effort: 1500 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'exclusion'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'exclusion');
@@ -214,7 +214,7 @@ describe('LogicalOperators evaluation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('possibility-bounded', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('possibility-bounded', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 1500 }); ).run({ effort: 1500 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'possibility-bounded'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'possibility-bounded');
@@ -251,7 +251,7 @@ describe('LogicalOperators evaluation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('collected-values-concat', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('collected-values-concat', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800 }); ).run({ effort: 800 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'collected-values-concat'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'collected-values-concat');
+1 -1
View File
@@ -165,7 +165,7 @@ describe('Manager vs index lookup parity (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('lookup-parity', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('lookup-parity', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 1200, seed: 'manager-index-parity' }); ).run({ effort: 1200, seed: 'manager-index-parity' , artifacts: { dir: '', persist: 'never' }});
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'lookup-parity'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'lookup-parity');
assert.ok(inv, 'invariant missing'); assert.ok(inv, 'invariant missing');
+5 -5
View File
@@ -51,7 +51,7 @@ describe('MultiHopRule evaluation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('missing-relation', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('missing-relation', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800 }); ).run({ effort: 800 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'missing-relation'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'missing-relation');
@@ -88,7 +88,7 @@ describe('MultiHopRule evaluation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('possibility-bounded', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('possibility-bounded', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800 }); ).run({ effort: 800 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'possibility-bounded'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'possibility-bounded');
@@ -126,7 +126,7 @@ describe('MultiHopRule evaluation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('single-path-strength', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('single-path-strength', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800 }); ).run({ effort: 800 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'single-path-strength'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'single-path-strength');
@@ -161,7 +161,7 @@ describe('MultiHopRule evaluation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('no-path', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('no-path', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 500 }); ).run({ effort: 500 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'no-path'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'no-path');
@@ -208,7 +208,7 @@ describe('MultiHopRule evaluation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('multi-hop-finds-path', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('multi-hop-finds-path', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800 }); ).run({ effort: 800 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'multi-hop-finds-path'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'multi-hop-finds-path');
@@ -154,7 +154,7 @@ describe('Multi-object independence (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('multi-object-isolation', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('multi-object-isolation', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 1000, seed: 'multi-object-independence' }); ).run({ effort: 1000, seed: 'multi-object-independence' , artifacts: { dir: '', persist: 'never' }});
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'multi-object-isolation'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'multi-object-isolation');
assert.ok(inv, 'invariant missing'); assert.ok(inv, 'invariant missing');
+2 -2
View File
@@ -201,7 +201,7 @@ describe('Node lifecycle semantics (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('remove-cascade', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('remove-cascade', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 1000, seed: 'node-lifecycle-remove' }); ).run({ effort: 1000, seed: 'node-lifecycle-remove' , artifacts: { dir: '', persist: 'never' }});
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'remove-cascade'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'remove-cascade');
assert.ok(inv, 'invariant missing'); assert.ok(inv, 'invariant missing');
@@ -254,7 +254,7 @@ describe('Node lifecycle semantics (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('node-readd', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('node-readd', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 600, seed: 'node-lifecycle-readd' }); ).run({ effort: 600, seed: 'node-lifecycle-readd' , artifacts: { dir: '', persist: 'never' }});
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'node-readd'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'node-readd');
assert.ok(inv, 'invariant missing'); assert.ok(inv, 'invariant missing');
+7 -7
View File
@@ -93,7 +93,7 @@ describe('NodeManager index invariants (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('inverse-maps', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('inverse-maps', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800 }); ).run({ effort: 800 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'inverse-maps'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'inverse-maps');
@@ -133,7 +133,7 @@ describe('NodeManager index invariants (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('addNode-idempotent', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('addNode-idempotent', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800 }); ).run({ effort: 800 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'addNode-idempotent'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'addNode-idempotent');
@@ -186,7 +186,7 @@ describe('NodeManager index invariants (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('size-invariant', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('size-invariant', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 1500 }); ).run({ effort: 1500 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'size-invariant'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'size-invariant');
@@ -249,7 +249,7 @@ describe('NodeManager index invariants (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('monotonic-ids', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('monotonic-ids', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800 }); ).run({ effort: 800 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'monotonic-ids'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'monotonic-ids');
@@ -295,7 +295,7 @@ describe('NodeManager index invariants (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('removeNode-cleanup', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('removeNode-cleanup', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800 }); ).run({ effort: 800 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'removeNode-cleanup'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'removeNode-cleanup');
@@ -335,7 +335,7 @@ describe('NodeManager index invariants (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('clearNodes-resets', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('clearNodes-resets', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 500 }); ).run({ effort: 500 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'clearNodes-resets'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'clearNodes-resets');
@@ -388,7 +388,7 @@ describe('NodeManager index invariants (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('updateNodeData-merges', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('updateNodeData-merges', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800 }); ).run({ effort: 800 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'updateNodeData-merges'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'updateNodeData-merges');
+2 -2
View File
@@ -88,7 +88,7 @@ describe('Partial graph overlay precedence (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('persistent-precedence', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('persistent-precedence', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 500, seed: 'overlay-persistent-precedence' }); ).run({ effort: 500, seed: 'overlay-persistent-precedence' , artifacts: { dir: '', persist: 'never' }});
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'persistent-precedence'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'persistent-precedence');
assert.ok(inv); assert.ok(inv);
@@ -138,7 +138,7 @@ describe('Partial graph overlay precedence (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('layer-precedence', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('layer-precedence', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 400, seed: 'overlay-layer-precedence' }); ).run({ effort: 400, seed: 'overlay-layer-precedence' , artifacts: { dir: '', persist: 'never' }});
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'layer-precedence'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'layer-precedence');
assert.ok(inv); assert.ok(inv);
+7 -7
View File
@@ -87,7 +87,7 @@ describe('ParentRule evaluation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('no-parents', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('no-parents', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 500 }); ).run({ effort: 500 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'no-parents'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'no-parents');
@@ -131,7 +131,7 @@ describe('ParentRule evaluation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('one-parent-strength', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('one-parent-strength', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800 }); ).run({ effort: 800 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'one-parent-strength'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'one-parent-strength');
@@ -175,7 +175,7 @@ describe('ParentRule evaluation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('threshold-cutoff', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('threshold-cutoff', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 1500 }); ).run({ effort: 1500 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'threshold-cutoff'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'threshold-cutoff');
@@ -212,7 +212,7 @@ describe('ParentRule evaluation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('cycle-detection', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('cycle-detection', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 200 }); ).run({ effort: 200 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'cycle-detection'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'cycle-detection');
@@ -258,7 +258,7 @@ describe('ParentRule evaluation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('multi-parent-max', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('multi-parent-max', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800 }); ).run({ effort: 800 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'multi-parent-max'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'multi-parent-max');
@@ -297,7 +297,7 @@ describe('ParentRule evaluation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('parent-relation-default', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('parent-relation-default', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 200 }); ).run({ effort: 200 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'parent-relation-default'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'parent-relation-default');
@@ -341,7 +341,7 @@ describe('ParentRule evaluation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('possibility-bounded', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('possibility-bounded', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800 }); ).run({ effort: 800 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'possibility-bounded'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'possibility-bounded');
+1 -1
View File
@@ -337,7 +337,7 @@ describe('Partial-graph overlay semantics (rigor)', () => {
}), }),
rigor.invariant('no action errors', (ctx) => ctx.error === null) rigor.invariant('no action errors', (ctx) => ctx.error === null)
]) ])
).run({ effort: 500, seed: 'partial-graph-parity', maxTraceLength: 30 }); ).run({ effort: 500, seed: 'partial-graph-parity', maxTraceLength: 30 , artifacts: { dir: '', persist: 'never' }});
const inv = result.crucibleVerdict; const inv = result.crucibleVerdict;
assert.equal(inv.passed, true, [ assert.equal(inv.passed, true, [
+2 -2
View File
@@ -128,7 +128,7 @@ describe('PLTC reachability parity (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('pltc-active-bypass-parity', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('pltc-active-bypass-parity', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 1500, seed: 'pltc-parity-active-bypass' }); ).run({ effort: 1500, seed: 'pltc-parity-active-bypass' , artifacts: { dir: '', persist: 'never' }});
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'pltc-active-bypass-parity'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'pltc-active-bypass-parity');
assert.ok(inv, 'invariant missing'); assert.ok(inv, 'invariant missing');
@@ -164,7 +164,7 @@ describe('PLTC reachability parity (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('pltc-fastfail-soundness', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('pltc-fastfail-soundness', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 1000, seed: 'pltc-parity-fastfail' }); ).run({ effort: 1000, seed: 'pltc-parity-fastfail' , artifacts: { dir: '', persist: 'never' }});
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'pltc-fastfail-soundness'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'pltc-fastfail-soundness');
assert.ok(inv, 'invariant missing'); assert.ok(inv, 'invariant missing');
@@ -218,7 +218,7 @@ const result = await rigor.campaign(
return true; return true;
}) })
]) ])
).run({ effort: 400, seed: 'snapshot-lifecycle-protocol', maxTraceLength: 24 }); ).run({ effort: 400, seed: 'snapshot-lifecycle-protocol', maxTraceLength: 24 , artifacts: { dir: '', persist: 'never' }});
describe('Snapshot lifecycle protocol (rigor gated invariants)', () => { describe('Snapshot lifecycle protocol (rigor gated invariants)', () => {
it('every random lifecycle sequence honors the protocol', () => { it('every random lifecycle sequence honors the protocol', () => {
+13 -13
View File
@@ -94,7 +94,7 @@ describe('QualitativeRelationalComparatorRule._getQualitativeScale (rigor)', ()
rigor.crucible([ rigor.crucible([
rigor.invariant('known-scales', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('known-scales', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 500 }); ).run({ effort: 500 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'known-scales'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'known-scales');
@@ -125,7 +125,7 @@ describe('QualitativeRelationalComparatorRule._getQualitativeScale (rigor)', ()
rigor.crucible([ rigor.crucible([
rigor.invariant('fallback', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('fallback', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800 }); ).run({ effort: 800 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'fallback'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'fallback');
@@ -162,7 +162,7 @@ describe('QualitativeRelationalComparatorRule._calculateDecayedPossibility (rigo
rigor.crucible([ rigor.crucible([
rigor.invariant('stable-identity', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('stable-identity', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 1500 }); ).run({ effort: 1500 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'stable-identity'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'stable-identity');
@@ -197,7 +197,7 @@ describe('QualitativeRelationalComparatorRule._calculateDecayedPossibility (rigo
rigor.crucible([ rigor.crucible([
rigor.invariant('zero-periods', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('zero-periods', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 1500 }); ).run({ effort: 1500 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'zero-periods'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'zero-periods');
@@ -238,7 +238,7 @@ describe('QualitativeRelationalComparatorRule._calculateDecayedPossibility (rigo
rigor.crucible([ rigor.crucible([
rigor.invariant('down-monotone', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('down-monotone', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 1500 }); ).run({ effort: 1500 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'down-monotone'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'down-monotone');
@@ -278,7 +278,7 @@ describe('QualitativeRelationalComparatorRule._calculateDecayedPossibility (rigo
rigor.crucible([ rigor.crucible([
rigor.invariant('up-monotone', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('up-monotone', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 1500 }); ).run({ effort: 1500 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'up-monotone'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'up-monotone');
@@ -314,7 +314,7 @@ describe('QualitativeRelationalComparatorRule._calculateDecayedPossibility (rigo
rigor.crucible([ rigor.crucible([
rigor.invariant('result-in-scale', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('result-in-scale', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 1500 }); ).run({ effort: 1500 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'result-in-scale'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'result-in-scale');
@@ -351,7 +351,7 @@ describe('QualitativeRelationalComparatorRule._createQualitativeInterval (rigor)
rigor.crucible([ rigor.crucible([
rigor.invariant('lower-le-upper', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('lower-le-upper', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 1500 }); ).run({ effort: 1500 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'lower-le-upper'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'lower-le-upper');
@@ -389,7 +389,7 @@ describe('QualitativeRelationalComparatorRule._createQualitativeInterval (rigor)
rigor.crucible([ rigor.crucible([
rigor.invariant('point-contained', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('point-contained', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 1500 }); ).run({ effort: 1500 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'point-contained'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'point-contained');
@@ -425,7 +425,7 @@ describe('QualitativeRelationalComparatorRule._createQualitativeInterval (rigor)
rigor.crucible([ rigor.crucible([
rigor.invariant('bounds-in-scale', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('bounds-in-scale', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 1500 }); ).run({ effort: 1500 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'bounds-in-scale'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'bounds-in-scale');
@@ -459,7 +459,7 @@ describe('QualitativeRelationalComparatorRule._createQualitativeInterval (rigor)
rigor.crucible([ rigor.crucible([
rigor.invariant('zero-blur', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('zero-blur', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 1500 }); ).run({ effort: 1500 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'zero-blur'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'zero-blur');
@@ -490,7 +490,7 @@ describe('QualitativeRelationalComparatorRule._calculatePossibilityLossSteps (ri
rigor.crucible([ rigor.crucible([
rigor.invariant('loss-is-zero', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('loss-is-zero', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800 }); ).run({ effort: 800 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'loss-is-zero'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'loss-is-zero');
@@ -529,7 +529,7 @@ describe('QualitativeRelationalComparatorRule._calculatePossibilityLossSteps (ri
rigor.crucible([ rigor.crucible([
rigor.invariant('loss-symmetric', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('loss-symmetric', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800 }); ).run({ effort: 800 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'loss-symmetric'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'loss-symmetric');
+6 -6
View File
@@ -74,7 +74,7 @@ describe('RelationManager.addRelation/removeRelation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('add-and-get', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('add-and-get', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800 }); ).run({ effort: 800 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'add-and-get'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'add-and-get');
@@ -127,7 +127,7 @@ describe('RelationManager.addRelation/removeRelation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('addRelation-idempotent', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('addRelation-idempotent', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800 }); ).run({ effort: 800 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'addRelation-idempotent'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'addRelation-idempotent');
@@ -195,7 +195,7 @@ describe('RelationManager.addRelation/removeRelation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('remove-clears-indexes', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('remove-clears-indexes', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800 }); ).run({ effort: 800 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'remove-clears-indexes'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'remove-clears-indexes');
@@ -284,7 +284,7 @@ describe('RelationManager.addRelation/removeRelation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('index-coherence', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('index-coherence', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 1500 }); ).run({ effort: 1500 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'index-coherence'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'index-coherence');
@@ -364,7 +364,7 @@ describe('RelationManager.addRelation/removeRelation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('add-remove-roundtrip', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('add-remove-roundtrip', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 1500 }); ).run({ effort: 1500 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'add-remove-roundtrip'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'add-remove-roundtrip');
@@ -396,7 +396,7 @@ describe('RelationManager.addRelation/removeRelation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('getDirectRelation-unknown', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('getDirectRelation-unknown', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 500 }); ).run({ effort: 500 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'getDirectRelation-unknown'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'getDirectRelation-unknown');
@@ -76,7 +76,7 @@ describe('RelationalComparatorRouter._isQualitativeRule (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('qualitative-wins', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('qualitative-wins', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800 }); ).run({ effort: 800 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'qualitative-wins'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'qualitative-wins');
@@ -116,7 +116,7 @@ describe('RelationalComparatorRouter._isQualitativeRule (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('scaleName-triggers', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('scaleName-triggers', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 500 }); ).run({ effort: 500 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'scaleName-triggers'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'scaleName-triggers');
@@ -156,7 +156,7 @@ describe('RelationalComparatorRouter._isQualitativeRule (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('decay-blur-triggers', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('decay-blur-triggers', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 500 }); ).run({ effort: 500 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'decay-blur-triggers'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'decay-blur-triggers');
@@ -190,7 +190,7 @@ describe('RelationalComparatorRouter._isQualitativeRule (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('marginSteps-correct', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('marginSteps-correct', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800 }); ).run({ effort: 800 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'marginSteps-correct'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'marginSteps-correct');
@@ -231,7 +231,7 @@ describe('RelationalComparatorRouter._isQualitativeRule (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('plain-numeric', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('plain-numeric', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800 }); ).run({ effort: 800 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'plain-numeric'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'plain-numeric');
@@ -276,7 +276,7 @@ describe('RelationalComparatorRouter._isQualitativeRule (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('getImplType-consistent', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('getImplType-consistent', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 1500 }); ).run({ effort: 1500 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'getImplType-consistent'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'getImplType-consistent');
@@ -322,7 +322,7 @@ describe('RelationalComparatorRouter._isQualitativeRule (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('hasValidProperty', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('hasValidProperty', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 1000 }); ).run({ effort: 1000 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'hasValidProperty'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'hasValidProperty');
@@ -72,7 +72,7 @@ describe('RelationalComparatorRule evaluation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('left-gt-right', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('left-gt-right', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800 }); ).run({ effort: 800 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'left-gt-right'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'left-gt-right');
@@ -111,7 +111,7 @@ describe('RelationalComparatorRule evaluation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('left-lt-right', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('left-lt-right', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800 }); ).run({ effort: 800 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'left-lt-right'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'left-lt-right');
@@ -146,7 +146,7 @@ describe('RelationalComparatorRule evaluation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('possibility-bounded', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('possibility-bounded', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 1500 }); ).run({ effort: 1500 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'possibility-bounded'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'possibility-bounded');
@@ -181,7 +181,7 @@ describe('RelationalComparatorRule evaluation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('result-shape-stable', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('result-shape-stable', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800 }); ).run({ effort: 800 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'result-shape-stable'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'result-shape-stable');
@@ -220,7 +220,7 @@ describe('RelationalComparatorRule evaluation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('determinism', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('determinism', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800 }); ).run({ effort: 800 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'determinism'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'determinism');
@@ -489,6 +489,64 @@ describe('Rule-kind × partial-graph parity (rigor)', () => {
const unchanged = a.relationManager.getDirectRelation(a.resolveNodeId('u:0'), 'balance', a.resolveNodeId('doc:0')); const unchanged = a.relationManager.getDirectRelation(a.resolveNodeId('u:0'), 'balance', a.resolveNodeId('doc:0'));
assert.equal(unchanged.changed_last_at, 5000, 'value-unchanged modify keeps old timestamp (override not a refresh)'); assert.equal(unchanged.changed_last_at, 5000, 'value-unchanged modify keeps old timestamp (override not a refresh)');
} }
// ---- reliability propagation across kinds ----
{
// chain: product of edge reliabilities (0.9 * 0.8)
const a = mkArbiter();
a.setRelationConfig('can_access', { type: 'chain', steps: [{ relation: 'member_of', direction: 'out' }, { relation: 'reads', direction: 'out' }] });
a.addRelation('u:0', 'member_of', 'g:0', { possibility: 0.8, reliability: 0.9 });
a.addRelation('g:0', 'reads', 'doc:0', { possibility: 0.7, reliability: 0.8 });
const c = a.check('u:0', 'can_access', 'doc:0');
assert.ok(Math.abs(c.reliability - 0.72) < 0.01, `chain reliability product, got ${c.reliability}`);
// multi_hop: product along the path
a.setRelationConfig('can_hop', { type: 'multi_hop', relation: 'member_of', maxDepth: 3 });
a.addNode('g:1', 'group');
a.addRelation('u:0', 'member_of', 'g:0', { possibility: 0.8, reliability: 0.9 });
a.addRelation('g:0', 'member_of', 'g:1', { possibility: 0.7, reliability: 0.8 });
const m = a.check('u:0', 'can_hop', 'g:1');
assert.ok(Math.abs(m.reliability - 0.72) < 0.01, `multi_hop reliability product, got ${m.reliability}`);
// union: the max child's reliability (editor 0.9 / reli 0.5)
a.setRelationConfig('can_union', { union: { rules: [{ relation: 'owner' }, { relation: 'editor' }] } });
a.addRelation('u:0', 'owner', 'doc:0', { possibility: 0.8, reliability: 0.9 });
a.addRelation('u:0', 'editor', 'doc:0', { possibility: 0.9, reliability: 0.5 });
const u = a.check('u:0', 'can_union', 'doc:0');
assert.equal(round4(u.reliability), 0.5, `union selected-child reliability, got ${u.reliability}`);
// intersection: the min child's reliability (verified 0.6)
a.setRelationConfig('can_intersect', { intersection: { rules: [{ relation: 'owner' }, { relation: 'verified' }] } });
a.addRelation('u:0', 'verified', 'doc:0', { possibility: 0.5, reliability: 0.6 });
const i = a.check('u:0', 'can_intersect', 'doc:0');
assert.equal(round4(i.reliability), 0.6, `intersection selected-child reliability, got ${i.reliability}`);
// exclusion: product of both legs (0.9 * 0.7)
a.setRelationConfig('can_excl', { exclusion: [{ relation: 'owner' }, { relation: 'banned' }] });
a.addRelation('u:0', 'banned', 'doc:0', { possibility: 0.5, reliability: 0.7 });
const e = a.check('u:0', 'can_excl', 'doc:0');
assert.ok(Math.abs(e.reliability - 0.63) < 0.01, `exclusion product reliability, got ${e.reliability}`);
// defeasible: when reli * unless reli (0.9 * 0.7)
a.setRelationConfig('can_def', { type: 'defeasible', when: { relation: 'owner' }, unless: { relation: 'banned' } });
const d = a.check('u:0', 'can_def', 'doc:0');
assert.ok(Math.abs(d.reliability - 0.63) < 0.01, `defeasible combined reliability, got ${d.reliability}`);
}
// ---- multi_hop value collection through partial (no crash, values flow) ----
{
const a = mkArbiter();
a.addNode('g:1', 'group');
a.setRelationConfig('can_access', { type: 'multi_hop', relation: 'member_of', maxDepth: 3 });
a.addRelation('u:0', 'member_of', 'g:0', { possibility: 0.8 });
a.addRelation('g:0', 'member_of', 'g:1', { possibility: 0.7, value: 42 });
const p = a.check('u:0', 'can_access', 'g:1', { collectValues: true });
assert.ok(Array.isArray(p.collectedValues) && p.collectedValues.length > 0, 'multi_hop persistent values collected');
assert.ok(typeof p.collectedValues[0].value?.min === 'number', 'multi_hop value interval present');
a.removeRelation('u:0', 'member_of', 'g:0');
a.removeRelation('g:0', 'member_of', 'g:1');
const r = a.check('u:0', 'can_access', 'g:1', {
collectValues: true,
partialGraph: { relations: [
{ src: 'u:0', relation: 'member_of', dst: 'g:0', possibility: 0.8 },
{ src: 'g:0', relation: 'member_of', dst: 'g:1', possibility: 0.7, value: 42 }
] }
});
assert.ok(Array.isArray(r.collectedValues) && r.collectedValues.length > 0, 'multi_hop partial values collected');
}
// ---- binary mode agrees with normal at the same threshold ---- // ---- binary mode agrees with normal at the same threshold ----
{ {
const a = mkArbiter(); const a = mkArbiter();
+2 -2
View File
@@ -28,7 +28,7 @@ describe('js-rigor smoke', () => {
rigor.invariant('non-negative', ({ actual }) => actual >= 0), rigor.invariant('non-negative', ({ actual }) => actual >= 0),
rigor.invariant('idempotent', ({ actual, fn }) => fn(actual) === actual) rigor.invariant('idempotent', ({ actual, fn }) => fn(actual) === actual)
]) ])
).run({ effort: 200 }); ).run({ effort: 200 , artifacts: { dir: '', persist: 'never' }});
assert.ok(report, 'campaign returns a report'); assert.ok(report, 'campaign returns a report');
assert.equal(typeof report.toTAP, 'function', 'report has toTAP()'); assert.equal(typeof report.toTAP, 'function', 'report has toTAP()');
@@ -46,7 +46,7 @@ describe('js-rigor smoke', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('equals-one', ({ actual }) => actual === 1) rigor.invariant('equals-one', ({ actual }) => actual === 1)
]) ])
).run({ effort: 50 }); ).run({ effort: 50 , artifacts: { dir: '', persist: 'never' }});
// Report shape varies — log it for debugging. // Report shape varies — log it for debugging.
if (process.env.TEST_DEBUG === '1') { if (process.env.TEST_DEBUG === '1') {
+2 -2
View File
@@ -125,7 +125,7 @@ describe('Condensed snapshot round trip (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('snapshot-parity', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('snapshot-parity', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 400, seed: 'snapshot-parity' }); ).run({ effort: 400, seed: 'snapshot-parity' , artifacts: { dir: '', persist: 'never' }});
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'snapshot-parity'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'snapshot-parity');
assert.ok(inv); assert.ok(inv);
@@ -177,7 +177,7 @@ describe('Condensed snapshot round trip (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('readonly-enforced', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('readonly-enforced', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 300, seed: 'snapshot-readonly' }); ).run({ effort: 300, seed: 'snapshot-readonly' , artifacts: { dir: '', persist: 'never' }});
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'readonly-enforced'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'readonly-enforced');
assert.ok(inv); assert.ok(inv);
@@ -155,7 +155,7 @@ describe('Condensed snapshot quantization parity (rigor)', () => {
return after1.every((v, i) => Math.abs(v - after2[i]) <= TOL); return after1.every((v, i) => Math.abs(v - after2[i]) <= TOL);
}) })
]) ])
).run({ effort: 300, seed: 'snapshot-quantization-parity' }); ).run({ effort: 300, seed: 'snapshot-quantization-parity' , artifacts: { dir: '', persist: 'never' }});
const inv = result.crucibleVerdict; const inv = result.crucibleVerdict;
assert.equal(inv.passed, true, [ assert.equal(inv.passed, true, [
+3 -3
View File
@@ -188,7 +188,7 @@ describe('Traversal semantics parity (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('chain-direction-parity', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('chain-direction-parity', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 1500, seed: 'traversal-chain-direction' }); ).run({ effort: 1500, seed: 'traversal-chain-direction' , artifacts: { dir: '', persist: 'never' }});
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'chain-direction-parity'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'chain-direction-parity');
assert.ok(inv, 'invariant missing'); assert.ok(inv, 'invariant missing');
@@ -264,7 +264,7 @@ describe('Traversal semantics parity (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('ttu-multi-tuple-parity', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('ttu-multi-tuple-parity', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 1200, seed: 'traversal-ttu-multituple' }); ).run({ effort: 1200, seed: 'traversal-ttu-multituple' , artifacts: { dir: '', persist: 'never' }});
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'ttu-multi-tuple-parity'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'ttu-multi-tuple-parity');
assert.ok(inv, 'invariant missing'); assert.ok(inv, 'invariant missing');
@@ -340,7 +340,7 @@ describe('Traversal semantics parity (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('update-path-parity', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('update-path-parity', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 600, seed: 'traversal-update-path' }); ).run({ effort: 600, seed: 'traversal-update-path' , artifacts: { dir: '', persist: 'never' }});
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'update-path-parity'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'update-path-parity');
assert.ok(inv, 'invariant missing'); assert.ok(inv, 'invariant missing');
+1 -1
View File
@@ -144,7 +144,7 @@ describe('Value TTL expiry through the comparator path (rigor)', () => {
}), }),
rigor.invariant('no action errors', (ctx) => ctx.error === null) rigor.invariant('no action errors', (ctx) => ctx.error === null)
]) ])
).run({ effort: 400, seed: 'ttl-expiry-parity', maxTraceLength: 30 }); ).run({ effort: 400, seed: 'ttl-expiry-parity', maxTraceLength: 30 , artifacts: { dir: '', persist: 'never' }});
const inv = result.crucibleVerdict; const inv = result.crucibleVerdict;
assert.equal(inv.passed, true, [ assert.equal(inv.passed, true, [
+6 -6
View File
@@ -92,7 +92,7 @@ describe('TupleToUsersetRule evaluation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('no-tuples', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('no-tuples', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 500 }); ).run({ effort: 500 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'no-tuples'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'no-tuples');
@@ -141,7 +141,7 @@ describe('TupleToUsersetRule evaluation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('min-fusion', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('min-fusion', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800 }); ).run({ effort: 800 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'min-fusion'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'min-fusion');
@@ -194,7 +194,7 @@ describe('TupleToUsersetRule evaluation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('multi-tuple-max', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('multi-tuple-max', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 1500 }); ).run({ effort: 1500 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'multi-tuple-max'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'multi-tuple-max');
@@ -232,7 +232,7 @@ describe('TupleToUsersetRule evaluation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('possibility-bounded', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('possibility-bounded', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800 }); ).run({ effort: 800 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'possibility-bounded'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'possibility-bounded');
@@ -292,7 +292,7 @@ describe('TupleToUsersetRule evaluation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('early-exit', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('early-exit', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 200 }); ).run({ effort: 200 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'early-exit'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'early-exit');
@@ -342,7 +342,7 @@ describe('TupleToUsersetRule evaluation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('cycle-detection', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('cycle-detection', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 200 }); ).run({ effort: 200 , artifacts: { dir: '', persist: 'never' }});
if (process.env.TEST_DEBUG === '1') console.log(report.toTAP()); if (process.env.TEST_DEBUG === '1') console.log(report.toTAP());
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'cycle-detection'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'cycle-detection');
+1 -1
View File
@@ -175,7 +175,7 @@ describe('Transactional batch atomicity and batch+PLTC (rigor)', () => {
}), }),
rigor.invariant('no action errors', (ctx) => ctx.error === null) rigor.invariant('no action errors', (ctx) => ctx.error === null)
]) ])
).run({ effort: 400, seed: 'tx-rollback-parity', maxTraceLength: 25 }); ).run({ effort: 400, seed: 'tx-rollback-parity', maxTraceLength: 25 , artifacts: { dir: '', persist: 'never' }});
const inv = result.crucibleVerdict; const inv = result.crucibleVerdict;
assert.equal(inv.passed, true, [ assert.equal(inv.passed, true, [
+1 -1
View File
@@ -190,7 +190,7 @@ describe('Value-layer and batch-path freshness (rigor)', () => {
return typeof actual.success === 'boolean'; return typeof actual.success === 'boolean';
}) })
]) ])
).run({ effort: 400, seed: 'value-freshness-parity', maxTraceLength: 30 }); ).run({ effort: 400, seed: 'value-freshness-parity', maxTraceLength: 30 , artifacts: { dir: '', persist: 'never' }});
const inv = result.crucibleVerdict; const inv = result.crucibleVerdict;
assert.equal(inv.passed, true, [ assert.equal(inv.passed, true, [
+6 -6
View File
@@ -93,7 +93,7 @@ describe('Authorization state consistency (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('ttu-mutation', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('ttu-mutation', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 400, seed: 'consistency-ttu-mutation' }); ).run({ effort: 400, seed: 'consistency-ttu-mutation' , artifacts: { dir: '', persist: 'never' }});
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'ttu-mutation'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'ttu-mutation');
assert.ok(inv); assert.ok(inv);
@@ -154,7 +154,7 @@ describe('Authorization state consistency (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('chain-mutation', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('chain-mutation', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 400, seed: 'consistency-chain-mutation' }); ).run({ effort: 400, seed: 'consistency-chain-mutation' , artifacts: { dir: '', persist: 'never' }});
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'chain-mutation'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'chain-mutation');
assert.ok(inv); assert.ok(inv);
@@ -222,7 +222,7 @@ describe('Authorization state consistency (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('config-change', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('config-change', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 400, seed: 'consistency-config-change' }); ).run({ effort: 400, seed: 'consistency-config-change' , artifacts: { dir: '', persist: 'never' }});
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'config-change'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'config-change');
assert.ok(inv); assert.ok(inv);
@@ -274,7 +274,7 @@ describe('Authorization state consistency (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('threshold-excludes', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('threshold-excludes', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 400, seed: 'consistency-threshold' }); ).run({ effort: 400, seed: 'consistency-threshold' , artifacts: { dir: '', persist: 'never' }});
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'threshold-excludes'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'threshold-excludes');
assert.ok(inv); assert.ok(inv);
@@ -340,7 +340,7 @@ describe('Authorization state consistency (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('values-complete', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('values-complete', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 400, seed: 'consistency-value-completeness' }); ).run({ effort: 400, seed: 'consistency-value-completeness' , artifacts: { dir: '', persist: 'never' }});
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'values-complete'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'values-complete');
assert.ok(inv); assert.ok(inv);
@@ -377,7 +377,7 @@ describe('Authorization state consistency (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('deterministic', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('deterministic', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 300, seed: 'consistency-determinism' }); ).run({ effort: 300, seed: 'consistency-determinism' , artifacts: { dir: '', persist: 'never' }});
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'deterministic'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'deterministic');
assert.ok(inv); assert.ok(inv);
@@ -100,7 +100,7 @@ describe('Defeasible logic, DSL parity, aggregation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('defeasible-semantics', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('defeasible-semantics', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 600, seed: 'defeasible-semantics' }); ).run({ effort: 600, seed: 'defeasible-semantics' , artifacts: { dir: '', persist: 'never' }});
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'defeasible-semantics'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'defeasible-semantics');
assert.ok(inv); assert.ok(inv);
@@ -174,7 +174,7 @@ describe('Defeasible logic, DSL parity, aggregation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('dsl-parity', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('dsl-parity', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 400, seed: 'dsl-runtime-parity' }); ).run({ effort: 400, seed: 'dsl-runtime-parity' , artifacts: { dir: '', persist: 'never' }});
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'dsl-parity'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'dsl-parity');
assert.ok(inv); assert.ok(inv);
@@ -265,7 +265,7 @@ describe('Defeasible logic, DSL parity, aggregation (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('aggregation-complete', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('aggregation-complete', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 500, seed: 'aggregation-completeness' }); ).run({ effort: 500, seed: 'aggregation-completeness' , artifacts: { dir: '', persist: 'never' }});
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'aggregation-complete'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'aggregation-complete');
assert.ok(inv); assert.ok(inv);
+6 -6
View File
@@ -312,7 +312,7 @@ describe('Zanzibar rewrite-rule semantics (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('union-max', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('union-max', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 400, seed: 'zanzibar-union' }); ).run({ effort: 400, seed: 'zanzibar-union' , artifacts: { dir: '', persist: 'never' }});
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'union-max'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'union-max');
assert.ok(inv); assert.ok(inv);
@@ -356,7 +356,7 @@ describe('Zanzibar rewrite-rule semantics (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('intersection-min', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('intersection-min', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 400, seed: 'zanzibar-intersection' }); ).run({ effort: 400, seed: 'zanzibar-intersection' , artifacts: { dir: '', persist: 'never' }});
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'intersection-min'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'intersection-min');
assert.ok(inv); assert.ok(inv);
@@ -399,7 +399,7 @@ describe('Zanzibar rewrite-rule semantics (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('exclusion-blocks', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('exclusion-blocks', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 400, seed: 'zanzibar-exclusion' }); ).run({ effort: 400, seed: 'zanzibar-exclusion' , artifacts: { dir: '', persist: 'never' }});
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'exclusion-blocks'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'exclusion-blocks');
assert.ok(inv); assert.ok(inv);
@@ -434,7 +434,7 @@ describe('Zanzibar rewrite-rule semantics (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('expand-parity', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('expand-parity', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 800, seed: 'zanzibar-expand-parity' }); ).run({ effort: 800, seed: 'zanzibar-expand-parity' , artifacts: { dir: '', persist: 'never' }});
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'expand-parity'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'expand-parity');
assert.ok(inv); assert.ok(inv);
@@ -490,7 +490,7 @@ describe('Zanzibar rewrite-rule semantics (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('nested-weakest-link', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('nested-weakest-link', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 500, seed: 'zanzibar-nested-chain' }); ).run({ effort: 500, seed: 'zanzibar-nested-chain' , artifacts: { dir: '', persist: 'never' }});
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'nested-weakest-link'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'nested-weakest-link');
assert.ok(inv); assert.ok(inv);
@@ -546,7 +546,7 @@ describe('Zanzibar rewrite-rule semantics (rigor)', () => {
rigor.crucible([ rigor.crucible([
rigor.invariant('cycle-safe', ({ error, errorMessage }) => !error && !errorMessage) rigor.invariant('cycle-safe', ({ error, errorMessage }) => !error && !errorMessage)
]) ])
).run({ effort: 400, seed: 'zanzibar-cycle-safety' }); ).run({ effort: 400, seed: 'zanzibar-cycle-safety' , artifacts: { dir: '', persist: 'never' }});
const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'cycle-safe'); const inv = report.crucibleVerdict?.invariants?.find(i => i.name === 'cycle-safe');
assert.ok(inv); assert.ok(inv);