pipecircuit-visualization 0.0.8

Code-derived architectural visualizations for Pipecircuit programs
Documentation

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:

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:

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:

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. Mount the snapshot under the same relative module hierarchy in the host harness:

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(
    module_path!(),
    runner,
)?;

For example, a browser snapshot at wiring::tests::behavior must remain a behavior child of wiring::tests in the host harness, not be flattened into a differently named module. The crate-name prefix may differ; the relative module path determines the scope. No handwritten qualified scope is needed.

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.

Compiled program and installation views

Experimental development scaffolding — not ready for application consumption. These rudimentary views support framework review. Their APIs and presentation need further design; expansion is deferred while authoring and performance work progress. This status applies to program/installation inspection, not the existing state-machine architecture and behavior tooling above.

pipe_roster! derives DescribeRoster from the named pipe declarations. Roster-bound MioPipeSet records derive DescribeBindings from the actual installed fields. Inspection therefore needs no parallel inventory:

let program = ProgramDiagram::<program::Schema>::for_board::<NpcConversationProgram>();
let semantic = program.render(DiagramTarget::TextTree);
let physical = program.installed(&pipes).render(DiagramTarget::MermaidFlowchart);

The board must use the selected roster, and the bindings must name that same roster. Text, Mermaid and Graphviz include pipe families, carrier types and package/file/line/column locations. Rendered type names abbreviate module paths; pipes() and bindings() preserve qualified diagnostic labels. Compiler type names are not stable protocol identifiers or wire revisions. This renderer currently emits plain text for both text targets.

These are declared boundary views, not inferred route or reachability graphs. Switchboard decisions, remote placement, compatibility and transport health cannot be deduced from Rust type names. Dashed graph edges identify the In/Out families and installation relation only.

The underlying metadata iterators and Mio ready_sources().statuses() borrow existing structures or return fixed-size values, without allocation. Source statuses expose retained activation hints and ready/paused state, in storage order; inspecting them neither dispatches nor resumes work. An absent source is not evidence of a healthy pipe. The source table's capacity bounds this observation, not the occupancy of every transport queue.

ProgramDiagram and its renderers allocate in this optional std tooling crate. Use them in development tools or examples, outside measured steady-state work. Keep pipecircuit-visualization a dev-dependency when semantic libraries must remain no_std.

For a directly usable browser artifact, call render_html() on either view and write the returned string to an HTML file. The standalone page works offline, uses native disclosure controls for exact types and declaration locations, and can hide the physical layer while preserving the logical pipes. It requires no JavaScript, Mermaid installation or web service. Text, HTML and graph views name message directions from each pipe's perspective: emits to the switchboard (Out), receives from the switchboard (In).

Ordinary Rust documentation on roster roles and installed fields travels with the compiled metadata. Write the role's purpose beside its declaration and describe physical choices beside the installed field. These descriptions explain the structure; they are not proofs of behavior. HTML treats them as plain escaped text, including documentation containing markup. Optional tooling allocates to render them; metadata iteration still uses static strings and fixed arrays.