Skip to content

Fix status-lifecycle false positives from shared values and negated requires (#18)#34

Merged
yavorpanayotov merged 1 commit into
mainfrom
fix/18-status-lifecycle-false-positives
Jun 11, 2026
Merged

Fix status-lifecycle false positives from shared values and negated requires (#18)#34
yavorpanayotov merged 1 commit into
mainfrom
fix/18-status-lifecycle-false-positives

Conversation

@yavorpanayotov

Copy link
Copy Markdown
Collaborator

Fixes #18.

allium check emitted false allium.status.unreachableValue and allium.status.noExit warnings in two scenarios where the lifecycle was fully specified by rules. Both are fixed in the Rust analyzer and in the TypeScript extension analyzer (which also serves allium-lsp).

Fix 1 — surface parameter types disambiguate shared status values

The checker now builds a map of command name → positional parameter entity types from surface provides: declarations (CancelSubscription(admin, sub: Subscription)) and uses it to type a rule's positional when: bindings (when: CancelSubscription(admin, sub) types sub as Subscription).

Previously binding entity resolution fell back to inferring the entity from the status value, which fails when a value is shared across entities (e.g. two entities both declaring active) — silently dropping the rule's status assignments and transition edges.

  • Rust: collect_command_param_types / augment_binding_types_from_commands
  • TS: collectCommandParamTypes / augmentBindingTypesFromCommands

Fix 2 — negated requires expand to the complement set

requires: x.status != v now records an exit edge for every other value of the entity's status enum (guarded on v belonging to the enum). Previously only = comparisons produced exit edges, so e.g. Order.approved was reported as having no exit even though ReactivateOrder covers it.

  • Rust: new NotEq arm in visit_status_comparisons
  • TS: new negated lane in findStatusStateMachineIssues

Not included

The issue's stretch suggestion (type annotations in rule when: clauses as explicit disambiguation) is a grammar change that overlaps with #24, so it is left out here.

Verification

  • The issue's full repro now produces zero status warnings (only the expected field.unused infos remain)
  • 4 regression tests added (2 Rust, 2 TS) mirroring the repro's two scenarios
  • cargo test --workspace, npm run test, and npm run lint all pass
  • Specs updated: docs/project/specs/allium-extension-behaviour.allium and docs/project/rust-checker-parity.md

🤖 Generated with Claude Code

…equires (#18)

Two fixes to the status state-machine checker, applied to both the Rust
analyzer and the TypeScript extension analyzer:

- Type rule `when:` bindings from the surface `provides:` declaration of
  the command they subscribe to, matched positionally. Previously a status
  value shared by several entities made the binding unresolvable and
  silently dropped the rule's assignments and transition edges, producing
  spurious unreachableValue/noExit warnings.

- Expand `requires: x.status != v` to the complement of the entity's
  status enum so every other value gains an exit edge. Previously only
  `=` comparisons produced exit edges.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@yavorpanayotov yavorpanayotov merged commit 153cc71 into main Jun 11, 2026
2 checks passed
@yavorpanayotov yavorpanayotov deleted the fix/18-status-lifecycle-false-positives branch June 11, 2026 15: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.

Status-lifecycle checker: false warnings from overlapping status values and negated requires

1 participant