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
14 changes: 13 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ Statum targets stable Rust and currently supports Rust `1.93+`.

```toml
[dependencies]
statum = "0.6.3"
statum = "0.6.6"
```

## 60-Second Example
Expand Down Expand Up @@ -139,6 +139,18 @@ that starts minimal and adds features one by one, see
flagship persistence story, see
[docs/case-study-event-log-rebuild.md](docs/case-study-event-log-rebuild.md).

## Machine Introspection

Statum can also emit typed machine introspection directly from the machine
definition itself. Use it when downstream tooling needs the machine structure
without rebuilding a parallel graph table by hand: CLI explainers, generated
docs, graph exports, branch-strip views, test assertions about exact legal
transitions, and replay or debug tooling.

See [docs/introspection.md](docs/introspection.md) for the full guide and
[statum-examples/src/toy_demos/16-machine-introspection.rs](statum-examples/src/toy_demos/16-machine-introspection.rs)
for a runnable example.

## Typed Rehydration

`#[validators]` is the feature that turns stored data back into typed machines. Each `is_*` method checks whether the persisted value belongs to a state, returns `()` or state-specific data, and Statum builds the right typed output:
Expand Down
7 changes: 7 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Release Notes

## v0.6.6 (2026-03-21)

### Changes
- Added first-class machine introspection emitted directly from `#[machine]`, including typed `StateId`, `TransitionId`, and a static `GRAPH` descriptor for exact transition-site queries.
- Added typed runtime transition recording so consumer crates can record the chosen branch and join it back to static machine metadata.
- Added an optional typed presentation overlay for attaching labels, descriptions, phases, and other consumer-owned metadata without rebuilding the machine graph.

## v0.6.1 (2026-03-18)

### Changes
Expand Down
4 changes: 4 additions & 0 deletions docs/agents/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ dynamic.
- parent, child, or nested-machine structure when one workflow owns another
- likely `#[transition]` impl blocks and the legal edges they encode
- whether `#[validators]` or `statum::projection` should be part of the design
- whether downstream tooling should use Statum's emitted introspection instead
of a handwritten graph table
- the explicit hybrid boundary: what should stay runtime-validated and why
- the smallest first migration slice that improves correctness without forcing a
repo-wide rewrite
Expand All @@ -70,9 +72,11 @@ The templates are intentionally short. Point agents back to the canonical docs
when they need detail:

- [../../README.md](../../README.md)
- [../introspection.md](../introspection.md)
- [../typestate-builder-design-playbook.md](../typestate-builder-design-playbook.md)
- [../patterns.md](../patterns.md)
- [../persistence-and-validators.md](../persistence-and-validators.md)
- [../../statum-examples/src/toy_demos/16-machine-introspection.rs](../../statum-examples/src/toy_demos/16-machine-introspection.rs)
- [../../statum-examples/src/toy_demos/13-review-flow.rs](../../statum-examples/src/toy_demos/13-review-flow.rs)
- [../../statum-examples/src/showcases/sqlite_event_log_rebuild.rs](../../statum-examples/src/showcases/sqlite_event_log_rebuild.rs)
- [../../statum-examples/src/showcases/tokio_websocket_session.rs](../../statum-examples/src/showcases/tokio_websocket_session.rs)
3 changes: 3 additions & 0 deletions docs/agents/prompts/abstract-guide-to-statum.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ Tasks:
- the legal `#[transition]` impl blocks
- any parent, child, or nested machine relationships
- whether `#[validators]` or `statum::projection` belongs at a persistence boundary
- whether downstream tooling should use Statum introspection instead of a
handwritten graph table
3. Call out what should explicitly stay outside Statum and why.
4. Keep the first implementation slice small enough for one focused PR.
5. If a claim is underspecified, name the missing protocol or evidence question instead of guessing.
Expand All @@ -42,6 +44,7 @@ Be concrete. Do not stop at "consider typestate."

Use these references if needed:
- https://github.com/eboody/statum/blob/main/README.md
- https://github.com/eboody/statum/blob/main/docs/introspection.md
- https://github.com/eboody/statum/blob/main/docs/typestate-builder-design-playbook.md
- https://github.com/eboody/statum/blob/main/docs/patterns.md
- https://github.com/eboody/statum/blob/main/docs/persistence-and-validators.md
Expand Down
3 changes: 3 additions & 0 deletions docs/agents/prompts/greenfield-workflow-design.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ Tasks:
- the state-specific data that should not live on the machine root
- the likely `#[transition]` impl blocks
- whether `#[validators]` or `statum::projection` is needed
- whether downstream docs, CLI explainers, replay tooling, graph exports, or
transition assertions should use Statum introspection
3. If it is a poor fit, say what should stay runtime-validated and why.
4. Keep the first implementation slice small enough for one PR.

Expand All @@ -29,6 +31,7 @@ workflows.

