dynamicsystemsarchitecture.org

Decision Kernels

Turns a research state into one graduated, concrete action — not a binary gate, not a menu of options. v2: rebuilt after direct critique that the original binary version collapsed real gradations (n=42 and n=42,000 got the same answer).

PurposeEvidence sufficiency, escalation recommendation, claim-relationship classification (4 real categories, not just contradiction/not), and claim-vs-evidence validation — all with continuous confidence, not booleans.
StatusVerified — all 4 functions executed directly with real output, re-confirmed fresh below
Built fromDirect critique of an earlier binary version
Depends onResearch State Kernel (feeds this kernel's stage input); exploratory_heuristic_42.py (previously missing, now resolved — see below)
Superseded by
EvidenceStructural bug found and fixed on this page; full real execution output below.

A bug found and fixed while preparing this page

The original file's self-test block had no if __name__ == "__main__": guard. Because its indentation matched the function above it, Python silently absorbed the entire test block as unreachable code inside validate_claim_against_evidence(), after its return statement. The file imported fine, all four functions worked correctly when called directly — but running the file itself produced zero output, no error, nothing. Fixed by adding the missing guard.

Fixing it surfaced a second, separate issue: the file imports MIN_EXPLORATORY_N and CONFIDENCE_TIERS from exploratory_heuristic_42.py — a file referenced since the very first version of this site's Infrastructure page and, for a long time, never actually uploaded. It has since arrived. All four functions are now confirmed running with real output, shown fresh below — not just correct on inspection.

Source (fixed)

def classify_claim_relationship(claim_a: dict, claim_b: dict) -> dict:
    """Real fix: four categories (contradiction / tension / refinement /
    independent), not a binary contradiction check that only caught
    same-subject-opposite-direction claims."""
    if claim_a.get("subject") != claim_b.get("subject"):
        return {"relationship": "independent", "reason": "Different subjects."}

    metric_a, metric_b = claim_a.get("metric"), claim_b.get("metric")
    dir_a, dir_b = claim_a.get("direction"), claim_b.get("direction")

    if metric_a == metric_b and dir_a and dir_b:
        if dir_a != dir_b:
            return {"relationship": "contradiction",
                    "reason": "Same subject, same metric, incompatible directions."}
        return {"relationship": "refinement",
                "reason": "Same subject, same metric, same direction -- likely
                            replication, not new information."}

    if metric_a != metric_b:
        return {"relationship": "tension",
                "reason": "Same subject, different metrics -- a real tradeoff
                            worth surfacing, not a direct contradiction."}

    return {"relationship": "independent", "reason": "Insufficient information to classify further."}

Real execution — all four functions, run fresh

=== Real test: graduated evidence sufficiency, n=42 vs n=42,000 now genuinely distinguishable ===
{'confidence': 0.3, 'action': 'continue_cautiously', 'reason': 'n=42 against a exploratory threshold of 42 (ratio=1.00).'}
{'confidence': 1.0, 'action': 'continue_high_confidence', 'reason': 'n=42000 against a exploratory threshold of 42 (ratio=1000.00).'}
{'confidence': 0.126, 'action': 'gather_more_data', 'reason': 'n=42 against a performance threshold of 100 (ratio=0.42).'}

=== Real test: graduated escalation, continuous spread instead of a boolean ===
{'confidence': 0.97, 'action': 'escalate_high_confidence', 'reason': 'spread=0.0300 across 2 independent passes.'}
{'confidence': 0.55, 'action': 'investigate_disagreement', 'reason': 'spread=0.4500 across 2 independent passes.'}
{'confidence': 0.1, 'action': 'reframe_question', 'reason': 'spread=0.9000 across 2 independent passes.'}

=== Real test: the exact case this function exists to catch — tension, not contradiction ===
{'relationship': 'tension', 'reason': "Same subject ('estimator_X'), different metrics
 ('accuracy' vs 'calibration') -- a real tradeoff worth surfacing, not a direct contradiction."}

=== Real test: a genuine direct contradiction, still caught correctly ===
{'relationship': 'contradiction', 'reason': "Same subject ('estimator_A_vs_B'), same metric
 ('accuracy'), incompatible directions: 'A_better' vs 'B_better'."}

"Improves accuracy" and "hurts calibration" are not opposites — an earlier binary version would have missed this entirely. Correctly flagged as tension, not silently dropped as unrelated — and a genuine direct contradiction, tested right alongside it, is still caught correctly rather than over-corrected into "everything is nuance."

Comments

We'll only use this to contact you if we reply.