# Pipecircuit visualization
`pipecircuit-visualization` derives reviewable architecture diagrams from a
crate's Rust source. It walks the crate's module tree, finds every explicit
`StateMachine` and `StepInputOf` implementation, follows imports, and expands
each outcome enum into its variants.
No diagram-specific step list is maintained:
```rust
use pipecircuit_visualization::{CrateArchitecture, DiagramTarget};
let architecture = CrateArchitecture::read(env!("CARGO_MANIFEST_DIR"))?;
let players = architecture.state_machine("Players")?;
let snapshot = players.render(DiagramTarget::TextTree);
```
The uncolored text tree is intended for expect tests and redirected output.
`AnsiTextTree` gives its role markers a terminal-safe lime treatment. The same
graph can be projected to `MermaidFlowchart` for Markdown and browser tooling
or `GraphvizDot` for Graphviz, SVG, and image generation.
The separately installable `pipecircuit-cli` package exposes the interactive
surface:
```text
pipec diagram Players
```
It selects ANSI text only for an interactive terminal and honors `NO_COLOR`.
Generation fails when a referenced local type, module, state machine, input,
or outcome cannot be resolved. Procedural macros that generate
`StepInputOf` implementations are not source-visible on stable Rust and are
not currently supported; step implementations themselves must remain ordinary
Rust. This keeps incomplete diagrams from masquerading as complete ones.
## Behavior expectations
Keep a `behavior.rs` snapshot module beside a machine's tests and architecture
expectation. Its inventory discovers the compiled tests automatically:
```rust
use expect_test::expect;
use pipecircuit_visualization::{BehaviorError, BehaviorInventory};
#[test]
fn behavior_remains_reviewable() -> Result<(), BehaviorError> {
expect![[r#"
- Equal sentences retain their distinct code locations
- Listing order does not change the inventory
"#]]
.assert_eq(&BehaviorInventory::for_snapshot(module_path!())?.render());
Ok(())
}
```
Register this dedicated child module with `#[cfg(test)] mod behavior;`. The
inventory includes tests below its parent module and excludes its own module.
Renaming, adding, or removing a check changes the expectation. Review the diff,
then accept it with `UPDATE_EXPECT=1 cargo test behavior_remains_reviewable`.
Discovery asks the current test executable for its list; it neither reruns
the checks nor starts Cargo. Macro-generated tests, including property tests,
are included. Names become alphabetically sorted sentence bullets; duplicate
sentences retain their code locations, and ignored tests carry `[ignored]`.
This is a review aid, not evidence that a named claim passed. The ordinary
verification run supplies that evidence. Discovery covers only the current
libtest target and compiled features: other test targets, doctests, and Kani
proof harnesses are not included. No production dependency or runtime
instrumentation is needed in a consuming application's semantic core; use
this library as a dev-dependency.
Browser-only checks need a host-side snapshot: the browser cannot launch a
test executable or update source files. Supply a configured listing command
to the same inventory instead of reimplementing discovery or rendering:
```rust
use std::process::Command;
let mut runner = Command::new(env!("CARGO"));
runner.current_dir(env!("CARGO_MANIFEST_DIR")).args([
"test", "--target", "wasm32-unknown-unknown", "--lib", "--",
]);
let inventory = BehaviorInventory::for_snapshot_with_runner(
"my_app::wiring::tests::behavior",
runner,
)?;
```
The scope names the module in the listed target. The helper appends the
standard listing flags and queries all checks and ignored checks separately.
WASM discovery lists compiled exports without starting a browser; run the
actual browser suite independently to verify those claims. Keep runner setup
in the host test harness and the expectation in `behavior.rs` beside its
owning browser tests. The existing `UPDATE_EXPECT=1` workflow is unchanged.