Use these references if needed:
- https://github.com/eboody/statum/blob/main/README.md
- https://github.com/eboody/statum/blob/main/docs/introspection.md
- https://github.com/eboody/statum/blob/main/docs/typestate-builder-design-playbook.md
- https://github.com/eboody/statum/blob/main/docs/patterns.md
- https://github.com/eboody/statum/blob/main/docs/persistence-and-validators.md
Expand Down
3 changes: 3 additions & 0 deletions docs/agents/prompts/pr-review-typestate-check.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,16 @@ If you choose "Follow-up candidate" or "Refactor now", include:
- the likely `#[machine]` fields and state data
- the first `#[transition]` blocks worth adding
- whether `#[validators]` or `statum::projection` should be involved
- whether downstream tooling should use Statum introspection instead of a
handwritten graph table
- why the change would be safer or clearer with Statum

Do not manufacture a typestate refactor if the new logic is still too dynamic or
too early.

Use these references if needed:
- https://github.com/eboody/statum/blob/main/README.md
- https://github.com/eboody/statum/blob/main/docs/introspection.md
- https://github.com/eboody/statum/blob/main/docs/typestate-builder-design-playbook.md
- https://github.com/eboody/statum/blob/main/docs/patterns.md
- https://github.com/eboody/statum/blob/main/docs/persistence-and-validators.md
Expand Down
3 changes: 3 additions & 0 deletions docs/agents/prompts/targeted-module-refactor.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ If Statum is a good fit, return:
- the state-specific data
- the legal transitions and likely `#[transition]` blocks
- whether persisted rebuilds require `#[validators]` or `statum::projection`
- whether downstream tooling should use Statum introspection instead of a
handwritten graph table
- the smallest safe migration slice
- the tests that should move or be added

Expand All @@ -29,6 +31,7 @@ Do not propose a full rewrite when a narrow first slice would prove the design.

Use these references if needed:
- https://github.com/eboody/statum/blob/main/README.md
- https://github.com/eboody/statum/blob/main/docs/introspection.md
- https://github.com/eboody/statum/blob/main/docs/typestate-builder-design-playbook.md
- https://github.com/eboody/statum/blob/main/docs/patterns.md
- https://github.com/eboody/statum/blob/main/docs/persistence-and-validators.md
Expand Down
3 changes: 3 additions & 0 deletions docs/agents/templates/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ staged workflows or protocol-heavy APIs.
- `#[transition]` for legal edges
- `#[validators]` and `statum::projection` if rebuilds exist
- distinguish machine fields from state-specific data
- if docs, CLI explainers, replay tooling, or graph exports need machine
structure, prefer Statum introspection over a handwritten graph table
- explain why plain runtime validation is weaker in this spot
- keep the first migration slice small and testable

Expand All @@ -37,6 +39,7 @@ staged workflows or protocol-heavy APIs.
rebuild paths
- read these references before proposing a non-trivial refactor:
- <https://github.com/eboody/statum/blob/main/README.md>
- <https://github.com/eboody/statum/blob/main/docs/introspection.md>
- <https://github.com/eboody/statum/blob/main/docs/typestate-builder-design-playbook.md>
- <https://github.com/eboody/statum/blob/main/docs/patterns.md>
- <https://github.com/eboody/statum/blob/main/docs/persistence-and-validators.md>
Expand Down
3 changes: 3 additions & 0 deletions docs/agents/templates/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,15 @@ real staged workflow or protocol whose legal edges should be encoded in types.
- separate durable machine context from state-only payloads
- list the likely `#[transition]` blocks
- decide whether `#[validators]` or `statum::projection` is part of the design
- if downstream tooling needs machine structure, prefer Statum introspection
over a parallel handwritten graph table
- cite exact files or symbols that justify the recommendation
- propose a narrow first migration slice

## Read Before Refactoring

- <https://github.com/eboody/statum/blob/main/README.md>
- <https://github.com/eboody/statum/blob/main/docs/introspection.md>
- <https://github.com/eboody/statum/blob/main/docs/typestate-builder-design-playbook.md>
- <https://github.com/eboody/statum/blob/main/docs/patterns.md>
- <https://github.com/eboody/statum/blob/main/docs/persistence-and-validators.md>
3 changes: 3 additions & 0 deletions docs/agents/templates/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,14 @@ fit for lifecycle-heavy or protocol-heavy code.
- map the design to `#[state]`, `#[machine]`, `#[transition]`, and, if needed,
`#[validators]`
- explain the split between machine context and state data
- if docs, CLI explainers, replay tooling, or graph exports need machine
structure, prefer Statum introspection over a parallel graph table
- keep the first change small enough to review and test

## References

