Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 0 additions & 47 deletions .github/workflows/lint-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,50 +35,3 @@ jobs:
# use-quiet-mode: 'yes'
# use-verbose-mode: 'yes'
# config-file: '.github/workflows/markdown-link-check-config.json'

validate-memory:
name: Validate Memory Files
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Check CLAUDE.md structure
run: |
echo "Validating CLAUDE.md structure..."
if ! grep -q "## Commands" CLAUDE.md; then
echo "❌ CLAUDE.md missing Commands section"
exit 1
fi
if ! grep -q "## Architecture" CLAUDE.md; then
echo "❌ CLAUDE.md missing Architecture section"
exit 1
fi
echo "✅ CLAUDE.md structure looks good"

- name: Check README.md structure
run: |
echo "Validating README.md structure..."
if ! grep -q "## Features" README.md; then
echo "❌ README.md missing Features section"
exit 1
fi
if ! grep -q "## Current Status" README.md; then
echo "❌ README.md missing Current Status section"
exit 1
fi
echo "✅ README.md structure looks good"

- name: Validate documentation consistency
run: |
echo "Checking for API consistency between CLAUDE.md and README.md..."

# Check that both files reference Statifier.Validator (not Statifier.Document.Validator)
if grep -q "Statifier\.Document\.Validator" CLAUDE.md README.md; then
echo "❌ Found outdated Statifier.Document.Validator references - should be Statifier.Validator"
grep -n "Statifier\.Document\.Validator" CLAUDE.md README.md || true
exit 1
fi

echo "✅ API references look consistent"
117 changes: 22 additions & 95 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,84 +10,26 @@

An Elixir implementation of SCXML (State Chart XML) state charts with a focus on W3C compliance.

## Features

- ✅ **Complete SCXML Parser** - Converts XML documents to structured data with precise location tracking
- ✅ **State Chart Interpreter** - Runtime engine for executing SCXML state charts
- ✅ **Secure Invoke System** - Handler-based external service integration with SCXML compliance
- ✅ **Modular Validation** - Document validation with focused sub-validators for maintainability
- ✅ **Compound States** - Support for hierarchical states with automatic initial child entry
- ✅ **Initial State Elements** - Full support for `<initial>` elements with transitions (W3C compliant)
- ✅ **Parallel States** - Support for concurrent state regions with simultaneous execution
- ✅ **Eventless Transitions** - Automatic transitions without event attributes (W3C compliant)
- ✅ **Conditional Transitions** - Full support for `cond` attributes with expression evaluation
- ✅ **Executable Content** - Complete support for `<assign>`, `<log>`, `<raise>`, `<if>`, `<invoke>` elements
- ✅ **Parameter Processing** - Unified parameter evaluation for `<send>` and `<invoke>` elements
- ✅ **Value Evaluation** - Non-boolean expression evaluation using Predicator v3.0 for actual data values
- ✅ **Data Model Integration** - StateChart data model with dynamic variable assignment and persistence
- ✅ **O(1) Performance** - Optimized state and transition lookups via Maps
- ✅ **Event Processing** - Internal and external event queues per SCXML specification
- ✅ **Parse → Validate → Optimize Architecture** - Clean separation of concerns
- ✅ **Feature Detection** - Automatic SCXML feature detection for test validation
- ✅ **Regression Testing** - Automated tracking of passing tests to prevent regressions
- ✅ **Git Hooks** - Pre-push validation workflow to catch issues early
- ✅ **Logging Infrastructure** - Protocol-based logging system with TestAdapter for clean test environments
- ✅ **Test Infrastructure** - Compatible with SCION and W3C test suites with integrated logging
- ✅ **Code Quality** - Full Credo compliance with proper module aliasing
- ✅ **History States** - Complete shallow and deep history state support per W3C SCXML specification
- ✅ **Multiple Transition Targets** - Support for space-separated multiple targets in transitions

## Current Status

### Working Features

