pyfcstm.diagnostics.inspect

Structured model inspection for pyfcstm.

This module provides inspect_model(), a single entry point that walks a pyfcstm.model.StateMachine and produces a stable, serialization-friendly view of its structure plus five derived relational graphs (reachability, event emission, variable data flow, aspect impact, action reference). The output is the foundation that Layer 2 design-health warnings (W_* / I_* codes) and downstream LLM / evaluation tooling consume.

The view shape is the single source of truth for the pyfcstm / jsfcstm contract. Adding or renaming a field here must be mirrored on the jsfcstm side (editors/jsfcstm/src/diagnostics/inspect.ts) and in pyfcstm/diagnostics/schema.json.

The module exposes the following dataclasses:

  • StateInfo — per-state structural summary

  • TransitionInfo — per-transition structural summary

  • VariableInfo — per-variable structural summary plus participation flags used by W_UNREFERENCED_VAR

  • EventInfo — per-event structural summary

  • ModelMetrics — aggregate counts and ratios

  • ModelInspect — top-level container including diagnostics

Example:

>>> from pyfcstm.dsl import parse_with_grammar_entry
>>> from pyfcstm.model.parse import parse_dsl_node_to_state_machine
>>> from pyfcstm.diagnostics import inspect_model
>>> source = '''
... def int counter = 0;
... state Root {
...     state Idle;
...     state Active;
...     [*] -> Idle;
...     Idle -> Active : if [counter > 0];
... }
... '''
>>> ast = parse_with_grammar_entry(source, 'state_machine_dsl')
>>> machine = parse_dsl_node_to_state_machine(ast)
>>> report = inspect_model(machine)
>>> report.metrics.n_states_leaf
2

StateInfo

class pyfcstm.diagnostics.inspect.StateInfo(path: str, name: str, parent_path: str | None, is_leaf: bool, is_pseudo: bool, is_composite: bool, substates: Tuple[str, ...], initial_targets: Tuple[Dict[str, Any], ...], entry_actions: Tuple[str, ...], during_actions: Tuple[str, ...], exit_actions: Tuple[str, ...], aspect_before: Tuple[str, ...], aspect_after: Tuple[str, ...], has_abstract_action: bool)[source]

Structural summary of a single state.

Parameters:
  • path (str) – Dotted hierarchical path, e.g. 'Root.SubSystem.Active'.

  • name (str) – Short name of the state (last component of path).

  • parent_path (Optional[str]) – Dotted path of the parent state, or None for the root state.

  • is_leaf (bool) – True when this state has no substates.

  • is_pseudo (bool) – True when the state was declared with pseudo state.

  • is_composite (bool) – True when this state has substates.

  • substates (Tuple[str, ...]) – Direct-child state paths, in source order.

  • initial_targets (Tuple[Mapping[str, Any], ...]) – Each item describes one [*] -> X initial transition declared inside this composite. target is the target child path, guard is the source text of the guard or None, event is the qualified event name or None, is_unconditional is True only when both guard and event are absent.

  • entry_actions (Tuple[str, ...]) – Action labels (function name or '<inline>') for enter actions on this state, in source order.

  • during_actions (Tuple[str, ...]) – Action labels for during actions.

  • exit_actions (Tuple[str, ...]) – Action labels for exit actions.

  • aspect_before (Tuple[str, ...]) – Aspect-action labels for >> during before.

  • aspect_after (Tuple[str, ...]) – Aspect-action labels for >> during after.

  • has_abstract_action (bool) – True if any of the actions above is abstract. Used by VariableInfo confidence judgements.

TransitionInfo

class pyfcstm.diagnostics.inspect.TransitionInfo(from_path: str, to_path: str, event: str | None, event_scope: str | None, guard: str | None, effect: str | None, is_forced: bool, forced_origin: str | None)[source]

Structural summary of a single transition.