- <https://github.com/eboody/statum/blob/main/README.md>
- <https://github.com/eboody/statum/blob/main/docs/introspection.md>
- <https://github.com/eboody/statum/blob/main/docs/typestate-builder-design-playbook.md>
- <https://github.com/eboody/statum/blob/main/docs/patterns.md>
- <https://github.com/eboody/statum/blob/main/docs/persistence-and-validators.md>
3 changes: 3 additions & 0 deletions docs/agents/templates/cursor-statum.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,15 @@ If you propose Statum:
- cite the files and symbols that show the lifecycle
- sketch `#[state]`, `#[machine]`, and `#[transition]`
- decide whether `#[validators]` or `statum::projection` should be used
- if downstream tooling needs machine structure, prefer Statum introspection
over a parallel handwritten graph table
- explain why runtime validation is weaker here
- keep the first migration slice small

References:

- <https://github.com/eboody/statum/blob/main/README.md>
- <https://github.com/eboody/statum/blob/main/docs/introspection.md>
- <https://github.com/eboody/statum/blob/main/docs/typestate-builder-design-playbook.md>
- <https://github.com/eboody/statum/blob/main/docs/patterns.md>
- <https://github.com/eboody/statum/blob/main/docs/persistence-and-validators.md>
158 changes: 158 additions & 0 deletions docs/introspection.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
# Machine Introspection

Statum can emit typed machine introspection directly from the machine
definition itself.

Use it when the machine definition should also drive downstream tooling:

- CLI explainers
- generated docs
- graph exports
- branch-strip views
- test assertions about exact legal transitions
- replay or debug tooling that joins runtime events back to the static graph

The important distinction is precision. Statum does not only expose a
machine-wide list of states. It exposes exact transition sites:

- source state
- transition method
- exact legal target states from that site

That means a branching transition like `Flow<Fetched>::validate() ->
Accepted | Rejected` can be rendered without maintaining a parallel handwritten
graph table.

## Static Graph Access

Use `MachineIntrospection` to get the generated graph:

```rust
use statum::{machine, state, transition, MachineIntrospection};

#[state]
enum FlowState {
Fetched,
Accepted,
Rejected,
}

#[machine]
struct Flow<FlowState> {}

#[transition]
impl Flow<Fetched> {
fn validate(self, accept: bool) -> Result<Flow<Accepted>, Flow<Rejected>> {
if accept {
Ok(self.accept())
} else {
Err(self.reject())
}
}

fn accept(self) -> Flow<Accepted> {
self.transition()
}

fn reject(self) -> Flow<Rejected> {
self.transition()
}
}

let graph = <Flow<Fetched> as MachineIntrospection>::GRAPH;
let validate = graph
.transition_from_method(flow::StateId::Fetched, "validate")
.unwrap();

assert_eq!(
graph.legal_targets(validate.id).unwrap(),
&[flow::StateId::Accepted, flow::StateId::Rejected]
);
```

From there, a consumer can ask for:

- transitions from a state
- a transition by id
- a transition by source state and method name
- the exact legal targets for a transition site

## Transition Identity

State ids are generated as a machine-scoped enum like `flow::StateId`.

Transition ids are typed and exact, but they are exposed as generated
associated consts on the source-state machine type, such as
`Flow::<Fetched>::VALIDATE`.

That keeps transition identity tied to the exact source-state plus method site,
including cfg-pruned and macro-generated transitions.

## Runtime Join Support

If you want replay or debug tooling, record the transition that actually
happened at runtime and join it back to the static graph:

```rust
use statum::{
machine, state, transition, MachineTransitionRecorder,
};

#[state]
enum FlowState {
Fetched,
Accepted,
Rejected,
}

#[machine]
struct Flow<FlowState> {}

#[transition]
impl Flow<Fetched> {
fn validate(self, accept: bool) -> Result<Flow<Accepted>, Flow<Rejected>> {
if accept {
Ok(self.accept())
} else {
Err(self.reject())
}
}

fn accept(self) -> Flow<Accepted> {
self.transition()
}

fn reject(self) -> Flow<Rejected> {
self.transition()
}
}

let event = <Flow<Fetched> as MachineTransitionRecorder>::try_record_transition_to::<
Flow<Accepted>,
>(Flow::<Fetched>::VALIDATE)
.unwrap();

assert_eq!(event.chosen, flow::StateId::Accepted);
```

Once you have both:

- static graph metadata
- runtime-taken transition records

you can render the chosen branch and the non-chosen legal branches from the
same source of truth.

## Presentation Metadata

Structural introspection is separate from human-facing metadata.

If a consumer crate wants labels, descriptions, or phases for rendering, it can
add a typed `MachinePresentation` overlay keyed by the generated ids. That lets
the machine definition remain the source of truth for structure while the
consumer owns local explanation and presentation.

## Example

Runnable example:
[statum-examples/src/toy_demos/16-machine-introspection.rs](../statum-examples/src/toy_demos/16-machine-introspection.rs)
Loading
Loading