- ✅ **Basic state transitions** and event-driven changes
- ✅ **Hierarchical states** with optimized O(1) state lookup and automatic initial child entry
- ✅ **Initial state elements** - Full `<initial>` element support with transitions and comprehensive validation
- ✅ **Parallel states** with concurrent execution of multiple regions and proper cross-boundary exit semantics
- ✅ **Eventless transitions** - Automatic transitions without event attributes (also called NULL transitions in SCXML spec), with cycle detection and microstep processing
- ✅ **Conditional transitions** - Full `cond` attribute support with Predicator v3.0 expression evaluation and SCXML `In()` function
- ✅ **Assign elements** - Complete `<assign>` element support with location-based assignment, nested property access, and mixed notation
- ✅ **If/Else/ElseIf conditional actions** - Complete `<if>`, `<elseif>`, `<else>` conditional execution blocks
- ✅ **Invoke elements** - Secure `<invoke>` element support with handler-based external service integration and SCXML-compliant event generation
- ✅ **Parameter processing** - Unified `<param>` element evaluation with strict/lenient error handling modes
- ✅ **Value evaluation system** - Enhanced Evaluator module with centralized parameter processing and non-boolean expression evaluation
- ✅ **Enhanced expression evaluation** - Predicator v3.0 integration with deep property access and type-safe operations
- ✅ **History states** - Complete shallow and deep history state implementation with recording, restoration, and validation
- ✅ **Multiple transition targets** - Support for space-separated multiple targets (e.g., `target="state1 state2"`)
- ✅ **Enhanced parallel state exit logic** - Proper W3C SCXML exit set computation for complex parallel hierarchies
- ✅ **Transition conflict resolution** - Child state transitions take priority over ancestor transitions per W3C specification
- ✅ **SCXML-compliant processing** - Proper microstep/macrostep execution model with exit set computation and LCCA algorithms
- ✅ **Modular validation** - Refactored from 386-line monolith into focused sub-validators
- ✅ **Feature detection** - Automatic SCXML feature detection prevents false positive test results
- ✅ **SAX-based XML parsing** with accurate location tracking for error reporting
- ✅ **Performance optimizations** - O(1) state/transition lookups, optimized active configuration
- ✅ **Source field optimization** - Transitions include source state for faster event processing
- ✅ **Comprehensive logging** - Protocol-based logging system with structured metadata and test environment integration

### Planned Features

- Internal and targetless transitions
- More executable content (`<script>`, `<send>`, etc.)
- Enhanced datamodel support with JavaScript expression engine
- Enhanced validation for complex SCXML constructs
## Installation

## Documentation
Add `statifier` to your list of dependencies in `mix.exs`:

For comprehensive guides, examples, and technical details, see the [Statifier Documentation](https://riddler.github.io/statifier):
```elixir
def deps do
[
{:statifier, "~> 1.8"}
]
end
```

- **[Getting Started](https://riddler.github.io/statifier/getting-started)** - Build your first state machine with working examples
- **[External Services](https://riddler.github.io/statifier/external-services)** - Secure integration with APIs and external systems
- **[Changelog](https://riddler.github.io/statifier/changelog)** - Recent major feature completions
- **[Roadmap](https://riddler.github.io/statifier/roadmap)** - Planned features and priorities
For comprehensive examples and usage patterns, see the [Getting Started Guide](https://riddler.github.io/statifier/getting-started).

## Quick Example

```elixir
# Simple traffic light state machine
xml = """
<?xml version="1.0" encoding="UTF-8"?>
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" initial="red">
<scxml initial="red">
<state id="red">
<transition event="timer" target="green"/>
</state>
Expand All @@ -104,46 +46,31 @@ xml = """
{:ok, document, _warnings} = Statifier.parse(xml)
{:ok, state_chart} = Statifier.initialize(document)

# Check active state
Statifier.active_leaf_states(state_chart)
# MapSet.new(["red"])

# Send events to transition between states
{:ok, state_chart} = Statifier.send_sync(state_chart, "timer")
```

## Installation

Add `statifier` to your list of dependencies in `mix.exs`:

```elixir
def deps do
[
{:statifier, "~> 1.8"}
]
end
# Check active state
Statifier.active_leaf_states(state_chart)
# MapSet.new(["green"])
```

For comprehensive examples and usage patterns, see the [Getting Started Guide](https://riddler.github.io/statifier/getting-started).
## Documentation

## Learn More
For comprehensive guides, examples, and technical details, see the [Statifier Documentation](https://riddler.github.io/statifier):

- **[Getting Started](https://riddler.github.io/statifier/getting-started)** - Build your first state machine with working examples
- **[External Services](https://riddler.github.io/statifier/external-services)** - Integrate safely with APIs and external systems
- **[External Services](https://riddler.github.io/statifier/external-services)** - Secure integration with APIs and external systems
- **[Installation Guide](https://riddler.github.io/statifier/installation)** - Set up Statifier in your project

## Project Information

- **[Architecture](https://riddler.github.io/statifier/architecture)** - Technical design and component overview
- **[Changelog](https://riddler.github.io/statifier/changelog)** - Recent major feature completions and implementation details
- **[Roadmap](https://riddler.github.io/statifier/roadmap)** - Planned features and development priorities
- **[Roadmap](https://riddler.github.io/statifier/roadmap)** - Planned features and priorities

## Resources

- **[GitHub Repository](https://github.com/riddler/statifier)** - Source code and issues
- **[Hex Package](https://hex.pm/packages/statifier)** - Package information
- **[HexDocs API](https://hexdocs.pm/statifier/)** - Complete API documentation

---

::: info Active Development Notice
This project is under active development. While stable for production use, APIs may evolve as we continue improving SCXML compliance and functionality.
:::

For comprehensive technical details about the implementation, see the [Architecture Documentation](https://riddler.github.io/statifier/architecture).
38 changes: 37 additions & 1 deletion lib/statifier.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ defmodule Statifier do
and optimization, including relaxed parsing mode for simplified tests.
"""

alias Statifier.{Event, Interpreter, Parser.SCXML, StateMachine, Validator}
alias Statifier.{Configuration, Event, Interpreter, Parser.SCXML, StateMachine, Validator}

@doc """
Parse and validate an SCXML document in one step.
Expand Down Expand Up @@ -123,6 +123,42 @@ defmodule Statifier do
Interpreter.send_event(state_chart, event)
end

@doc """
Get the active leaf states from a StateChart.

Returns a MapSet containing the IDs of all currently active leaf states.
Leaf states are atomic states that have no child states. This is the
most common way to check which states are currently active in a state chart.

For hierarchical state charts, this only returns the deepest active states
(leaf nodes), not their parent states. Use `Configuration.active_ancestors/2`
if you need to include parent states in the hierarchy.

## Examples

{:ok, state_chart} = Statifier.initialize(document)
active_states = Statifier.active_leaf_states(state_chart)
# Returns: #MapSet<["initial_state"]>

{:ok, new_state_chart} = Statifier.send_sync(state_chart, "transition_event")
active_states = Statifier.active_leaf_states(new_state_chart)
# Returns: #MapSet<["target_state"]>

# For parallel states, multiple states can be active simultaneously
active_states = Statifier.active_leaf_states(parallel_state_chart)
# Returns: #MapSet<["region1_state", "region2_state"]>

## See Also

- `Statifier.Configuration.active_leaf_states/1` - The underlying implementation
- `Statifier.Configuration.active_ancestors/2` - Get all active states including ancestors

"""
@spec active_leaf_states(Statifier.StateChart.t()) :: MapSet.t(String.t())
def active_leaf_states(%Statifier.StateChart{configuration: config}) do
Configuration.active_leaf_states(config)
end

# Private helper to reduce nesting depth
defp handle_validation(document, strict?) do
case Validator.validate(document) do
Expand Down