Parameters:
  • from_path (str) – Dotted path of the source state, or the literal '[*]' for an initial transition declared at the root.

  • to_path (str) – Dotted path of the target state, or '[*]' for an exit transition.

  • event (Optional[str]) – Qualified event name (e.g. 'Root.SubA.E') or None if the transition has no event.

  • event_scope (Optional[str]) – 'local', 'chain', 'absolute', or None when there is no event.

  • guard (Optional[str]) – Source text of the guard expression, or None.

  • effect (Optional[str]) – Source text of the effect block, or None.

  • is_forced (bool) – True when the transition was expanded from a !-prefixed forced transition.

  • forced_origin (Optional[str]) – Raw source text of the original !X -> Y declaration when is_forced is True, otherwise None.

VariableInfo

class pyfcstm.diagnostics.inspect.VariableInfo(name: str, type: str, init_value: str, read_in_states: Tuple[str, ...], written_in_states: Tuple[str, ...], read_in_guards: Tuple[Tuple[str, str], ...], written_in_effects: Tuple[Tuple[str, str], ...], participates_directly: bool, participates_indirectly: bool, abstract_actions_in_scope: Tuple[str, ...])[source]

Structural summary of a variable definition plus participation flags.

The participates_directly and participates_indirectly flags are precomputed here so that PR-C’s W_UNREFERENCED_VAR / I_UNREFERENCED_VAR_MAYBE_ABSTRACT rules can be expressed as a one-line filter against this object.

Parameters:
  • name (str) – Variable identifier.

  • type (str) – Declared type, currently 'int' or 'float'.

  • init_value (str) – Source text of the initializer expression.

  • read_in_states (Tuple[str, ...]) – State paths where the variable is read inside any action (enter / during / exit / aspect).

  • written_in_states (Tuple[str, ...]) – State paths where the variable is written inside any action.

  • read_in_guards (Tuple[Tuple[str, str], ...]) – Tuples (from_path, to_path) of transitions whose guard reads this variable.

  • written_in_effects (Tuple[Tuple[str, str], ...]) – Tuples (from_path, to_path) of transitions whose effect block writes this variable.

  • participates_directly (bool) – True when the variable is read by at least one guard, transition effect, or action operation.

  • participates_indirectly (bool) – True when the variable is not directly referenced but is transitively reachable through write-then-read data dependency across blocks. PR-A keeps this field as False for variables that lack any read; PR-C replaces it with the closure-based computation.

  • abstract_actions_in_scope (Tuple[str, ...]) – Function names of abstract actions whose enclosing state is on the ancestor chain or sub-tree of any state that touches this variable. Used by PR-C to split W_UNREFERENCED_VAR (high confidence) from I_UNREFERENCED_VAR_MAYBE_ABSTRACT (low confidence).

EventInfo

class pyfcstm.diagnostics.inspect.EventInfo(qualified_name: str, scope: str, used_by: Tuple[Tuple[str, str], ...])[source]

Structural summary of an event declaration.

Parameters:
  • qualified_name (str) – Dotted fully qualified event name (e.g. 'Root.SubA.E').

  • scope (str) – 'local', 'chain', or 'absolute'.

  • used_by (Tuple[Tuple[str, str], ...]) – (from_path, to_path) tuples for every transition that references this event.

ModelMetrics

class pyfcstm.diagnostics.inspect.ModelMetrics(n_states_leaf: int, n_states_composite: int, n_states_pseudo: int, max_hierarchy_depth: int, n_transitions_normal: int, n_transitions_forced: int, n_events: int, n_variables: int, var_to_leaf_ratio: float, aspect_coverage: Dict[str, int], abstract_action_inventory: Tuple[str, ...])[source]

Aggregate model metrics.

Parameters:
  • n_states_leaf (int) – Number of leaf states excluding pseudo states.

  • n_states_composite (int) – Number of composite states.

  • n_states_pseudo (int) – Number of pseudo states.

  • max_hierarchy_depth (int) – Maximum depth of state nesting, counted from the root (depth 0 = root).

  • n_transitions_normal (int) – Number of transitions that did not originate from a !-forced declaration.

  • n_transitions_forced (int) – Number of transitions expanded from !-forced declarations.

  • n_events (int) – Number of distinct qualified events used in transitions.

  • n_variables (int) – Number of variable definitions.

  • var_to_leaf_ratio (float) – n_variables / max(n_states_leaf, 1).

  • aspect_coverage (Dict[str, int]) – Mapping composite_path -> n_descendant_leaves for composite states that declare >> during aspects.

  • abstract_action_inventory (Tuple[str, ...]) – Function names of every abstract action across the model, sorted for stable output.

