Skip to content

fix: graceful disable/clear scopes + Redis rule enabled parity#11

Merged
Ch4s3 merged 2 commits into
mainfrom
api-fixes
May 24, 2026
Merged

fix: graceful disable/clear scopes + Redis rule enabled parity#11
Ch4s3 merged 2 commits into
mainfrom
api-fixes

Conversation

@Ch4s3

@Ch4s3 Ch4s3 commented May 23, 2026

Copy link
Copy Markdown
Owner

Three correctness/robustness fixes surfaced by a review of the Tier 1 features on main. No behavior change for existing valid call sites.

Fixes

1. disable/2 no longer crashes on grant-only scopes

do_disable/2 had no catch-all, so disable(:f, schedule: ...), when:, for_segment:, requires:, variant:, rule:, boolean: raised FunctionClauseError (surfaced as a telemetry exception) instead of a clean result. Now returns {:error, :unsupported_scope}. Grant-only gates are removed via clear/2 — the docstring now says so.

2. clear/2 accepts the requires: {parent, required} tuple

enable/2 accepts both requires: parent and requires: {parent, required}, but clear/2 only had the bare-atom clause, so the tuple form raised. Added the tuple clause (delegates to the bare-atom form, since the required state isn't part of the prerequisite gate's slot id) plus a catch-all returning {:error, :unsupported_scope} for symmetry.

3. Redis dropped a rule gate's enabled flag on round-trip

The Redis serializer encoded only a rule gate's constraint list and hardcoded enabled: true on read — diverging from the Ecto adapter, which persists enabled in its own column. Rule gates now serialize as {"enabled": bool, "constraints": [...]}. decode_rule/1 also reads the legacy bare-array format (as enabled: true), so existing Redis data round-trips unchanged.

Not changed (deliberate, documented behavior)

  • Numeric equality coercion in Constraint ("007" == "7") — documented and tested.
  • :contains is string-substring only — documented.

Verification

  • mix test: 394 passed (81 doctests, 8 properties, 305 tests); 13 redis-tagged excluded (no live Redis — the new rule-serializer tests are pure and ran). Written test-first (red → green).
  • mix format --check-formatted: clean
  • mix credo --strict: no issues
  • mix assay (Dialyzer): 0 errors
  • no compiler warnings

Ch4s3 added 2 commits May 23, 2026 19:31
…/clear scopes

disable/2 and clear/2 had no catch-all clause, so an unsupported scope
(e.g. disable(:f, schedule: ...) or clear(:f, nonsense: true)) raised a
FunctionClauseError surfaced as a telemetry exception rather than a clean
result. Add catch-all clauses returning {:error, :unsupported_scope}, and
accept the requires: {parent, required} tuple form in clear/2 for symmetry
with enable/2 (the required state is not part of the prerequisite gate's
slot id, so it clears the same gate as the bare-atom form).
The Redis serializer encoded only a rule gate's constraint list and
hardcoded enabled: true on read, dropping the enabled flag — diverging
from the Ecto adapter, which persists it in its own column. Encode rule
gates as {"enabled": bool, "constraints": [...]} to match. decode_rule/1
also reads the legacy bare-array format (as enabled: true) so existing
Redis data round-trips unchanged.
@Ch4s3
Ch4s3 merged commit 5586d29 into main May 24, 2026
2 checks passed
@Ch4s3
Ch4s3 deleted the api-fixes branch May 24, 2026 02:38
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