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:

Examples:

>>> from pyfcstm.dsl import parse_with_grammar_entry
>>> from pyfcstm.model 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

DEFAULT_DEEP_HIERARCHY_THRESHOLD

pyfcstm.diagnostics.inspect.DEFAULT_DEEP_HIERARCHY_THRESHOLD = 6

int([x]) -> integer int(x, base=10) -> integer

Convert a number or string to an integer, or return 0 if no arguments are given. If x is a number, return x.__int__(). For floating point numbers, this truncates towards zero.

If x is not a number or if base is given, then x must be a string, bytes, or bytearray instance representing an integer literal in the given base. The literal can be preceded by ‘+’ or ‘-’ and be surrounded by whitespace. The base defaults to 10. Valid bases are 0 and 2-36. Base 0 means to interpret the base from the string as an integer literal. >>> int(‘0b100’, base=0) 4

DEFAULT_LARGE_COMPOSITE_THRESHOLD

pyfcstm.diagnostics.inspect.DEFAULT_LARGE_COMPOSITE_THRESHOLD = 12

int([x]) -> integer int(x, base=10) -> integer

Convert a number or string to an integer, or return 0 if no arguments are given. If x is a number, return x.__int__(). For floating point numbers, this truncates towards zero.

If x is not a number or if base is given, then x must be a string, bytes, or bytearray instance representing an integer literal in the given base. The literal can be preceded by ‘+’ or ‘-’ and be surrounded by whitespace. The base defaults to 10. Valid bases are 0 and 2-36. Base 0 means to interpret the base from the string as an integer literal. >>> int(‘0b100’, base=0) 4

DEFAULT_VAR_TO_LEAF_RATIO_THRESHOLD

pyfcstm.diagnostics.inspect.DEFAULT_VAR_TO_LEAF_RATIO_THRESHOLD = 2.0

Convert a string or number to a floating point number, if possible.

DEFAULT_STRUCTURE_MAX_TRANSITIONS_PER_STATE

pyfcstm.diagnostics.inspect.DEFAULT_STRUCTURE_MAX_TRANSITIONS_PER_STATE = 6.0

Convert a string or number to a floating point number, if possible.

DEFAULT_STRUCTURE_MAX_UNREACHABLE_LEAF_STATE_RATE

pyfcstm.diagnostics.inspect.DEFAULT_STRUCTURE_MAX_UNREACHABLE_LEAF_STATE_RATE = 0.1

Convert a string or number to a floating point number, if possible.

DEFAULT_STRUCTURE_MAX_UNREACHABLE_TRANSITION_RATE

pyfcstm.diagnostics.inspect.DEFAULT_STRUCTURE_MAX_UNREACHABLE_TRANSITION_RATE = 0.1

Convert a string or number to a floating point number, if possible.

KNOWN_SPANLESS_CODES

pyfcstm.diagnostics.inspect.KNOWN_SPANLESS_CODES = frozenset({})

frozenset() -> empty frozenset object frozenset(iterable) -> frozenset object

Build an immutable unordered collection of unique elements.

VERIFY_SHARED_STATIC_CODES

pyfcstm.diagnostics.inspect.VERIFY_SHARED_STATIC_CODES = frozenset({'W_UNREACHABLE_STATE'})

frozenset() -> empty frozenset object frozenset(iterable) -> frozenset object

Build an immutable unordered collection of unique elements.

COMBO_GUARD_VERIFY_REPLACEMENT_CODES

pyfcstm.diagnostics.inspect.COMBO_GUARD_VERIFY_REPLACEMENT_CODES = {'W_DEAD_GUARD': 'W_COMBO_GUARD_CONST_FALSE', 'W_GUARD_TAUTOLOGY': 'W_COMBO_GUARD_CONST_TRUE'}

Mapping from generic guard diagnostics to combo-specific diagnostics when optional verify analysis runs against an expanded combo pseudo transition.

DEFAULT_STRUCTURE_STATISTICS_POLICY

pyfcstm.diagnostics.inspect.DEFAULT_STRUCTURE_STATISTICS_POLICY = StructureStatisticsPolicy(max_transitions_per_state=6.0, max_unreachable_leaf_state_rate=0.1, max_unreachable_transition_rate=0.1)

Advisory thresholds for the structure-statistics section.

The defaults are deliberately limited to size-normalized topology signals: a PSMBench-style pooled transition/state ratio of 2.75 remains below the broad review trigger, while 10% unreachable populations provide a useful review trigger without pretending to be a semantic proof. None disables one advisory threshold. Exceeding a threshold only records metadata; it never creates a diagnostic.

Parameters:
  • max_transitions_per_state – Maximum advisory T / S ratio.

  • max_unreachable_leaf_state_rate – Maximum advisory unreachable leaf state fraction.

  • max_unreachable_transition_rate – Maximum advisory unreachable authored transition fraction.

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, span: Span | None = None)[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, effect_self_assigns: ~typing.Tuple[str, ...], is_forced: bool, forced_origin: str | None, transition_index: int | None, span: ~pyfcstm.utils.validate.Span | None = None, effect_spans: ~typing.Tuple[~pyfcstm.utils.validate.Span, ...] = <factory>, effect_self_assign_spans: ~typing.Tuple[~pyfcstm.utils.validate.Span | None, ...] = <factory>, combo_origin_refs: ~typing.Tuple[~pyfcstm.diagnostics.inspect.ComboOriginRefInfo, ...] = <factory>, combo_projection_key: ~typing.Tuple[object, ...] | None = None, combo_projection_order_key: ~typing.Tuple[object, ...] | None = None, combo_reuse_group_id: str | None = None, combo_priority_run_identity: ~typing.Tuple[str, int | None] | None = None, combo_priority_run_index: int | None = None, source_path: str | None = None, target_history: str | None = 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]) – Normalized guard expression text, or None. Pyfcstm and jsfcstm share this inspect expression format so downstream range resolution can treat guard_text as a stable disambiguation hint.

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

  • effect_self_assigns (Tuple[str, ...]) – Variable names assigned to themselves anywhere inside the transition effect block, including nested if branches. Duplicate names are preserved so quick-fix emitters can detect ambiguous occurrences.

  • effect_self_assign_spans (Tuple[Optional[pyfcstm.utils.validate.Span], ...]) – Source spans for the self-assign statements listed in effect_self_assigns. The order matches effect_self_assigns and uses None when a statement has no source span, preventing later spans from shifting to earlier names.

  • 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.

  • transition_index (Optional[int]) – Zero-based index in parent-first model transition order, including expanded forced transitions at their declaring state before ordinary transitions and descendant-state transitions. Downstream tooling may use this as a best-effort source-range disambiguation hint when spans are not available.

  • source_path (Optional[str]) – Source file that authored this transition, when the model carries source metadata. This is distinct from the state paths in the transition endpoints.

  • combo_origin_refs (Tuple[ComboOriginRefInfo, ...]) – Provenance references from a generated combo edge back to the original combo trigger terms. Empty for ordinary transitions.

  • combo_projection_key (Optional[Tuple[object, ...]]) – Logical combo chooser key used to project generated continuation edges back to their original transition chooser.

  • combo_projection_order_key (Optional[Tuple[object, ...]]) – Stable ordering key inside the combo ordered-trie projection.

  • combo_reuse_group_id (Optional[str]) – Stable identifier explaining the local prefix-sharing group for generated combo edges.

  • combo_priority_run_identity (Optional[Tuple[str, Optional[int]]]) – Stable ordered-trie run identity, typically (run_anchor_origin_id, duplicate_discriminator).

  • combo_priority_run_index (Optional[int]) – Preorder index of the generated combo edge inside the projection.

  • target_history (Optional[str]) – 'shallow' or 'deep' when the transition enters Target.[H] or Target.[H*], otherwise None; to_path then names the history owner.

ComboOriginRefInfo

class pyfcstm.diagnostics.inspect.ComboOriginRefInfo(origin_id: str, term_index: int, role: str, consumes_term: bool, term_text: str, transition_span: Span | None = None, trigger_span: Span | None = None, term_span: Span | None = None, value_span: Span | None = None, removal_span: Span | None = None, source_kind: str = 'state', source_path: str | None = None, selection_owner_path: str | None = None, target_kind: str = 'state', target_path: str | None = None)[source]

Structured provenance reference for one generated combo edge.

Parameters:
  • origin_id (str) – Stable identifier of the original combo transition.

  • term_index (int) – Zero-based trigger term index consumed by this edge.

  • role (str) – Projection role, such as 'prefix' or 'terminal'.

  • consumes_term (bool) – Whether this edge consumes the referenced term.

  • term_text (str) – Canonical text of the referenced combo term.

  • transition_span (pyfcstm.utils.validate.Span, optional) – Source span of the original combo transition.

  • trigger_span (pyfcstm.utils.validate.Span, optional) – Source span of the full combo trigger suffix.

  • term_span (pyfcstm.utils.validate.Span, optional) – Source span of the referenced term.

  • value_span (pyfcstm.utils.validate.Span, optional) – Source span inside the term when available.

  • removal_span (pyfcstm.utils.validate.Span, optional) – Source span suitable for removing the term.

  • source_kind (str) – Authored source kind, 'state' or 'init'.

  • source_path (Optional[str]) – Authored source state path, or None for init.

  • selection_owner_path (Optional[str]) – Composite owner path for an init transition, or None for a normal transition.

  • target_kind (str) – Authored target kind, 'state' or 'exit'.

  • target_path (Optional[str]) – Authored target path, or '[*]' for exit.

Example:

>>> ref = ComboOriginRefInfo('Root:A->B::: E1 + E2', 0, 'prefix', True, 'E1')
>>> ref.term_text
'E1'

ComboOriginTermInfo

class pyfcstm.diagnostics.inspect.ComboOriginTermInfo(term_index: int, role: str, consumes_term: bool, term_text: str, transition_span: Span | None = None, trigger_span: Span | None = None, term_span: Span | None = None, value_span: Span | None = None, removal_span: Span | None = None)[source]

Term-level inspect record for one original combo trigger term.

Parameters:
  • term_index (int) – Zero-based trigger term index.

  • role (str) – Role of the generated edge first exposing this term.

  • consumes_term (bool) – Whether the term is consumed by that edge.

  • term_text (str) – Canonical term text.

  • transition_span (pyfcstm.utils.validate.Span, optional) – Source span of the original combo transition.

  • trigger_span (pyfcstm.utils.validate.Span, optional) – Source span of the combo trigger suffix.

  • term_span (pyfcstm.utils.validate.Span, optional) – Source span of the full term.

  • value_span (pyfcstm.utils.validate.Span, optional) – Source span inside the term when available.

  • removal_span (pyfcstm.utils.validate.Span, optional) – Source span suitable for removing the term.

Example:

>>> term = ComboOriginTermInfo(1, 'terminal', True, 'E2')
>>> term.term_index
1

ComboOriginInfo

class pyfcstm.diagnostics.inspect.ComboOriginInfo(origin_id: str, transition_span: Span | None, trigger_span: Span | None, terms: Tuple[ComboOriginTermInfo, ...])[source]

Inspect projection for one user-authored combo transition.

Parameters:

Example:

>>> origin = ComboOriginInfo('Root:A->B::: E1 + E2', None, None, ())
>>> origin.terms
()

VariableAccessSite

class pyfcstm.diagnostics.inspect.VariableAccessSite(kind: str, state_path: str, action: str | None, action_index: int | None, transition_index: int | None, statement_path: Tuple[int, ...], source_path: str | None, span: Span | None)[source]

A static variable access in an expanded model.

statement_path contains zero-based statement and branch indices within the owning action or effect; an empty path identifies a transition guard. span covers the authored statement, branch block, or transition, rather than claiming a token-level variable position. Missing source metadata remains None for programmatically constructed models.

VariableInfo

class pyfcstm.diagnostics.inspect.VariableInfo(name: str, type: str, init_value: str, read_in_states: ~typing.Tuple[str, ...], written_in_states: ~typing.Tuple[str, ...], read_in_guards: ~typing.Tuple[~typing.Tuple[str, str], ...], written_in_effects: ~typing.Tuple[~typing.Tuple[str, str], ...], affects_guard_directly: bool, affects_guard_indirectly: bool, abstract_actions_in_scope: ~typing.Tuple[str, ...], float_literal_assignments: ~typing.Tuple[str, ...] = <factory>, span: ~pyfcstm.utils.validate.Span | None = None, float_literal_assignment_spans: ~typing.Tuple[~pyfcstm.utils.validate.Span | None, ...] = <factory>, role: str = 'control', external_supply: str = 'none', diagnostic_policy: ~typing.Dict[str, bool] = <factory>, read_sites: ~typing.Tuple[~pyfcstm.diagnostics.inspect.VariableAccessSite, ...] = <factory>, write_sites: ~typing.Tuple[~pyfcstm.diagnostics.inspect.VariableAccessSite, ...] = <factory>)[source]

Structural summary of a variable definition plus guard-affect flags.

The affects_guard_directly and affects_guard_indirectly flags are precomputed here so that unreferenced-variable diagnostics 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.

  • affects_guard_directly (bool) – True when the variable is read by at least one transition guard.

  • affects_guard_indirectly (bool) – True when the variable reaches a transition guard through the conservative use-def graph.

  • abstract_actions_in_scope (Tuple[str, ...]) – Function names of abstract actions that may access this variable. FCSTM variables are global, so any abstract action in the machine is conservatively visible here. Downstream diagnostics can use this to distinguish high-confidence unused variables from variables that may be touched by abstract behavior.

  • float_literal_assignments (Tuple[str, ...]) – Source text of float literal assignments to this variable from lifecycle actions or transition effects.

  • external_supply (str) – cycle for inputs, construction for parameters, or none for model-owned control/output variables.

  • diagnostic_policy (Dict[str, bool]) – Fixed applicability of control-variable unused, unwritten-read, write-only and guard-variable-change diagnostics. These flags do not suppress other validation or expression analysis.

  • read_sites (Tuple[VariableAccessSite, ...]) – Static reads in expanded model traversal order. Repeated occurrences within one expression share a site.

  • write_sites (Tuple[VariableAccessSite, ...]) – Static assignment destinations, including unreachable statements but excluding declaration initializers.

EventInfo

class pyfcstm.diagnostics.inspect.EventInfo(qualified_name: str, scope: str, used_by: Tuple[Tuple[str, str], ...], is_declared: bool, is_used: bool, span: Span | None = None)[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.

  • is_declared (bool) – True when the event came from an explicit event declaration.

  • is_used (bool) – True when at least one transition references the event.

ActionInfo

class pyfcstm.diagnostics.inspect.ActionInfo(signature: str, state_path: str, name: str | None, stage: str, aspect: str | None, is_ref: bool, ref_target: str | None, is_attached: bool, span: Span | None = None)[source]

Structural summary of a lifecycle action declaration.

ForcedTransitionInfo

class pyfcstm.diagnostics.inspect.ForcedTransitionInfo(state_path: str, from_path: str, to_path: str, event: str | None, event_scope: str | None, guard: str | None, original_raw: str, expansion_count: int, span: Span | None = None)[source]

Structural summary of a forced transition declaration.

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 exposed by the inspect surface, including explicitly declared events that no transition uses.

  • 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.

StructureStatisticsPolicy

class pyfcstm.diagnostics.inspect.StructureStatisticsPolicy(max_transitions_per_state: float | None = 6.0, max_unreachable_leaf_state_rate: float | None = 0.1, max_unreachable_transition_rate: float | None = 0.1)[source]

Advisory thresholds for the structure-statistics section.

The defaults are deliberately limited to size-normalized topology signals: a PSMBench-style pooled transition/state ratio of 2.75 remains below the broad review trigger, while 10% unreachable populations provide a useful review trigger without pretending to be a semantic proof. None disables one advisory threshold. Exceeding a threshold only records metadata; it never creates a diagnostic.

Parameters:
  • max_transitions_per_state – Maximum advisory T / S ratio.

  • max_unreachable_leaf_state_rate – Maximum advisory unreachable leaf state fraction.

  • max_unreachable_transition_rate – Maximum advisory unreachable authored transition fraction.

StructureStatistics

class pyfcstm.diagnostics.inspect.StructureStatistics(state_count: int, leaf_state_count: int, composite_state_count: int, authored_transition_count: int, transitions_per_state: float | None, states_per_transition: float | None, unreachable_leaf_states: int, unreachable_leaf_state_rate: float | None, unreachable_transitions: int, unreachable_transition_rate: float | None, unreachable_transition_reasons: Dict[str, int], thresholds: StructureStatisticsPolicy, exceeded_thresholds: Tuple[str, ...], unguarded_transitions: int, guard_eligible_transitions: int, unguarded_rate: float | None, missing_effect_transitions: int, effect_eligible_transitions: int, missing_effect_rate: float | None, eventless_unconditional_transitions: int, behavior_transitions: int, eventless_unconditional_rate: float | None)[source]

Descriptive structure statistics for LLM and human review.

These values are intentionally observations, not health thresholds. The transition population is authored behavior: initial edges are excluded, generated combo edges are folded to their origin, and forced expansions are counted once per declaration.

Parameters:
  • state_count – Non-pseudo state count, including composite states.

  • leaf_state_count – Non-pseudo leaf-state count.

  • composite_state_count – Non-pseudo composite-state count.

  • authored_transition_count – Authored behavior transitions after initial-edge, combo-expansion, and forced-expansion normalization.

  • transitions_per_state – authored_transition_count / state_count; None when there are no states.

  • states_per_transition – Inverse ratio; None when there are no authored transitions.

  • unreachable_leaf_states – Leaf states reported as unreachable.

  • unreachable_leaf_state_rate – Unreachable leaf states divided by the leaf-state population, or None when there are no leaves.

  • unreachable_transitions – Distinct authored transitions covered by the existing unreachable/dead/shadowed diagnostics.

  • unreachable_transition_rate – Unreachable authored transitions divided by authored transitions, or None when there are none.

  • unreachable_transition_reasons – Reason buckets for that count.

  • thresholds – Advisory thresholds applied to the rates. These are metadata only and do not emit diagnostics.

  • exceeded_thresholds – Names of advisory thresholds exceeded by this report.

  • unguarded_transitions – Authored transitions without an AST guard.

  • guard_eligible_transitions – Authored transition denominator for unguarded_rate.

  • unguarded_rate – unguarded_transitions / guard_eligible_transitions or None for an empty denominator.

  • missing_effect_transitions – Effect-eligible transitions without an AST effect block.

  • effect_eligible_transitions – Authored non-forced transition denominator for missing_effect_rate.

  • missing_effect_rate – Missing-effect fraction or None when the denominator is empty.

  • eventless_unconditional_transitions – Authored transitions with no event and no guard.

  • behavior_transitions – Denominator for the eventless-unconditional rate; currently equal to authored_transition_count.

  • eventless_unconditional_rate – Eventless-unconditional fraction or None when the denominator is empty.

InspectVerificationPolicy

class pyfcstm.diagnostics.inspect.InspectVerificationPolicy(max_complexity_tier: str | None, max_call_count_scaling: str | None, smt_timeout_ms: int | None)[source]

Verify policy requested for one model inspection.

InspectVerificationSummary

class pyfcstm.diagnostics.inspect.InspectVerificationSummary(registered: int | None, executed: int, not_run: int, indeterminate: int)[source]

Aggregate execution counts for the verify projection.

InspectVerificationAlgorithm

class pyfcstm.diagnostics.inspect.InspectVerificationAlgorithm(algorithm_name: str, complexity_tier: str, call_count_scaling: str, verification_scope: str | None, declared_diagnostic_codes: Tuple[str, ...], result_kind: str, reason_code: str | None, reason: str | None, partial_diagnostic_count: int)[source]

Public execution projection for one registered verify algorithm.

InspectVerificationReport

class pyfcstm.diagnostics.inspect.InspectVerificationReport(supported: bool, enabled: bool, provider: str | None, reason_code: str | None, requested_policy: InspectVerificationPolicy, summary: InspectVerificationSummary, algorithms: Tuple[InspectVerificationAlgorithm, ...])[source]

Optional verification status attached to ModelInspect.

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, ...], actions: ~typing.Tuple[~pyfcstm.diagnostics.inspect.ActionInfo, ...], forced_transitions: ~typing.Tuple[~pyfcstm.diagnostics.inspect.ForcedTransitionInfo, ...], combo_transitions: ~typing.Tuple[~pyfcstm.diagnostics.inspect.TransitionInfo, ...], combo_origins: ~typing.Tuple[~pyfcstm.diagnostics.inspect.ComboOriginInfo, ...], 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>, verification: ~pyfcstm.diagnostics.inspect.InspectVerificationReport = <factory>, structure_statistics: ~pyfcstm.diagnostics.inspect.StructureStatistics = <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 exposed by the inspect surface, including explicitly declared events that no transition uses, sorted by qualified name.

  • actions (Tuple[ActionInfo, ...]) – Lifecycle and aspect actions attached to states.

  • forced_transitions (Tuple[ForcedTransitionInfo, ...]) – Source forced-transition declarations and their expansion summary.

  • combo_transitions (Tuple[TransitionInfo, ...]) – Generated combo transitions copied out of transitions for machine consumers that need direct access to combo projection metadata.

  • combo_origins (Tuple[ComboOriginInfo, ...]) – Original combo transition provenance grouped by origin_id and ordered by trigger term index.

  • metrics (ModelMetrics) – Aggregate model metrics.

  • structure_statistics (StructureStatistics) – Descriptive authored-structure counts and rates for human and LLM consumers. These are not health thresholds.

  • reachability_graph (Dict[str, Tuple[str, ...]]) – Mapping from every state path to state paths reachable through normal transitions and composite initial edges. Guards are ignored; [*] entry/exit markers are not exposed. A transition into a history also reaches the states of its default path and what a restore can re-enter.

  • 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_* plus design-health W_* / I_* diagnostics derived from the inspect payload.

  • verification (InspectVerificationReport) – Verify-provider policy and execution projection. The disabled report is populated without importing pyfcstm.verify.

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]

Examples:

>>> from pyfcstm.dsl import parse_with_grammar_entry
>>> from pyfcstm.model import parse_dsl_node_to_state_machine
>>> ast = parse_with_grammar_entry('state Root;', 'state_machine_dsl')
>>> machine = parse_dsl_node_to_state_machine(ast)
>>> report = inspect_model(machine)
>>> report.to_json()['root_state_path']
'Root'

inspect_model

pyfcstm.diagnostics.inspect.inspect_model(machine: StateMachine, *, deep_hierarchy_threshold: int = 6, large_composite_threshold: int = 12, var_to_leaf_ratio_threshold: float = 2.0, structure_statistics_policy: object | None = None, enable_verify: bool = False, max_complexity_tier: str = 'structural', max_call_count_scaling: str = 'linear_in_transitions', smt_timeout_ms: int | None = None, model_diagnostics: Sequence[ModelDiagnostic] | None = None) → ModelInspect[source]

Build a structured inspection report for a state machine model.

The report combines the structural payload, the five derived view graphs, and design-health diagnostics that can be computed from the inspect surface.

A machine that uses [H] / [H*] history is reported as written: model conversion can also build the model before history lowering, and the report, its findings and the optional verify run describe that model, with each history entry an ordinary transition marked by target_history. The lowered variables, gate states and route initials therefore appear nowhere in the report. For reachability, a history entry also reaches every state on the default path of the kind it names.

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

  • deep_hierarchy_threshold (int) – Maximum accepted hierarchy depth.

  • large_composite_threshold (int) – Maximum accepted number of direct child states in one composite.

  • var_to_leaf_ratio_threshold (float) – Maximum accepted variable to non-pseudo leaf-state ratio.

  • structure_statistics_policy (Optional[object]) – Optional advisory structure-statistics thresholds. A mapping may override selected fields; None uses the general-purpose defaults. Thresholds only appear in report metadata and never emit diagnostics.

  • enable_verify (bool, optional) – Whether to run inspect-eligible pyfcstm.verify algorithms and append their diagnostics. The default False preserves the Layer 2 inspect contract.

  • max_complexity_tier (str, optional) – Maximum verify complexity tier accepted by the inspect adapter when enable_verify is true. "structural" keeps the default to graph-only verification.

  • max_call_count_scaling (str, optional) – Maximum verify call-count scaling accepted by the inspect adapter when enable_verify is true.

  • smt_timeout_ms (Optional[int], optional) – Optional solver timeout forwarded to SMT-local verify algorithms. None preserves the raw verify default of no configured timeout; an integer value must be non-negative.

  • model_diagnostics (Optional[Sequence[pyfcstm.utils.validate.ModelDiagnostic]], optional) – Diagnostics produced while building machine, prepended to the analyzer output. Callers that built the model with pyfcstm.model.parse_dsl_node_to_state_machine() in collect mode pass the returned diagnostic list here so the report carries the model errors alongside the design-health warnings. Defaults to None, which uses the warnings a strict build of machine emitted while converting the model, such as W_HISTORY_UNUSED, so a strictly loaded model gets the same report as pyfcstm inspect.

Returns:

Structured view of the model.

Return type:

ModelInspect

Examples:

>>> from pyfcstm.dsl import parse_with_grammar_entry
>>> from pyfcstm.model import parse_dsl_node_to_state_machine
>>> source = '''
... state Root {
...     state A;
...     state B;
...     [*] -> A;
...     A -> B;
... }
... '''
>>> ast = parse_with_grammar_entry(source, 'state_machine_dsl')
>>> machine = parse_dsl_node_to_state_machine(ast)
>>> report = inspect_model(machine)
>>> report.reachability_graph['Root.A']
('Root.B',)
>>> verify_report = inspect_model(machine, enable_verify=True)
>>> len(verify_report.diagnostics) >= len(report.diagnostics)
True