Skip to content

fix: cs/cf single-sub passthrough bypasses Group, Fate, and Versus rejections #109#110

Merged
edloidas merged 1 commit into
masterfrom
issue-109
Apr 29, 2026
Merged

fix: cs/cf single-sub passthrough bypasses Group, Fate, and Versus rejections #109#110
edloidas merged 1 commit into
masterfrom
issue-109

Conversation

@edloidas
Copy link
Copy Markdown
Owner

Closed three CRITICAL bypasses introduced by #97's single-sub-roll Group passthrough. All three shared one root cause: shallow reject helpers vs. the deep containsDicePool/deepContainsDicePool walk that the Group passthrough now relies on.

  • Added containsMultiSubGroup deep-walk to ast.ts; rejectGroupTarget uses it on the single sub-expression so a multi-sub Group buried under arithmetic, function calls, or unary ops re-rejects ({{1d20, 1d20}kh1}cs>18, {abs({1d6, 2d8})}cs>5, {-{1d6, 2d8}}cs>5, {floor({1d6, 2d8}/1)}cs>5).
  • Added deepContainsFatePool mirroring deepContainsDicePool; containsFatePool's Group case switches to it so {4dF+1d6}cf, ({4dF+1d6})cf, and {abs(4dF)}cf no longer flip Fate +1 faces to fumble: true.
  • Added containsVersus deep-walk; rejectVersusTarget uses it against Group([single])'s inner expression so Versus buried under arithmetic/function-call rejects with NESTED_VERSUS ({1d20 vs 15}cs>18, {1+(1d20 vs 15)}cs>18, {abs(1d20 vs 15)}cs>18, {1d20 vs 15}s, 4d6>={1d20 vs 15}).
  • Added the missing rejectVersusTarget call to parseModifier so {1d20 vs 15}kh1 rejects — closes a pre-existing inconsistency where the parens form rejected with INVALID_MODIFIER_TARGET while the brace form silently dropped degree/natural.
  • Strengthened the evaluator round-trip test: asserts result.expression === '{1d20}kh1cs>18' (preserves user notation) and tightened the MockRNG sequence to one value (was four — three were dead weight).
  • Added evaluator coverage for the four flipped-accept tests from fix: cs/cf on modifier-wrapped group flags dropped sub-roll dice #97 ({1d6}cs>5, ({1d6})cs>5, {1d6}cf<2, {1d6}kh1cf<2).
  • {1d20 vs 15}+5 continues to ACCEPT — BinaryOp uses mergeContext and propagates degree/natural correctly. Explicitly tested.
  • Updated STAGE3.md group edge-cases list and CHANGELOG.md [Unreleased] with three Fixed entries.

Closes #109

Drafted with AI assistance

…jections #109

Closed three CRITICAL bypasses introduced by #97's single-sub-roll Group passthrough at `parseCritThreshold`. All three shared one root cause — shallow reject helpers that the new Group route walked past while `containsDicePool`'s deep walk pulled the inner pool through.
Added `containsMultiSubGroup` deep-walk to `ast.ts` and used it from `rejectGroupTarget` so a buried multi-sub Group (`{{1d20, 1d20}kh1}cs>18`, `{abs({1d6, 2d8})}cs>5`, `{-{1d6, 2d8}}cs>5`, `{floor({1d6, 2d8}/1)}cs>5`) re-rejects with `INVALID_CRIT_THRESHOLD_TARGET`.
Added `deepContainsFatePool` mirroring `deepContainsDicePool` and used it from `containsFatePool`'s Group case so `{4dF+1d6}cf`, `({4dF+1d6})cf`, and `{abs(4dF)}cf` no longer flip Fate `+1` faces to `fumble: true` via the bare-cf default check.
Added `containsVersus` deep-walk and used it from `rejectVersusTarget` so Versus buried under arithmetic/function-call inside a single-sub Group (`{1d20 vs 15}cs>18`, `{1+(1d20 vs 15)}cs>18`, `{abs(1d20 vs 15)}cs>18`, `{1d20 vs 15}s`, `4d6>={1d20 vs 15}`) throws `NESTED_VERSUS` instead of silently dropping `degree`/`natural`.
Added the missing `rejectVersusTarget` call to `parseModifier` so `{1d20 vs 15}kh1` rejects too — closes a pre-existing inconsistency where `(1d20 vs 15)kh1` rejected with `INVALID_MODIFIER_TARGET` while the brace form silently dropped metadata.
Strengthened the evaluator round-trip test (asserts `result.expression === '{1d20}kh1cs>18'` and tightened the MockRNG sequence to a single value) and added evaluator coverage for the four flipped accept tests (`{1d6}cs>5`, `({1d6})cs>5`, `{1d6}cf<2`, `{1d6}kh1cf<2`).
Updated `STAGE3.md` group-edge-cases list and `CHANGELOG.md [Unreleased]` with three entries covering the closures.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@edloidas edloidas self-assigned this Apr 29, 2026
@edloidas edloidas merged commit 804fda6 into master Apr 29, 2026
10 checks passed
@edloidas edloidas deleted the issue-109 branch April 29, 2026 14:31
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.

fix: cs/cf single-sub passthrough bypasses Group, Fate, and Versus rejections

1 participant