ModelInspect

class pyfcstm.diagnostics.inspect.ModelInspect(root_state_path: str, states: ~typing.Tuple[~pyfcstm.diagnostics.inspect.StateInfo, ...], transitions: ~typing.Tuple[~pyfcstm.diagnostics.inspect.TransitionInfo, ...], variables: ~typing.Tuple[~pyfcstm.diagnostics.inspect.VariableInfo, ...], events: ~typing.Tuple[~pyfcstm.diagnostics.inspect.EventInfo, ...], metrics: ~pyfcstm.diagnostics.inspect.ModelMetrics, reachability_graph: ~typing.Dict[str, ~typing.Tuple[str, ...]], event_emission_map: ~typing.Dict[str, ~typing.Tuple[str, ...]], var_dataflow: ~typing.Dict[str, ~typing.Dict[str, ~typing.Tuple[str, ...]]], aspect_impact_map: ~typing.Dict[str, ~typing.Tuple[str, ...]], action_ref_graph: ~typing.Dict[str, ~typing.Tuple[str, ...]], diagnostics: ~typing.Tuple[~pyfcstm.utils.validate.ModelDiagnostic, ...] = <factory>)[source]

Top-level structured view of a state machine model.

Parameters:
  • root_state_path (str) – Dotted path of the root state.

  • states (Tuple[StateInfo, ...]) – All states walked from the root in pre-order.

  • transitions (Tuple[TransitionInfo, ...]) – All transitions, including expanded forced transitions, in source order.

  • variables (Tuple[VariableInfo, ...]) – All def variables, in declaration order.

  • events (Tuple[EventInfo, ...]) – All qualified events that appear in at least one transition, sorted by qualified name.

  • metrics (ModelMetrics) – Aggregate model metrics.

  • reachability_graph (Dict[str, Tuple[str, ...]]) – Mapping state path → list of state paths reachable through normal transitions (BFS closure, ignoring guards).

  • event_emission_map (Dict[str, Tuple[str, ...]]) – Mapping event qualified name → list of source state paths that can emit it.

  • var_dataflow (Dict[str, Dict[str, Tuple[str, ...]]]) – Mapping variable name → {'reads': [...], 'writes': [...]} of state paths.

  • aspect_impact_map (Dict[str, Tuple[str, ...]]) – Mapping composite path → descendant leaf paths actually reached by its aspect actions.

  • action_ref_graph (Dict[str, Tuple[str, ...]]) – Mapping named-action function path → list of ref edges out of it.

  • diagnostics (Tuple[ModelDiagnostic, ...]) – Layer 1 E_* + Layer 2 W_* / I_* diagnostics. PR-A returns an empty tuple; PR-B / PR-C populate it.

to_json() Dict[str, Any][source]

Serialize this inspection report to a plain JSON-friendly dict.

Tuples are converted to lists; frozen dataclasses to dicts. ModelDiagnostic instances are serialized via their public attributes (code, severity, message, span, refs).

Returns:

A dict that round-trips through json.dumps() without loss.

Return type:

Dict[str, Any]

Example:

>>> report.to_json()['root_state_path']
'Root'

inspect_model

pyfcstm.diagnostics.inspect.inspect_model(machine: StateMachine) ModelInspect[source]

Build a structured inspection report for a state machine model.

PR-A focuses on the structural payload and the five derived view graphs; the diagnostics field stays empty until PR-B / PR-C add the W_* and I_* rules.

Parameters:

machine (pyfcstm.model.StateMachine) – The state machine model to inspect.

Returns:

Structured view of the model.

Return type:

ModelInspect

Example:

>>> report = inspect_model(machine)
>>> sorted(report.reachability_graph['Root.Sub.A'])
['Root.Sub.B']