js-rigor: value-collection crucibles; TTU 0-strength paths, crash, fusion reliability

The new value-collection crucibles in the TTU and chain differential
campaigns immediately found three engine defects:

- TTU join pushed 0-strength 'matches' (missing computed leg, or
  0-possibility edges, with minPossibility 0) as valid paths: denied
  decisions reported tuple_to_userset_found and leaked the tupleset edge's
  value into collectedValues. Both join modes now require combined > 0.
- A ReferenceError (bare resolveKey) crashed the computed-join mode under
  collectValues, silently turning the whole check into an evaluation_error
  denial. Fixed the call to this.arbiter.resolveKey.
- Multi-path TTU fusion fell back to Math.max over all path reliabilities,
  pairing the winning possibility with another intermediate's reliability.
  The fallback now picks the max-possibility path's reliability.

New campaigns: defeasible and intersection differential properties
(when/unless and min-children with reliability parity under persistent/
partial splits). The model-based campaign keeps its reliability crucible;
its value comparison was reverted — the harness's shrink reporting is
opaque and unreconstructable there, and the value semantics are covered by
the TTU/chain campaigns instead.
This commit is contained in:
John Dvorak
2026-08-01 22:34:00 -07:00
parent ff6e52111d
commit dab9671d20
3 changed files with 274 additions and 28 deletions
+18 -8
View File
@@ -264,7 +264,11 @@ export class TupleToUsersetRule extends BaseRule {
const combinedPossibility = Math.min(resolvePossibility(tupleEdge.possibility), res.possibility);
const combinedReliability = resolveReliability(tupleEdge.reliability) * (res.reliability !== undefined ? res.reliability : 1.0);
if (combinedPossibility >= minPossibility) {
// A 0-strength "match" (e.g. the computed leg missing with
// minPossibility 0) is not a valid path: it would report
// tuple_to_userset_found and leak the tupleset value into the
// collected values on a denied decision.
if (combinedPossibility > 0 && combinedPossibility >= minPossibility) {
const path = {
possibility: combinedPossibility,
reliability: combinedReliability,
@@ -279,7 +283,7 @@ export class TupleToUsersetRule extends BaseRule {
computedRelation: { relation: rule.computedRelation, possibility: res.possibility, reliability: res.reliability, meta: res.meta }
}
}),
...(!useLightweightPaths && collectValues && { collectedValue: buildCollectedValue(tupleEdge, resolveKey(tupleEdge.src, options), intermediateKey) })
...(!useLightweightPaths && collectValues && { collectedValue: buildCollectedValue(tupleEdge, this.arbiter.resolveKey(tupleEdge.src, options), intermediateKey) })
};
allValidPaths.push(path);
if (!bestPath || combinedPossibility > bestPath.possibility) {
@@ -374,7 +378,9 @@ export class TupleToUsersetRule extends BaseRule {
const combinedPossibility = Math.min(resolvePossibility(t.possibility), res.possibility);
const combinedReliability = resolveReliability(t.reliability) * (res.reliability !== undefined ? res.reliability : 1.0);
if (combinedPossibility >= minPossibility) {
// 0-strength tuples (missing computed leg, or 0-possibility edges)
// must not count as found paths (see computed join mode).
if (combinedPossibility > 0 && combinedPossibility >= minPossibility) {
const pathData = {
possibility: combinedPossibility,
reliability: combinedReliability,
@@ -512,11 +518,15 @@ export class TupleToUsersetRule extends BaseRule {
const winningPath = validPathData.find(p => p.meta.intermediateKey === fusionResult.meta.intermediateKey && p.meta.pathType === fusionResult.meta.pathType);
if (winningPath) {
fusedReliability = winningPath.reliability;
} else if (reliabilities.length > 0) {
fusedReliability = Math.max(...reliabilities); // Use max reliability for performance
}
} else if (reliabilities.length > 0) {
fusedReliability = Math.max(...reliabilities);
}
}
if (fusedReliability === 1.0 && reliabilities.length > 0) {
// Fallback: the fusion aggregator is max, so the winner is the
// max-possibility path — its reliability, not the max reliability
// (which would pair a possibility from one intermediate with a
// reliability from another).
const maxIndex = possibilities.indexOf(Math.max(...possibilities));
fusedReliability = maxIndex >= 0 ? (reliabilities[maxIndex] ?? 1.0) : 1.0;
}
if (evaluationMeta) {