Report the evaluated value when a predicate application violates a prohibition or requirement - #70
Merged
Merged
Conversation
…ohibition or requirement
_condition_actual_values handled ConditionNode and CompoundConditionNode
but fell through to "" for PredicateApplicationNode. _exec_forbid and
_exec_require both append the result to the violation message only when
non-empty, so `forbid <field> is <predicate>` named the field and the
predicate but never the value that tripped it — e.g. "Prohibition
violated: household-income-at-move-in is initial-income-over-limit."
with no mention of the actual income. Equality-style conditions already
reported their value via the ConditionNode branch; this was a gap for
named predicates, not a design choice.
Adds a PredicateApplicationNode branch that reports the subject's actual
value plus the predicate's own body, e.g. "household-income-at-move-in
is 65000. initial-income-over-limit: is above 58320." A predicate body's
field is an implicit EachPronoun ("each is above 58320" when rendered
directly), which reads as noise in a violation message, so a leading
"each " is stripped before appending — a formatting call, not a
correctness one; noted here in case a different convention is preferred.
Scoped to exactly one function: _exec_forbid and _exec_require are
unchanged (they already append `actual` when non-empty), and render()'s
output for every existing node type is unchanged (confirmed by the full
existing suite passing, including round-trip tests).
Confirmed before branching that HEAD (de96e45) is what PyPI actually
serves as liminate 0.18.0: the published sdist's src/liminate is
byte-identical to this repo at HEAD (only __pycache__ differs), and
5db033d..de96e45 shows the TI-Q15 commit itself bumped pyproject.toml
to 0.18.0. No divergence to report.
Bumps to 0.18.1 (bugfix in message content, not a language or API
change). Adds 8 tests to tests/test_define.py: forbid/require predicate
violations report the value, an unset-subject predicate degrades to ""
without raising (direct call, mirroring the pre-existing ConditionNode
test shape), a negated predicate violation still reports, a compound
condition reports whichever branch actually failed regardless of its
type, and two regression tests pinning the exact pre-existing
ConditionNode message text byte-for-byte.
Local end-to-end check against the real Section 42 evidence-noncompliant
fact pattern (household-income-at-move-in: 65000) produces exactly:
"Prohibition violated: household-income-at-move-in is
initial-income-over-limit. household-income-at-move-in is 65000.
initial-income-over-limit: is above 58320." — not a substitute for the
post-deploy live re-run against the real Agreements API once this is
published and the liminate-dev pin is bumped, but confirms the fix and
the Build C boundary correction compose correctly.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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
_condition_actual_values(interpreter.py:1915) handledConditionNode/CompoundConditionNodebut fell through to""forPredicateApplicationNode._exec_forbid/_exec_requireonly append the result when non-empty, soforbid <field> is <predicate>named the field and the predicate but never the value that tripped it — e.g. a Section 42 income-limit violation said "household-income-at-move-in is initial-income-over-limit." with no mention of the $65,000 that actually triggered it. Equality-style conditions already reported their value; this was a gap, not a design choice.PredicateApplicationNodebranch: reports the subject's value plus the predicate's own body —household-income-at-move-in is 65000. initial-income-over-limit: is above 58320.EachPronoun, so rendering it directly produceseach is above 58320— "each" reads as noise in a violation message, so I strip a leadingeachbefore appending. This is a formatting choice, not a correctness one; happy to go the other way if you'd rather see it verbatim._exec_forbid/_exec_requireare unchanged (they already appendactualwhen non-empty).render()'s output for every existing node type is unchanged — confirmed by the full existing suite passing, including round-trip tests.pyproject.tomlto 0.18.1 (bugfix in message content, not a language or API change).Pre-branch confirmation (B.0)
Confirmed HEAD (
de96e45) is exactly what PyPI serves as0.18.0before touching anything: downloaded the realliminate-0.18.0.tar.gzsdist from PyPI and diffed itssrc/liminateagainst this repo atde96e45— byte-identical except__pycache__. Also confirmed5db033d..de96e45shows the TI-Q15 commit (#69) itself bumpedpyproject.tomlto0.18.0, so there's no unexplained gap. No divergence to report.Tests
Added 8 tests to
tests/test_define.py, all smoke-tested against the running fix before being written down:forbid <field> is <predicate>violating → message contains the value and the predicate bodyrequire <field> is <predicate>not met → same"", nothing raises (direct call, mirroring the pre-existingConditionNodetest shape)is not <predicate>(negated) violating → value still reported5/6. Compound condition (
A and B) → reports whichever branch actually failed, whether it's theConditionNodeor thePredicateApplicationNodeside (two tests, one per direction)7/8. Regression, blocking: exact byte-for-byte pre-existing message text for plain
ConditionNodeforbid/require violations, unchangedVerification
pytest tests/— full suite: 1859 passed, 0 failedpytest tests/test_define.py— 55 passed (44 pre-existing + 11 new, including 3 parse-only predicate tests untouched by this change)Local end-to-end check against the real Section 42
evidence-noncompliant.json(household-income-at-move-in: 65000) through the fixed interpreter and the boundary-corrected agreement (liminate-dev PR #100) produces exactly:This is a local sanity check, not the live re-run — that happens after publish + the liminate-dev pin bump (B.6), against the real Agreements API.
Test plan
pytest tests/— 1859 passedliminatepin to>=0.18.1in liminate-dev, deploy, confirm livereceipt-noncompliant-v2.json, confirm the income-limit rule's message contains65000🤖 Generated with Claude Code