fix: opaque-atom predicate awareness in deontic contradiction detection#55
Merged
Merged
Conversation
_collect_deontic_statements previously parsed require/forbid lines with no predicate table, so `is <predicate>` misparsed as string equality instead of a PredicateApplicationNode. Rewrite as a single ordered scan that accumulates predicate names from `define` lines as it encounters them, mirroring the interpreter's forward-declaration rule. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…tector _extract_deontic now handles PredicateApplicationNode, extracting it as a kind="pred" _Deontic keyed on the predicate name. Rule 1 (require+forbid, same op/value) already generalizes soundly to predicates. Rule 4 (two equality requirements with different values) is now guarded against predicate kinds, since two different predicates -- or a predicate and a literal -- may well co-hold and the detector has no basis to claim otherwise. This kills the false-positive class where different-predicate or predicate+literal requirements were wrongly flagged as contradictory. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds program_warnings, a whole-program run() based helper needed because the existing analyzer-only warnings_for parses without a predicate table and would misparse `is <predicate>` exactly as the bug did. Covers: same-predicate require/forbid warning, different-predicate and predicate+literal silence, pre-declaration ordering, negated-predicate exclusion, and guarded-predicate exception wording. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
rmichaelthomas
added a commit
that referenced
this pull request
Jul 15, 2026
…n-awareness fix: opaque-atom predicate awareness in deontic contradiction detection
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The deontic contradiction pre-pass (
detect_contradictionsinanalyzer.py, driven by_collect_deontic_statementsinrun.py) parsedrequire/forbidlines with no predicate table. After the Definitional Era (v31/v32),require account is high-risk— wherehigh-riskis a predicate introduced by an earlierdefine— misparsed as ordinary string equality instead of a predicate application. This produced false positives: two requirements applying different predicates, or a predicate requirement plus a literal-equality requirement, wrongly tripped Rule 4 ("two equality requirements with different values can never both hold").This PR makes the detector treat a predicate application as an opaque atom: it can't see inside the predicate's body (deliberately unbuilt, evidence-activated), but it can still reason that P ∧ ¬P is unsatisfiable whatever P means.
Files changed:
src/liminate/run.py—_collect_deontic_statementsrewritten as a single ordered scan that accumulates predicate names fromdefinelines as it encounters them (mirroring the interpreter's forward-declaration rule) and threads them into everyrequire/forbidparse.src/liminate/analyzer.py—_Deonticgains a"pred"kind;_extract_deontichandlesPredicateApplicationNodeas an opaque atom (negated applications and non-NameRefsubjects are out of scope); Rule 4 is guarded so it never fires when either side is a predicate.tests/test_contradiction_detection.py— six new tests via aprogram_warningswhole-program helper (the existingwarnings_forhelper parses without a predicate table and can't exercise predicates).Six new tests:
unlesswordingFive invariants satisfied:
src/liminate/build.pyis untouched (confirmed it runs no contradiction detection)Predicate body expansion for satisfiability analysis remains deliberately unbuilt (evidence-activated) — this PR only treats predicates as opaque atoms.
Test plan
python3 -m pytest -q— 1654 passed (1648 baseline + 6 new), zero regressionspython3 -m pytest tests/test_contradiction_detection.py -v— 26 passed (20 existing unchanged + 6 new)git diff --stattouches only the three intended files🤖 Generated with Claude Code