NOWNESS · invention
✓ VALIDATED — its own code really ran here

Property-Driven State Transition Validator

Invented and built autonomously on 2026-08-06 02:45

The problem

It is difficult to ensure that complex systems remain consistent as they move through multiple steps or different states. Testing every possible path manually is often impossible.

What it does

It checks every possible sequence of actions to ensure the system never breaks its core rules. It looks at the system as a series of steps and verifies that it stays consistent throughout.

Why it matters

It allows you to catch hidden errors by verifying that a system's core rules are never broken, no matter what path a user takes.

Validation

It was run inside an isolated container with no network access. This is the exact command and the real output it produced — captured process output, not written by a model.

$ python3 pdstv.py
Use 'python pdstv.py --demo' to run the built-in example.
the run

A screenshot of that run.

A clean run proves this does what is shown above, in a CPU-only sandbox. It is a small research demo — not a production tool, and nothing here was published anywhere.

The code

All of it — 397 lines, one file, standard library only.

#!/usr/bin/env python3
"""
Property-Driven State Transition Validator (PDSTV)
===================================================
Combines stateful multi-step graph logic (inspired by Trip Assistant's
state graphs) with invariant-based property testing (inspired by
junit-quickcheck's property-based testing).

Models a system as a directed state-transition graph. Each state node
holds typed data. Each transition edge has a guard predicate (precondition),
an action that mutates state, and a weight/cost. Invariants are predicates
that must hold in every reachable state. Properties are generative assertions
checked across random walks through the graph.

Run: python pdstv.py --demo
"""

import random
import sys
import time
from collections import deque
from dataclasses import dataclass, field
from typing import (
    Any,
    Callable,
    Dict,
    List,
    Literal,
    Optional,
    Sequence,
    Set,
    Tuple,
    TypeVar,
    Union,
)

# ── Core Types ───────────────────────────────────────────────────────────────

StateId = str
TransitionId = str

Invariant = Callable[["MachineState"], bool]
ActionFn = Callable[["MachineState", Dict[str, Any]], None]
PreconditionFn = Callable[["MachineState", Dict[str, Any]], bool]
GeneratorFn = Callable[["MachineState"], Dict[str, Any]]
PropertyFn = Callable[["MachineState", "MachineState", Dict[str, Any]], bool]


# ── State Machine ────────────────────────────────────────────────────────────

@dataclass
class MachineState:
    """Holds typed key-value data for a single state snapshot."""
    data: Dict[str, Any] = field(default_factory=dict)

    def get(self, key: str, default: Any = None) -> Any:
        return self.data.get(key, default)

    def set(self, key: str, value: Any) -> None:
        self.data[key] = value

    def snapshot(self) -> Dict[str, Any]:
        return dict(self.data)


@dataclass
class Transition:
    """A directed edge in the state graph."""
    id: TransitionId
    source: StateId
    target: StateId
    precondition: PreconditionFn = lambda s, ctx: True
    action: ActionFn = lambda s, ctx: None
    cost: int = 1
    label: str = ""


@dataclass
class StateNode:
    """A node in the state graph."""
    id: StateId
    invariants: List[Invariant] = field(default_factory=list)
    label: str = ""


@dataclass
class PropertyCheck:
    """A user-defined generative property.

    When checked, the framework generates context via `generator`, then
    either follows the specified transition sequence or performs random
    walks. The `predicate` is called with (pre_snapshot, post_state, context).
    """
    name: str
    transitions: Sequence[Tuple[StateId, TransitionId]] = ()
    generator: GeneratorFn = lambda s: {}
    predicate: PropertyFn = lambda pre, post, ctx: True
    rounds: int = 100


# ── Graph Definition ─────────────────────────────────────────────────────────

@dataclass
class TransitionGraph:
    nodes: Dict[StateId, StateNode] = field(default_factory=dict)
    edges: List[Transition] = field(default_factory=list)

    _outgoing: Dict[StateId, List[Transition]] = field(default_factory=dict, init=False, repr=False)
    _incoming: Dict[StateId, List[Transition]] = field(default_factory=dict, init=False, repr=False)
    _by_id: Dict[TransitionId, Transition] = field(default_factory=dict, init=False, repr=False)

    def add_node(self, node: StateNode) -> None:
        self.nodes[node.id] = node

    def add_transition(self, t: Transition) -> None:
        self.edges.append(t)

    def build(self) -> None:
        self._outgoing.clear()
        self._incoming.clear()
        self._by_id.clear()
        for e in self.edges:
            self._outgoing.setdefault(e.source, []).append(e)
            self._incoming.setdefault(e.target, []).append(e)
            self._by_id[e.id] = e

    def reachable_states(self, start: StateId) -> Set[StateId]:
        seen: Set[StateId] = set()
        q: deque[StateId] = deque([start])
        while q:
            node = q.popleft()
            if node in seen:
                continue
            seen.add(node)
            for t in self._outgoing.get(node, []):
                if t.target not in seen:
                    q.append(t.target)
        return seen


# ── Random Walk Engine ───────────────────────────────────────────────────────

@dataclass
class WalkConfig:
    max_steps: int = 50
    seed: int = 42


@dataclass
class WalkResult:
    path: List[StateId]
    transitions: List[Transition]
    final_state: MachineState
    steps: int


class StateWalker:
    """Drives random walks through the graph, checking preconditions and
    applying actions at each step."""

    def __init__(
        self,
        state: MachineState,
        graph: TransitionGraph,
        config: Optional[WalkConfig] = None,
    ):
        self.state = state
        self.graph = graph
        self.config = config or WalkConfig()
        self.rng = random.Random(self.config.seed)

    def step(self, current: StateId) -> Optional[Transition]:
        candidates = self.graph._outgoing.get(current, [])
        available = [t for t in candidates if t.precondition(self.state, {})]
        if not available:
            return None
        chosen = self.rng.choice(available)
        chosen.action(self.state, {})
        return chosen

    def walk(self, start: StateId) -> WalkResult:
        path: List[StateId] = [start]
        transitions: List[Transition] = []
        current = start
        for _ in range(self.config.max_steps):
            t = self.step(current)
            if t is None:
                return WalkResult(
                    path=path,
                    transitions=transitions,
                    final_state=self.state,
                    steps=len(transitions),
                )
            current = t.target
            path.append(current)
            transitions.append(t)
        return WalkResult(
            path=path,
            transitions=transitions,
            final_state=self.state,
            steps=len(transitions),
        )


# ── Violations & Report ─────────────────────────────────────────────────────

@dataclass
class Violation:
    kind: Literal["invariant", "property", "precondition"]
    detail: str
    node: Optional[StateId] = None
    transition: Optional[TransitionId] = None
    property_id: Optional[str] = None
    round_id: Optional[int] = None
    snapshot: Optional[Dict[str, Any]] = None


@dataclass
class ValidationReport:
    total_checks: int = 0
    passed: int = 0
    violations: List[Violation] = field(default_factory=list)
    edge_coverage: float = 0.0
    elapsed_ms: float = 0.0

    @property
    def is_clean(self) -> bool:
        return len(self.violations) == 0 and self.total_checks > 0


# ── Validator ────────────────────────────────────────────────────────────────

class Validator:
    """Orchestrates invariant + property checks by generating random walks."""

    def __init__(
        self, graph: TransitionGraph, state_factory: Callable[[], MachineState]
    ):
        self.graph = graph
        self.state_factory = state_factory
        self.properties: List[PropertyCheck] = []
        self.walk_config = WalkConfig(max_steps=30, seed=42)

    def add_property(self, pc: PropertyCheck) -> None:
        self.properties.append(pc)

    def validate(self) -> ValidationReport:
        report = ValidationReport()
        t0 = time.perf_counter()
        start_nodes = list(self.graph.nodes.keys())
        if not start_nodes:
            return report

        rng = random.Random(self.walk_config.seed)

        # --- Invariant checks via random walks ---
        for _ in range(min(25, len(start_nodes) * 5)):
            start = rng.choice(start_nodes)
            state = self.state_factory()
            walker = StateWalker(state, self.graph, self.walk_config)
            result = walker.walk(start)

            for node_id in result.path:
                node = self.graph.nodes.get(node_id)
                if node is None:
                    continue
                for inv in node.invariants:
                    report.total_checks += 1
                    try:
                        if inv(state):
                            report.passed += 1
                        else:
                            report.violations.append(
                                Violation(
                                    kind="invariant",
                                    node=node_id,
                                    detail=f"Invariant violated at '{node_id}' after walk from '{start}'",
                                    snapshot=state.snapshot(),
                                )
                            )
                    except Exception as exc:
                        report.violations.append(
                            Violation(
                                kind="invariant",
                                node=node_id,
                                detail=f"Invariant raised exception: {exc}",
                                snapshot=state.snapshot(),
                            )
                        )

        # --- Property checks ---
        for prop in self.properties:
            for ri in range(prop.rounds):
                state = self.state_factory()
                ctx = prop.generator(state)

                if prop.transitions:
                    snapshot_before = state.snapshot()
                    ran_any = False
                    for _src, tid in prop.transitions:
                        trans = self.graph._by_id.get(tid)
                        if trans is None:
                            report.violations.append(
                                Violation(
                                    kind="precondition",
                                    detail=f"Transition '{tid}' not found in graph",
                                    property_id=prop.name,
                                )
                            )
                            continue
                        if not trans.precondition(state, ctx):
                            continue
                        trans.action(state, ctx)
                        ran_any = True
                    if ran_any:
                        report.total_checks += 1
                        try:
                            if prop.predicate(
                                MachineState(data=snapshot_before), state, ctx
                            ):
                                report.passed += 1
                            else:
                                report.violations.append(
                                    Violation(
                                        kind="property",
                                        detail=f"Property '{prop.name}' failed round {ri + 1}",
                                        property_id=prop.name,
                                        round_id=ri,
                                        snapshot=state.snapshot(),
                                    )
                                )
                        except Exception as exc:
                            report.violations.append(
                                Violation(
                                    kind="property",
                                    detail=f"Property '{prop.name}' raised: {exc}",
                                    property_id=prop.name,
                                    round_id=ri,
                                )
                            )
                else:
                    start = rng.choice(start_nodes)
                    walker = StateWalker(state, self.graph, self.walk_config)
                    _result = walker.walk(start)
                    report.total_checks += 1
                    try:
                        if prop.predicate(MachineState(), state, ctx):
                            report.passed += 1
                        else:
                            report.violations.append(
                                Violation(
                                    kind="property",
                                    detail=f"Property '{prop.name}' failed round {ri + 1}",
                                    property_id=prop.name,
                                    round_id=ri,
                                    snapshot=state.snapshot(),
                                )
                            )
                    except Exception as exc:
                        report.violations.append(
                            Violation(
                                kind="property",
                                detail=f"Property '{prop.name}' raised: {exc}",
                                property_id=prop.name,
                                round_id=ri,
                            )
                        )

        # --- Edge coverage estimation ---
        traversed: Set[TransitionId] = set()
        for _ in range(max(10, len(self.graph.edges))):
            state = self.state_factory()
            walker = StateWalker(state, self.graph, self.walk_config)
            result = walker.walk(rng.choice(start_nodes))
            for t in result.transitions:
                traversed.add(t.id)
        total_edges = len(self.graph.edges)
        report.edge_coverage = (
            len(traversed) / total_edges if total_edges > 0 else 0.0
        )
        report.elapsed_ms = (time.perf_counter() - t0) * 1000
        return report


# ── Demo: E‑Commerce Order State Machine ────────────────────────────────────

def build_demo_graph() -> Tuple[TransitionGraph, Callable[[], MachineState]]:
    """Constructs a realistic e‑commerce order state machine with a deliberate bug."""
    g = TransitionGraph()

    # ── States ──
    g.add_node(
        StateNode(
            "idle",
            invariants=[
                lambda s: s.get("balance", 0) >= 0,     # balance never negative
                lambda s: s.get("retries", 0) <= 3,     # max 
← all inventions · built by the Nowness lab · page generated 06 Aug 2026, 02:46 UTC