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 summaryTransitionInfo— per-transition structural summaryVariableInfo— per-variable structural summary plus participation flags used byW_UNREFERENCED_VAREventInfo— per-event structural summaryModelMetrics— aggregate counts and ratiosModelInspect— 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
Nonefor the root state.is_leaf (bool) –
Truewhen this state has no substates.is_pseudo (bool) –
Truewhen the state was declared withpseudo state.is_composite (bool) –
Truewhen this state has substates.substates (Tuple[str, ...]) – Direct-child state paths, in source order.
initial_targets (Tuple[Mapping[str, Any], ...]) – Each item describes one
[*] -> Xinitial transition declared inside this composite.targetis the target child path,guardis the source text of the guard orNone,eventis the qualified event name orNone,is_unconditionalisTrueonly when both guard and event are absent.entry_actions (Tuple[str, ...]) – Action labels (function name or
'<inline>') forenteractions on this state, in source order.during_actions (Tuple[str, ...]) – Action labels for
duringactions.exit_actions (Tuple[str, ...]) – Action labels for
exitactions.aspect_before (Tuple[str, ...]) – Aspect-action labels for
>> during before.aspect_after (Tuple[str, ...]) – Aspect-action labels for
>> during after.has_abstract_action (bool) –
Trueif any of the actions above is abstract. Used byVariableInfoconfidence 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') orNoneif the transition has no event.event_scope (Optional[str]) –
'local','chain','absolute', orNonewhen 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) –
Truewhen the transition was expanded from a!-prefixed forced transition.forced_origin (Optional[str]) – Raw source text of the original
!X -> Ydeclaration whenis_forcedisTrue, otherwiseNone.
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_directlyandparticipates_indirectlyflags are precomputed here so that PR-C’sW_UNREFERENCED_VAR/I_UNREFERENCED_VAR_MAYBE_ABSTRACTrules 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) –
Truewhen the variable is read by at least one guard, transition effect, or action operation.participates_indirectly (bool) –
Truewhen the variable is not directly referenced but is transitively reachable through write-then-read data dependency across blocks. PR-A keeps this field asFalsefor 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) fromI_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_leavesfor composite states that declare>> duringaspects.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
defvariables, 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
refedges out of it.diagnostics (Tuple[ModelDiagnostic, ...]) – Layer 1
E_*+ Layer 2W_*/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.
ModelDiagnosticinstances 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
diagnosticsfield stays empty until PR-B / PR-C add theW_*andI_*rules.- Parameters:
machine (pyfcstm.model.StateMachine) – The state machine model to inspect.
- Returns:
Structured view of the model.
- Return type:
Example:
>>> report = inspect_model(machine) >>> sorted(report.reachability_graph['Root.Sub.A']) ['Root.Sub.B']