Skip to content

feat: unless exception clauses on require/forbid/permit/expect#52

Merged
rmichaelthomas merged 1 commit into
mainfrom
feat/unless-on-deontics
Jul 4, 2026
Merged

feat: unless exception clauses on require/forbid/permit/expect#52
rmichaelthomas merged 1 commit into
mainfrom
feat/unless-on-deontics

Conversation

@rmichaelthomas

Copy link
Copy Markdown
Owner

Summary

Adds unless <exception> exception clauses to the four condition-grammar verbs — require, forbid, permit, expect — per Checkpoint v28 (Unless on Deontics Design Lock). Zero new reserved words: unless was already a connective via the existing when ... unless guard grammar.

Phases completed:

  • Parserexception: ASTNode | None field added to RequireNode/ForbidNode/PermitNode/ExpectNode (default compare=True, since the exception is semantic content, not inert metadata). unless consumed inside each of the four verb parsers via a shared _try_consume_unless_exception helper, positioned before because/inherited/temporal metadata. require each and when are untouched.
  • Interpreter — exception-aware evaluation in _exec_require/_exec_forbid/_exec_permit/_exec_expect: require/forbid/expect are excused when the exception holds; permit is narrowed (its emission suppressed) when the exception holds.
  • Rendererunless <exception> renders between the condition and the because suffix, in both render and render_with_explicit_precedence.
  • Analyzer — exception validated via _check_choose_condition; mixed and/or in the exception triggers the amber prompt (this logic actually lives in parser.py's _contains_mixed_precedence/_condition_is_mixed, not analyzer.py as the original spec assumed — corrected during implementation); detect_contradictions now carries guarded deontics through with conditional wording (... unless <exception>[ or <exception>]) instead of excluding them.
  • Docs — new "Exception clauses (unless)" section in docs/language/syntax.md.
  • Tests — 43 new tests across test_require.py, test_forbid.py, test_permit.py, test_expect.py, test_contradiction_detection.py, test_render_roundtrip.py.

Invariants verified:

  • Vocabulary count unchanged — the public 60-word vocabulary (VERBS|CONNECTIVES|OPERATORS|ARTICLES|V2_RESERVED|MULTI_WORD_RESERVED|DECLARATIONS) is identical before and after; vocabulary.py was not touched. (Note: len(ALL_RESERVED) prints 61 both before and after this branch — a pre-existing, unrelated fact caused by the tombstoned combine word being included in that constant.)
  • RequireEachNode and WhenNode are untouched.
  • Render round-trip holds for all four verbs with unless, including full canonical metadata order.
  • Backward compatibility: all pre-existing tests pass unmodified.

Test plan

  • pytest tests/ — 1557 passed, 0 failed
  • Smoke test: forbid total is above 10000 unless approved is equal to yes with approved = noPROHIBITION_VIOLATED
  • Smoke test: same with approved = yesSUCCESS

🤖 Generated with Claude Code

Extends require/forbid/permit/expect with an optional `unless <exception>`
clause, mirroring the existing `when...unless` guard grammar but scoped to
each verb's polarity: require/forbid/expect are excused when the exception
holds, permit is narrowed (suppressed) when it holds. Zero new reserved
words (`unless` was already a connective via `when` guards).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@rmichaelthomas
rmichaelthomas merged commit b02b301 into main Jul 4, 2026
2 checks passed
@rmichaelthomas
rmichaelthomas deleted the feat/unless-on-deontics branch July 4, 2026 16:25
rmichaelthomas added a commit that referenced this pull request Jul 15, 2026
feat: unless exception clauses on require/forbid/permit/expect
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