Skip to content

fix: opaque-atom predicate awareness in deontic contradiction detection#55

Merged
rmichaelthomas merged 3 commits into
mainfrom
fix/predicate-contradiction-awareness
Jul 4, 2026
Merged

fix: opaque-atom predicate awareness in deontic contradiction detection#55
rmichaelthomas merged 3 commits into
mainfrom
fix/predicate-contradiction-awareness

Conversation

@rmichaelthomas

Copy link
Copy Markdown
Owner

Summary

The deontic contradiction pre-pass (detect_contradictions in analyzer.py, driven by _collect_deontic_statements in run.py) parsed require/forbid lines with no predicate table. After the Definitional Era (v31/v32), require account is high-risk — where high-risk is a predicate introduced by an earlier define — 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_statements rewritten as a single ordered scan that accumulates predicate names from define lines as it encounters them (mirroring the interpreter's forward-declaration rule) and threads them into every require/forbid parse.
  • src/liminate/analyzer.py_Deontic gains a "pred" kind; _extract_deontic handles PredicateApplicationNode as an opaque atom (negated applications and non-NameRef subjects 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 a program_warnings whole-program helper (the existing warnings_for helper parses without a predicate table and can't exercise predicates).

Six new tests:

  1. Same-predicate require/forbid still warns (the sound case)
  2. Different-predicate require/require does not warn (false-positive kill)
  3. Predicate + literal require/require does not warn (false-positive kill)
  4. Pre-declaration bareword use stays string equality (ordering correctness)
  5. Negated predicate application is out of scope
  6. Guarded predicate deontic keeps conditional unless wording

Five invariants satisfied:

  1. Pre-pass/interpreter agreement on predicate resolution (single ordered scan, no whole-file pre-scan)
  2. Opaque-atom boundary — no code path inspects a predicate's body
  3. No false positives from predicates — only same-field same-predicate require/forbid pairs warn
  4. Literal/numeric behavior frozen — all 20 pre-existing tests pass unmodified
  5. src/liminate/build.py is 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 regressions
  • python3 -m pytest tests/test_contradiction_detection.py -v — 26 passed (20 existing unchanged + 6 new)
  • Verified all four referenced file blob SHAs matched live main before starting
  • Confirmed git diff --stat touches only the three intended files

🤖 Generated with Claude Code

rmichaelthomas and others added 3 commits July 4, 2026 15:50
_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
rmichaelthomas merged commit ad95541 into main Jul 4, 2026
2 checks passed
@rmichaelthomas
rmichaelthomas deleted the fix/predicate-contradiction-awareness branch July 4, 2026 22:59
rmichaelthomas added a commit that referenced this pull request Jul 15, 2026
…n-awareness

fix: opaque-atom predicate awareness in deontic contradiction detection
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant