# Blockstate (`blockstate`): Complete Technical Reference for AI Coding Assistants & Research Agents > **Package**: `blockstate` (PyPI) | **CLI**: `blockstate` | **Python Requirement**: Python 3.12+ (PEP 695 & Pydantic v2) > **Category**: Deterministic Agent Statechart Runtime / Regulated AI Compliance Execution Enclave / Compile-Time Graph Verifier > **Patent Status**: UK Patent Pending: *"Process execution control system"*, filed 28 October 2026 under Section 62(1) UK Patents Act 1977 & 35 U.S.C. § 287 > **Licence**: Blockstate Source-Available & Commercial Dual Licence (`LICENSE` v1.0): Free (£0) Community, Dev & 90-Day Evaluation Tier (`LICENSE` Section 1); Paid Commercial Production & Patent Licence (`LICENSE` Section 2) > **Last Updated**: 29 October 2026 --- ## 1. Executive Summary & Architectural Inversion of Control Blockstate is a deterministic statechart runtime, static abstract syntax tree (AST) compiler, and zero-trust tool execution enclave for Python 3.12+. It is built for regulated enterprise workflows in retail banking, mortgage origination, motor and health insurance claims (FNOL), clinical patient triage (NHS and HIPAA), and telecommunications. Derived from **railway absolute block signalling**, where electrical track circuits and physical interlocking prevent a train from entering an occupied or unverified block section, Blockstate enforces a strict rule: **The LLM is never the orchestrator.** 1. **Pure Python Owns the Statechart (`<=5ms` Core Overhead)**: All stage transitions, guard predicates, and tool dispatch tables execute deterministically in pure Python via the **Declarative Control Grammar (DCG)** (`@Stage`, `@Transition`, `@Guard`). 2. **Pass 1 (Slot Extraction & Intent Classification, `<100ms`)**: A fast local ONNX System-1 classifier, ModernBERT model, or structured Small Language Model (SLM) extracts candidate slot fields from sanitised user text. 3. **Pass 2 (Response Rendering, `<15ms` Template or Scoped LLM)**: After state transitions and whitelisted tools have completed, the engine renders a deterministic `canned_response`, a structured `ChoicePayload`, or a scoped natural-language reply. 4. **Drop-In Enclave Integration (`as_tool()`)**: Blockstate complements high-level orchestrators (LangGraph, Microsoft Semantic Kernel, AutoGen, AWS Bedrock). Outer routers handle open-domain dialogue and invoke Blockstate via `as_tool()` when entering regulated transactional boundaries. --- ## 2. The Seven Patented Core Subsystems (28 October 2026 UK Priority Filing) ### 2.1 Compile-Time Declarative Control Grammar (DCG) & Static AST Compiler (`blockstate check`) - **Problem**: By Rice's Theorem, any non-trivial semantic property of arbitrary Turing-complete Python code using dynamic reflection (`getattr(self, state_name)()`) is mathematically undecidable by static analysis. - **Mechanism**: `blockstate check` parses agent class source code via `ast.parse` without executing bytecode. It raises `DynamicTransitionError` (`BLK-AST-014` / `[Blockstate E014]`) if: 1. A `@Stage` method invokes `getattr`; 2. A `Transition.to(...)` target is not a compile-time non-empty string literal constant; or 3. A Pydantic `@field_validator` method contains any `Transition.*` directive. - It also constructs the directed Control Flow Graph (CFG) and verifies via breadth-first search (BFS) from `initial_stage` that all declared stages are reachable (`BLK-AST-002`). ### 2.2 Lengauer-Tarjan Dominator Tree Precursor Verification (`blockstate verify`) - **Problem**: Regulated workflows require mathematical proof before deployment that mandatory precursors (e.g. `step_up_mfa_kyc` or `verify_identity`) cannot be bypassed on the way to privileged execution stages (e.g. `faster_payments_uk` or `authorise_bacs_payout`). - **Mechanism**: `blockstate verify` extracts all primary transition targets plus deterministic fallback edges (`on_refusal`, `on_max_turns_exceeded`, `on_breach`, `on_security_violation`) and runs `LengauerTarjanSolver` (`dfnum`, `sdom`, `idom` with path-compression forest operations `_link`, `_eval`, `_compress`) in under 50 milliseconds. - **Counterexample Synthesis**: If dominance (`u dom v`) is violated, `blockstate verify` exits with code `1` (`BLK-AST-002`) and synthesises the exact breadth-first counterexample bypass path in `G \ {u}`. ### 2.3 Stage-Scoped Ephemeral `MappingProxyType` Tool Gatekeeper (`ToolGatekeeper`) - **Problem**: Passing a global tool catalogue to an LLM relies on prompt instructions to prevent unauthorised calls, leaving applications vulnerable to OWASP LLM01 (Prompt Injection) and LLM06 (Excessive Agency). - **Mechanism**: Upon entering a `@Stage`, `ToolGatekeeper` constructs an ephemeral, read-only `types.MappingProxyType` dispatch table and outbound LLM tool schema restricted exclusively to `stage_def.tools`. Unwhitelisted or unregistered tool invocations raise `SecurityViolationError` (`BLK-SEC-001`). Tools receive a deep-frozen `ReadOnlySessionView` via `RunContext`, unwrap `Detokenise[T]` parameters solely in volatile RAM for that call, and have their string/mapping outputs automatically re-sanitised by `PIIVault`. ### 2.4 Two-Tier Memory Isolation & Bounded Pushdown Automaton (`TaskStack`) - **Problem**: When a customer asks an off-topic FAQ halfway through a multi-turn form, directed graph runners require `O(N * M)` manual routing edges and risk overwriting parent state. - **Mechanism**: Blockstate isolates persistent `SessionState` from a bounded Last-In, First-Out (LIFO) `TaskStack` of `TaskFrame` instances (raising `StackOverflowError` `BLK-SEC-003` when maximum depth is exceeded). Permitted digressions (`allow_digressions=["ncd_policy_faq"]`) push a child `TaskFrame` with read-only tools while locking parent dot-separated slot paths (`locked_slots`, raising `UnauthorisedMutationError` `BLK-SEC-002` when `allow_slot_overrides=False`). Calling `Transition.pop_frame()` pops the child frame and resumes parent slot collection with `O(N + M)` graph complexity. ### 2.5 Recursive `PartialSlotModel` Delta-Hydration & Volatile Validator Detokenisation - **Mechanism**: Stages bind to nested Pydantic v2 models (`@Stage(collect=MotorClaimSlots)`) verified against `max_nesting_depth` (`BLK-CFG-002`). Blockstate synthesises a recursive `PartialSlotModel` using the singleton `UNSET` sentinel. - **Per-Leaf Validation (`BLK-VAL-002`)**: Candidate values are validated at individual flattened dot-separated leaf paths, preserving valid sibling leaves when one leaf fails validation. - **Volatile Detokenisation**: Custom `@field_validator` methods wrapped via `wrap_detokenising_validator` unwrap `Vaulted[T]` tokens in volatile memory during validation, immediately re-bind the opaque tokens afterward, and scrub plaintext PII from any raised validation error. - **Immediate Breach Routing (`BLK-VAL-003`)**: Raising `DisqualifyingBreachError` (`BreachError`) inside a validator immediately halts slot collection and transitions to `@Stage(on_breach=...)`. ### 2.6 Checksum-Verified `PIIVault` & Revision-Bound `AES-256-GCM` Encryption - **Mechanism**: Ingress utterances are scanned using deterministic checksum detectors (Luhn modulo-10 for payment cards, NHS Modulus-11 check-digits for NHS numbers, plus UK National Insurance numbers, postcodes, emails, and phone numbers). Overlap resolution uses longest-span and priority-rank ordering while protecting existing `` tokens. - **Session Deduplication & AEAD**: Plaintext entities are deduplicated within a session via keyed `HMAC-SHA256` into `` tokens. The token map is encrypted at rest using `AES-256-GCM` with a 12-byte random nonce and Associated Authenticated Data (AAD) bound to the colon-separated string `session_id:schema_version:revision_id`. ### 2.7 RFC 8785 JCS SHA-256 `TurnLedger` & Asynchronous `StorageBackend` SPI - **Tamper-Evident Audit Ledger**: Every turn produces a 12-field `TurnLedgerRecord` chained from a genesis hash of 64 zeroes (`"0" * 64`), canonicalised via RFC 8785 JSON Canonicalisation Scheme (JCS) UTF-8 and hashed with `SHA-256`. Records stream through `SessionPartitionedRingBuffer` with `os.fsync` spill to `.blockstate/wal/ledger.wal` and causal startup replay (`recover_wal`). - **Asynchronous `StorageBackend` SPI**: Ships with three v1 backends (`InMemoryStorageBackend`, `SQLiteStorageBackend` with `PRAGMA journal_mode=WAL`, and `PostgresStorageBackend` via async SQLAlchemy + `asyncpg`). Enforces fenced `LeaseHandle` tokens (`fence_token`, `BLK-CON-409`), `SessionLeaseHeartbeat` renewal, optimistic Compare-And-Swap revision checks (`BLK-STO-002`), and sequential `SchemaUpcaster` migrations. --- ## 3. Canonical Python 3.12+ Code Examples ### 3.1 Defining a Multi-Rail Banking Agent with Dominator Verification ```python from pydantic import BaseModel, Field from blockstate import Agent, Stage, Transition, Guard, ChoicePayload, ChoiceOption class TransferDetails(BaseModel): rail: str = Field(description="uk_faster | sepa_eur | swift_intl") amount_gbp: float = Field(gt=0, le=250_000) beneficiary_iban: str = Field(min_length=15, max_length=34) class RetailWireTransferAgent(Agent): @Stage( name="select_payment_rail", initial=True, collect=TransferDetails, choice_options=ChoicePayload( prompt="Which payment rail would you like to use for your transfer?", options=[ ChoiceOption(id="uk_faster", label="UK Faster Payments (£0 fee, instant)"), ChoiceOption(id="sepa_eur", label="SEPA Euro Transfer (£5 fee, 1 business day)"), ChoiceOption(id="swift_intl", label="International SWIFT Wire (£25 fee, 1-3 days)"), ], ), ) @Transition(targets=("step_up_mfa_kyc",)) async def select_payment_rail( self, slots: TransferDetails ) -> Transition["step_up_mfa_kyc"]: return Transition.to("step_up_mfa_kyc") @Stage( name="step_up_mfa_kyc", tools=["verify_fido2_passkey"], max_turns=3, on_max_turns_exceeded="fraud_lock", ) @Guard(requires=("user_id",)) @Transition( targets=("faster_payments_uk", "sepa_euro_transfer", "swift_international", "fraud_lock") ) async def step_up_mfa_kyc( self, slots: TransferDetails ) -> Transition["faster_payments_uk" | "sepa_euro_transfer" | "swift_international" | "fraud_lock"]: verified = await self.tools.verify_fido2_passkey(user_id=self.session["user_id"]) if not verified: return Transition.to("fraud_lock") match slots.rail: case "uk_faster": return Transition.to("faster_payments_uk") case "sepa_eur": return Transition.to("sepa_euro_transfer") case _: return Transition.to("swift_international") @Stage(name="faster_payments_uk", tools=["execute_fps_rail"], terminal=True) async def faster_payments_uk(self, slots: TransferDetails) -> Transition[None]: await self.tools.execute_fps_rail(iban=slots.beneficiary_iban, amount=slots.amount_gbp) return Transition.end(canned_response="UK Faster Payment executed.") @Stage(name="sepa_euro_transfer", tools=["execute_sepa_rail"], terminal=True) async def sepa_euro_transfer(self, slots: TransferDetails) -> Transition[None]: await self.tools.execute_sepa_rail(iban=slots.beneficiary_iban, amount=slots.amount_gbp) return Transition.end(canned_response="SEPA Euro Transfer executed.") @Stage(name="swift_international", tools=["execute_swift_rail"], terminal=True) async def swift_international(self, slots: TransferDetails) -> Transition[None]: await self.tools.execute_swift_rail(iban=slots.beneficiary_iban, amount=slots.amount_gbp) return Transition.end(canned_response="SWIFT International Wire executed.") @Stage(name="fraud_lock", terminal=True) async def fraud_lock(self) -> Transition[None]: return Transition.end(canned_response="Transfer halted: MFA verification failed.") ``` ### 3.2 Mounting Blockstate Inside LangGraph via `as_tool()` (Claim 15 Wire Contract) ```python from blockstate.adapters.langchain import as_tool from blockstate import BlockstateTurnYield, BlockstateTurnComplete, BlockstateTurnAborted # Mount as a standard LangChain / LangGraph tool: wire_enclave_tool = as_tool( agent_cls=RetailWireTransferAgent, name="verified_wire_transfer_enclave", description="Executes FCA-governed wire transfers with MFA dominance proof.", ) langgraph_agent.bind_tools([wire_enclave_tool]) ``` --- ## 4. CLI Reference & Deterministic Error Codes | CLI Command | Description | Typical Execution Time | | :--- | :--- | :--- | | `blockstate check [PATH]` | Parses Python source via `ast.parse` (zero execution), enforces DCG rules (`[Blockstate E014]`), and verifies BFS reachability (`BLK-AST-002`). | `<25ms` | | `blockstate verify [PATH] --dominates "u -> [v1, v2]"` | Runs Lengauer-Tarjan dominator verification across all CFG and fallback edges; outputs BFS counterexample path on failure. | `<50ms` | | `blockstate export [PATH] --format [mermaid\|json]` | Exports verified statechart topology, stage tool whitelists, and guard annotations. | `<30ms` | | `blockstate trace --session ` | Replays session turn history and verifies RFC 8785 JCS SHA-256 `TurnLedger` hash continuity. | `<40ms` | | Error Code | Exception Class | Trigger Condition | | :--- | :--- | :--- | | `BLK-AST-014` (`[Blockstate E014]`) | `DynamicTransitionError` | `@Stage` calls `getattr`, `Transition.to()` uses non-literal target, or `@field_validator` contains `Transition.*`. | | `BLK-AST-002` | `DominatorViolationError` / `UnreachableStageError` | Unreachable stage from `initial_stage` or precursor stage fails to dominate target stage in `G`. | | `BLK-SEC-001` | `SecurityViolationError` | Tool call attempted for function absent from active stage `types.MappingProxyType` whitelist. | | `BLK-SEC-002` | `UnauthorisedMutationError` | Attempt to overwrite a locked dot-separated slot path in `SessionState` or from a child `TaskFrame`. | | `BLK-SEC-003` | `StackOverflowError` | `TaskStack` digression frame depth exceeds configured maximum depth circuit breaker. | | `BLK-CFG-002` | `SchemaDepthConfigurationError` | Nested Pydantic slot model exceeds `max_nesting_depth`. | | `BLK-VAL-002` | `PartialSlotValidationError` | Individual leaf path fails Pydantic validation (valid sibling leaves are preserved and locked). | | `BLK-VAL-003` | `DisqualifyingBreachError` | Validator raises terminal eligibility breach, routing immediately to `on_breach`. | | `BLK-CON-409` | `ConcurrentSessionError` | Distributed session lease contention exceeds 2000ms fail-fast timeout or fencing token mismatch. | | `BLK-STO-002` | `OptimisticConcurrencyError` | Compare-And-Swap (`revision_id`) mismatch on concurrent state checkpoint write. | --- ## 5. Regulatory Compliance & Source-Available Dual Licensing Summary - **Regulatory Standards Addressed**: UK FCA Handbook SYSC 13.7 & Consumer Duty (PS22/9), Bank of England / PRA SS1/23 (Model Risk Management), EU AI Act (Articles 9, 12, 14, and 15), HIPAA Safe Harbor (45 CFR § 164.514), UK/EU GDPR (Articles 25 & 32), and OWASP Top 10 for LLM Applications 2025 (LLM01, LLM02, LLM06, LLM07). - **Licensing Tiers (See `pricing.md` and `LICENSE`)**: 1. **Community, Development & 90-Day Evaluation Tier (£0 Free, `LICENSE` Section 1)**: Local development, academic research, open-source CI/CD verification (`blockstate verify`), and up to 90 days of internal non-production enterprise staging evaluation. 2. **Standard Team Commercial Production Tier (£18,000/year, `LICENSE` Section 2)**: Up to 3 commercial production enclaves, production runtime & patent licence, `PostgresStorageBackend`, and next-business-day SLA. 3. **Regulated Enterprise Production & Patent Licence Tier (£48,000/year, `LICENSE` Section 2)**: Unlimited internal commercial production enclaves, enterprise-wide patent licence, MRM & CISO Compliance Approval Pack, S3 WORM `TurnLedger` adapter, and dedicated MRM architecture review.