Core & Exceptions¶
Foundation types shared across all SPO subsystems.
Purpose for production teams¶
This page is the reliability seam for the entire runtime. The shared error family and compatibility controls are what allow subsystems to fail with bounded, actionable behaviour instead of cascading into undefined control actions.
When reading this page:
- use the exception section to align operational alerting and fallback policy,
- use the compatibility section to understand optional dependency fallbacks,
- use the observability entry points to verify what metrics and telemetry remain available when optional stacks are missing.
The intent is to make runtime limits and optional paths explicit before deployment planning begins.
Exception Hierarchy¶
SPO defines a hierarchy of domain-specific exceptions rooted at
SPOError. Each subsystem raises its own subclass so that callers
can catch errors at the appropriate granularity:
SPOError
├── BindingError # Invalid binding specification or missing fields
├── ValidationError # Schema or constraint violation
├── ExtractorError # Phase extraction failure (bad signal, wrong sample rate)
├── EngineError # UPDE integration failure (NaN, divergence, dt violation)
├── PolicyError # Policy rule evaluation failure
└── AuditError # Audit chain integrity violation
All exceptions carry a descriptive message and preserve the original traceback when wrapping lower-level errors.
Catching by Granularity¶
from scpn_phase_orchestrator.exceptions import SPOError, EngineError
try:
engine.step(phases, omegas, knm, zeta, psi, alpha)
except EngineError as e:
# Handle integration failure specifically
logger.warning(f"Engine diverged: {e}")
supervisor.force_transition(Regime.CRITICAL)
except SPOError as e:
# Catch-all for any SPO error
logger.error(f"SPO error: {e}")
exceptions ¶
Exception hierarchy for scpn-phase-orchestrator.
Classes¶
SPOError ¶
Bases: Exception
Base exception for all scpn-phase-orchestrator errors.
ExtractorError ¶
Bases: SPOError, RuntimeError
Phase extractor encountered an unrecoverable signal condition.
Compatibility¶
Internal compatibility module providing shared constants and conditional imports for optional dependencies:
TWO_PI— \(2\pi\) asfloat64(avoids repeated computation)- Conditional imports for
jax,equinox,redis,opentelemetrythat fall back gracefully when the dependency is not installed
_compat ¶
Shared compatibility constants for Python and optional Rust execution paths.
The module exposes TWO_PI and a process-local HAS_RUST capability flag
computed from the presence of the optional spo_kernel package. Importing it
does not load the Rust extension or change backend selection by itself; concrete
modules decide when to dispatch to accelerated kernels.
CLI entry point¶
The public Click command tree lives in scpn_phase_orchestrator.runtime.cli. Use the
CLI Reference for command-oriented examples; this API section
keeps the callable entry points visible to mkdocstrings.
cli ¶
Command-line entry point for validation, replay, export, and review workflows.
The CLI wraps public SPO APIs behind explicit commands for binding validation, inspection, auto-binding proposals, coupling estimation, formal export, replay, plugin catalogs, scaffolding, and selected runtime utilities. Commands validate local inputs and emit text or JSON review artifacts; they do not push commits, start network services, or perform live actuation unless an explicit subcommand is invoked for that runtime path.
Functions:¶
main ¶
meta_transfer_manifest ¶
meta_transfer_manifest(
audit_paths: tuple[str, ...],
audit_directory: str | None,
pattern: str,
min_records: int,
package_name: str,
import_target: str,
console_script: str,
output: str | None,
) -> None
Emit a review-only meta-transfer package manifest from audit history.
Parameters¶
audit_paths : tuple[str, ...]
Audit-log paths to package.
audit_directory : str | None
Directory of audit logs, or None.
pattern : str
Glob pattern for audit-log discovery.
min_records : int
Minimum number of records required.
package_name : str
Name for the emitted package.
import_target : str
Import target for the generated package.
console_script : str
Console-script entry-point name.
output : str | None
Destination path, or None for stdout.
Raises¶
ClickException If the inputs are invalid or the operation fails.
Source code in src/scpn_phase_orchestrator/runtime/cli/meta.py
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | |
simulate ¶
simulate(
spec: BindingSpec,
*,
steps: int = 100,
seed: int = 42,
policy_enabled: bool = True,
control_mode: SimulationControlMode = "supervisor_policy",
audit_logger: AuditLogger | None = None,
strict_audit_integrity: bool = True,
binding_spec_path: Path | None = None,
scenario_hook: ScenarioCallback | None = None,
conformal_gate: TwinConformalGate | None = None,
twin_confidence_source: TwinConfidenceSource
| None = None,
) -> SimulationResult
Advance a binding spec for steps and return the simulation outcome.
Parameters¶
spec : BindingSpec
A validated binding spec.
steps : int
Number of integration steps.
seed : int
RNG seed for the initial phases.
policy_enabled : bool
Closed-loop control feedback on (True) or open-loop baseline (False).
control_mode : {"supervisor_policy"}
The live control surface used by the generic binding-spec simulator.
Koopman MPC is intentionally not a selectable simulate mode; it
remains a review-only/offline proposal surface in
:mod:scpn_phase_orchestrator.runtime.dvoc_oscillation_damping.
audit_logger : AuditLogger | None
Optional logger; when given, the header, per-step records, and events are
written. The caller owns its lifecycle (close).
strict_audit_integrity : bool
Fail closed on a broken audit chain. When True (default) and the audit
logger owns a protobuf event stream whose close-time integrity check fails,
the run raises :class:~scpn_phase_orchestrator.exceptions.AuditError
instead of returning a result with a tamper-evident but unenforced
audit_event_stream_integrity field. Set False to attach the failed
result for inspection without failing the run.
binding_spec_path : Path | None
Optional path to the spec, used only to locate an adjacent policy.yaml. When
None, no domainpack policy rules are loaded (the supervisor policy still
runs when policy_enabled).
scenario_hook : ScenarioCallback | None
Optional deterministic per-step perturbation hook for benchmark and case-study
scenarios. The hook can mutate phases, frequencies, coupling, zeta, or
psi_target in a validated :class:SimulationScenarioContext; it cannot
perform actuation.
conformal_gate : TwinConformalGate | None
Optional calibrated conformal twin-confidence gate. When supplied with
twin_confidence_source, the gate scores every live policy tick before
actions are applied.
twin_confidence_source : TwinConfidenceSource | None
Callable that returns a twin-confidence score for the current policy tick.
It must be supplied together with conformal_gate.
Returns¶
SimulationResult
A :class:SimulationResult.
Raises¶
ValueError
If the spec declares no oscillators, an unsupported control mode, or an
incomplete conformal-admission configuration.
AuditError
If strict_audit_integrity and the audit event-stream integrity check
fails at close time (the recorded run cannot be trusted).
Source code in src/scpn_phase_orchestrator/runtime/simulation.py
363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 | |
evolutionary_grammar ¶
CLI commands for the review-only evolutionary supervisor grammar family.
Three operator commands wrap the deterministic offline mutation-search grammars
in supervisor.evolutionary_policy_dsl,
supervisor.evolutionary_petri_grammar, and
supervisor.evolutionary_topology_grammar. Each reads a local source artefact,
runs the offline search, and emits one deterministic review bundle. None of the
commands actuate, merge, hot-patch, or execute any mutated candidate; they only
generate operator-review evidence.
Functions:¶
evolutionary_policy_dsl_search ¶
evolutionary_policy_dsl_search(
policy_dsl_file: Path,
generations: int,
population: int,
mutation_step: float,
output: Path | None,
) -> None
Emit review evidence for offline policy-DSL mutation candidates.
Parameters¶
policy_dsl_file : Path Text file containing the compact policy-DSL source. generations : int Number of search generations. population : int Number of candidates per generation. mutation_step : float Mutation step size applied per generation. output : Path | None Optional path to write the emitted bundle JSON.
Raises¶
ClickException If the DSL source or search parameters are invalid.
Source code in src/scpn_phase_orchestrator/runtime/cli/evolutionary_grammar.py
evolutionary_petri_mutation ¶
evolutionary_petri_mutation(
net_json: Path,
generations: int,
candidates_per_generation: int,
mutation_step: float,
max_arc_weight: int,
max_token_bound: int,
output: Path | None,
) -> None
Emit review evidence for offline Petri-net mutation candidates.
Parameters¶
net_json : Path JSON file with a net-like payload of places, transitions, and arcs. generations : int Number of search generations. candidates_per_generation : int Candidates evaluated per generation. mutation_step : float Mutation step size applied per generation. max_arc_weight : int Maximum arc weight allowed in a mutated net. max_token_bound : int Maximum token count allowed per place. output : Path | None Optional path to write the emitted bundle JSON.
Raises¶
ClickException If the net payload or bounds are invalid.
Source code in src/scpn_phase_orchestrator/runtime/cli/evolutionary_grammar.py
evolutionary_topology_mutation ¶
evolutionary_topology_mutation(
topology_json: Path,
generations: int,
population: int,
mutation_step: float,
min_edge_weight: float,
max_edge_weight: float,
edge_add_base_weight: float,
max_add_candidates: int,
output: Path | None,
) -> None
Emit review evidence for offline topology mutation candidates.
Parameters¶
topology_json : Path
JSON file with a nodes array and an edges array.
generations : int
Number of search generations.
population : int
Number of candidates per generation.
mutation_step : float
Mutation step size applied per generation.
min_edge_weight : float
Minimum retained edge weight.
max_edge_weight : float
Maximum allowed edge weight.
edge_add_base_weight : float
Base weight assigned to newly added edges.
max_add_candidates : int
Maximum number of edge-addition candidates.
output : Path | None
Optional path to write the emitted bundle JSON.
Raises¶
ClickException If the topology records or bounds are invalid.
Source code in src/scpn_phase_orchestrator/runtime/cli/evolutionary_grammar.py
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 | |
federated_dp_noise_service ¶
CLI command for review-only federated DP noise-service preflight evidence.
The command consumes a DP-noise request declaration plus a deployment
declaration, builds the deterministic DP-noise request and response manifests,
then a review-only deployment preflight manifest, and emits a non-actuating
preflight bundle. It validates the same production supervisor surface as
supervisor.federated_dp_noise_service and never opens sockets, generates live
noise, or permits live DP noise-service execution. Missing deployment
prerequisites are reported as a not-ready readiness verdict rather than an error;
only malformed inputs fail closed.
Classes¶
Functions:¶
federated_dp_noise_service_preflight ¶
federated_dp_noise_service_preflight(
request_json: Path,
deployment_json: Path,
output: Path | None,
) -> None
Emit deterministic review evidence for a DP noise-service deployment.
Parameters¶
request_json : Path JSON file describing the DP-noise request (privacy parameters, seed commitment, policy keys, and per-node privacy budgets). deployment_json : Path JSON file describing the deployment preflight inputs (mechanism, custody, accountant, budget issuer, service endpoint, and operator approval). output : Path | None Optional path to write the emitted bundle JSON.
Raises¶
ClickException If inputs are malformed or the DP noise-service preflight fails closed. A merely not-ready deployment is reported in the bundle, not raised.
Source code in src/scpn_phase_orchestrator/runtime/cli/federated_dp_noise_service.py
federated_secure_aggregation ¶
CLI command for review-only federated secure-aggregation preflight evidence.
The command consumes newline-delimited secure-aggregation node commitment records
plus a deployment declaration, builds the deterministic secure-aggregation
manifest, then a review-only deployment preflight manifest, and emits a
non-actuating preflight bundle. It validates the same production supervisor
surface as supervisor.federated_secure_aggregation and never opens sockets,
runs aggregation, or permits live secure-aggregation execution.
Classes¶
Functions:¶
federated_secure_aggregation_preflight ¶
federated_secure_aggregation_preflight(
node_commitments_jsonl: Path,
deployment_json: Path,
output: Path | None,
) -> None
Emit deterministic review evidence for a secure-aggregation deployment.
Parameters¶
node_commitments_jsonl : Path JSONL file containing secure-aggregation node commitment records. deployment_json : Path JSON file describing the aggregation policy and deployment preflight inputs (quorum evidence, custody records, operator approval). output : Path | None Optional path to write the emitted bundle JSON.
Raises¶
ClickException If inputs are malformed or the secure-aggregation preflight fails closed.
Source code in src/scpn_phase_orchestrator/runtime/cli/federated_secure_aggregation.py
federated_transport ¶
CLI command for review-only federated transport preflight evidence.
The command consumes newline-delimited federated node update audit records plus a
transport declaration, then builds signed/hash-linked envelopes, replays the
batch, and emits a deterministic non-actuating preflight bundle. It validates
the same production supervisor transport surface as
supervisor.federated_transport and never opens sockets or permits live
transport execution.
Functions:¶
federated_transport_preflight ¶
federated_transport_preflight(
node_updates_jsonl: Path,
transport_declaration_json: Path,
output: Path | None,
) -> None
Emit deterministic review evidence for a federated transport batch.
Parameters¶
node_updates_jsonl : Path JSONL file containing node update audit records. transport_declaration_json : Path JSON file describing the intended transport boundary. output : Path | None Optional path to write the emitted bundle JSON.
Raises¶
ClickException If inputs are malformed or the transport preflight fails closed.
Source code in src/scpn_phase_orchestrator/runtime/cli/federated_transport.py
Simulation core¶
runtime.simulation is the single non-actuating closed/open-loop core that
backs both spo run and the public evaluate_binding_spec facade, so a
binding spec is advanced by exactly one implementation. policy_enabled is the
open/closed-loop switch: with it on, the supervisor and domainpack policy feed
bounded actions back into the dynamics; with it off, the same drivers and
intrinsic plasticity run without control feedback, giving the baseline for
measuring orchestration uplift on a fixed seed.
The live control mode is deliberately explicit and narrow:
control_mode="supervisor_policy" is the only accepted simulate() mode.
Koopman MPC is not a hidden or partially wired binding-spec simulator mode; it is
kept in the offline/review-only dVOC damping pipeline where the fitted predictor,
plant model, and PRC evidence boundary are explicit.
simulate() also accepts an optional scenario_hook for deterministic
non-actuating perturbation schedules. The hook receives a
SimulationScenarioContext before each integration step and may adjust phases,
natural frequencies, coupling state, zeta, or psi_target; the core validates
finite vector shapes, scalar types, and CouplingState identity immediately
after the hook returns. This is the supported path for benchmark and case-study
scenario evidence, not a hardware or live-actuation path.
For deployments that have an observed-twin confidence stream, simulate() can
also receive a calibrated TwinConformalGate plus a twin_confidence_source.
The source receives SimulationTwinConfidenceContext on each live policy tick
and returns a TwinConfidenceScore; the gate scores that value through
confidence_nonconformity. A rejected conformal decision fails closed for that
tick by suppressing all proposed policy actions, and the result records the
admission count, rejection count, and last decision. Audit-enabled runs emit a
conformal_admission event for every scored policy tick. The default CLI run
does not synthesize an observed twin feed, so the gate is opt-in rather than
implicitly active for every binding spec.
simulation ¶
Single-source closed/open-loop simulation core for binding specs.
The CLI spo run command and the public Orchestrator.evaluate API both
call :func:simulate, so a binding spec is advanced by exactly one
implementation — there is no second loop to drift out of fidelity.
The core is non-actuating: it never writes to hardware, opens a network connection, or enforces a safety tier (tier enforcement is a caller policy, kept in the CLI). It supports both the Kuramoto (UPDE) and amplitude (Stuart-Landau) engines, optional Hebbian imprint plasticity, geometry-prior projection, exogenous physical/informational/symbolic drivers, Petri-net protocol gating, boundary observation, and the supervisor + domainpack policy control loop.
policy_enabled is the open/closed-loop switch:
True(closed loop) — the supervisor and domainpack policy evaluate the state every control interval and their bounded, projected actions feed back into coupling, lag, damping, and drive. This is whatspo runuses.False(open loop) — the same exogenous drivers and intrinsic plasticity still run, but no control feedback is applied. This is the baseline against which the closed-loop orchestration uplift is measured on the same seed.
control_mode is intentionally narrower than the actuation catalogue. The
generic binding-spec loop accepts only "supervisor_policy"; Koopman MPC stays
in the specialized offline/review-only dVOC damping pipeline where its fitted
predictor, plant model, and evidence boundaries are explicit.
Classes¶
SimulationResult
dataclass
¶
SimulationResult(
spec_name: str,
steps: int,
policy_enabled: bool,
control_mode: str,
amplitude_mode: bool,
final_phases: FloatArray,
final_amplitudes: FloatArray | None,
r_good: float,
r_bad: float,
separation: float,
final_regime: str,
mean_amplitude: float | None,
r_good_history: tuple[float, ...],
r_bad_history: tuple[float, ...],
boundary_violation_total: int,
action_total: int,
conformal_admission_total: int = 0,
conformal_admission_rejections: int = 0,
last_conformal_admission: ConformalDecision
| None = None,
audit_event_stream_integrity: AuditStreamIntegrityResult
| None = None,
)
Outcome of one :func:simulate run.
Attributes¶
spec_name: Binding-spec name.
steps: Number of steps advanced.
policy_enabled: Whether the closed-loop control feedback was active.
control_mode: Live control surface used by the simulation core.
amplitude_mode: Whether the Stuart-Landau (amplitude) engine was used.
final_phases: Final oscillator phases, shape ``(n,)``.
final_amplitudes: Final amplitudes for amplitude mode, else ``None``.
r_good: Final order parameter over the objective good layers.
r_bad: Final order parameter over the objective bad layers.
separation: ``r_good - r_bad`` (the coherence objective margin).
final_regime: Final regime label.
mean_amplitude: Final mean amplitude for amplitude mode, else ``None``.
r_good_history / r_bad_history: Per-step good/bad order parameters.
boundary_violation_total: Summed boundary violations across steps.
action_total: Total projected control actions applied (0 when open loop).
conformal_admission_total: Number of live policy ticks scored by the
conformal twin-confidence admission gate.
conformal_admission_rejections: Number of scored policy ticks rejected by
the conformal admission gate. Rejected ticks suppress all proposed
actions for that control interval.
last_conformal_admission: Last conformal admission decision, when the
optional gate was active.
audit_event_stream_integrity: Close-time protobuf audit-stream integrity
summary when an event stream was written, else ``None``. Under the
default ``strict_audit_integrity`` this is always ``ok`` (a failed
check fails the run closed); it can carry a failed summary only when
:func:`simulate` was called with ``strict_audit_integrity=False``.
Methods:¶
to_record ¶
Return a deterministic JSON-serialisable summary (history omitted).
Returns¶
dict[str, object] Return a deterministic JSON-serialisable summary (history omitted).
Source code in src/scpn_phase_orchestrator/runtime/simulation.py
SimulationScenarioContext
dataclass
¶
SimulationScenarioContext(
spec_name: str,
step: int,
sample_period_s: float,
phases: FloatArray,
omegas: FloatArray,
coupling: CouplingState,
zeta: float,
psi_target: float,
layer_osc_ranges: dict[int, list[int]],
rng: Generator,
)
Mutable, validated per-step scenario hook context.
Scenario hooks are non-actuating perturbation fixtures for benchmarks and
case-study replay. They may mutate phase/frequency arrays in place or assign
updated coupling, zeta, and psi_target values. The simulation
core validates those fields immediately after the hook returns.
SimulationTwinConfidenceContext
dataclass
¶
SimulationTwinConfidenceContext(
spec_name: str,
step: int,
sample_period_s: float,
model_phases: FloatArray,
r_good: float,
r_bad: float,
regime: str,
boundary_violation_count: int,
proposed_action_count: int,
layer_order_parameters: tuple[float, ...],
)
Read-only live state handed to a twin-confidence source.
The simulation core does not know how a deployment acquires observed twin
data. A configured source receives this context, computes or retrieves a
:class:~scpn_phase_orchestrator.monitor.twin_confidence.TwinConfidenceScore,
and the conformal gate uses that score to admit or reject the current policy
tick.
Attributes¶
spec_name : str Binding-spec name being simulated. step : int Zero-based simulation step for the policy tick. sample_period_s : float Integration sample period in seconds. model_phases : FloatArray Current model phase vector after the integration step. r_good, r_bad : float Objective-layer order parameters for the current model state. regime : str Current supervisor regime label. boundary_violation_count : int Number of boundary violations on the current step. proposed_action_count : int Number of projected actions proposed before conformal admission. layer_order_parameters : tuple[float, ...] Per-layer order parameters for the current model state.
Functions:¶
petri_net_from_protocol ¶
Build a Petri net and initial marking from a protocol-net spec.
Parameters¶
protocol : ProtocolNetSpec The protocol-net specification.
Returns¶
tuple[PetriNet, Marking] The Petri net and its initial marking.
Source code in src/scpn_phase_orchestrator/runtime/simulation.py
simulate ¶
simulate(
spec: BindingSpec,
*,
steps: int = 100,
seed: int = 42,
policy_enabled: bool = True,
control_mode: SimulationControlMode = "supervisor_policy",
audit_logger: AuditLogger | None = None,
strict_audit_integrity: bool = True,
binding_spec_path: Path | None = None,
scenario_hook: ScenarioCallback | None = None,
conformal_gate: TwinConformalGate | None = None,
twin_confidence_source: TwinConfidenceSource
| None = None,
) -> SimulationResult
Advance a binding spec for steps and return the simulation outcome.
Parameters¶
spec : BindingSpec
A validated binding spec.
steps : int
Number of integration steps.
seed : int
RNG seed for the initial phases.
policy_enabled : bool
Closed-loop control feedback on (True) or open-loop baseline (False).
control_mode : {"supervisor_policy"}
The live control surface used by the generic binding-spec simulator.
Koopman MPC is intentionally not a selectable simulate mode; it
remains a review-only/offline proposal surface in
:mod:scpn_phase_orchestrator.runtime.dvoc_oscillation_damping.
audit_logger : AuditLogger | None
Optional logger; when given, the header, per-step records, and events are
written. The caller owns its lifecycle (close).
strict_audit_integrity : bool
Fail closed on a broken audit chain. When True (default) and the audit
logger owns a protobuf event stream whose close-time integrity check fails,
the run raises :class:~scpn_phase_orchestrator.exceptions.AuditError
instead of returning a result with a tamper-evident but unenforced
audit_event_stream_integrity field. Set False to attach the failed
result for inspection without failing the run.
binding_spec_path : Path | None
Optional path to the spec, used only to locate an adjacent policy.yaml. When
None, no domainpack policy rules are loaded (the supervisor policy still
runs when policy_enabled).
scenario_hook : ScenarioCallback | None
Optional deterministic per-step perturbation hook for benchmark and case-study
scenarios. The hook can mutate phases, frequencies, coupling, zeta, or
psi_target in a validated :class:SimulationScenarioContext; it cannot
perform actuation.
conformal_gate : TwinConformalGate | None
Optional calibrated conformal twin-confidence gate. When supplied with
twin_confidence_source, the gate scores every live policy tick before
actions are applied.
twin_confidence_source : TwinConfidenceSource | None
Callable that returns a twin-confidence score for the current policy tick.
It must be supplied together with conformal_gate.
Returns¶
SimulationResult
A :class:SimulationResult.
Raises¶
ValueError
If the spec declares no oscillators, an unsupported control mode, or an
incomplete conformal-admission configuration.
AuditError
If strict_audit_integrity and the audit event-stream integrity check
fails at close time (the recorded run cannot be trusted).
Source code in src/scpn_phase_orchestrator/runtime/simulation.py
363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 | |
Network security helpers¶
Shared helpers for production-mode detection, environment integer parsing, and per-identity fixed-window rate limiting.
network_security ¶
Small network-service security helpers shared by optional HTTP surfaces.
The module provides production-mode environment detection, validated non-negative integer environment parsing, and a thread-safe per-identity token-bucket rate limiter. Helpers are intentionally local and dependency-free: they do not configure servers, store credentials, or perform authentication by themselves.
Classes¶
TokenBucketRateLimiter ¶
Thread-safe per-identity token-bucket rate limiter.
Source code in src/scpn_phase_orchestrator/runtime/network_security.py
Methods:¶
allow ¶
Return True if identity has at least one available token.
Parameters¶
identity : str
Caller identity for rate limiting.
now : float | None
Current time in seconds, or None.
Returns¶
bool
True when the identity has an available token.
Raises¶
ValueError If the inputs are invalid or inconsistent.
Source code in src/scpn_phase_orchestrator/runtime/network_security.py
FixedWindowRateLimiter ¶
Bases: TokenBucketRateLimiter
Backward-compatible name for the production token-bucket limiter.
Source code in src/scpn_phase_orchestrator/runtime/network_security.py
Functions:¶
is_production_mode ¶
Return True when a service-specific or generic env profile is production.
Parameters¶
prefix : str Service-specific environment-variable prefix.
Returns¶
bool
True when the environment profile is production.
Source code in src/scpn_phase_orchestrator/runtime/network_security.py
env_int ¶
Read a non-negative integer from the environment.
Parameters¶
name : str The span or resource name. default : int Default value when the variable is unset.
Returns¶
int The non-negative integer read from the environment.
Raises¶
ValueError If the inputs are invalid or inconsistent.
Source code in src/scpn_phase_orchestrator/runtime/network_security.py
Runtime observability¶
Prometheus text metrics are a default Runtime/Serving surface, not an optional
external adapter. OpenTelemetry remains an optional backend; absent OTel packages
produce validated no-op spans while /api/metrics and local Prometheus text
export stay active.
observability ¶
Default runtime observability for SPO serving surfaces.
The runtime layer always exposes deterministic Prometheus text metrics and a validated OpenTelemetry-compatible export surface. OpenTelemetry remains an optional backend dependency; when it is absent, traces and metrics become validated no-ops rather than disabling runtime observability.
Classes¶
RuntimeMetricSnapshot
dataclass
¶
RuntimeMetricSnapshot(
upde_state: UPDEState,
regime: str,
latency_ms: float,
step_idx: int | None = None,
)
Validated runtime metrics emitted by HTTP, gRPC, and CLI surfaces.
PrometheusEvidenceSource ¶
Protocol-like base for audit-record evidence exported as metrics.
Methods:¶
to_audit_record ¶
Return JSON-safe evidence fields for Prometheus exposition.
Returns¶
Mapping[str, object] Return JSON-safe evidence fields for Prometheus exposition.
Raises¶
NotImplementedError If the operation is not implemented.
Source code in src/scpn_phase_orchestrator/runtime/observability.py
MetricsExporter ¶
Format UPDE state, regime, and latency as Prometheus text exposition.
Source code in src/scpn_phase_orchestrator/runtime/observability.py
Methods:¶
exposition_lines ¶
exposition_lines(
upde_state: UPDEState,
regime: str,
latency_ms: float,
*,
step_idx: int | None = None,
) -> list[str]
Build individual metric lines in Prometheus text format.
Parameters¶
upde_state : UPDEState
The UPDE state to record or export.
regime : str
The current control regime label.
latency_ms : float
Step latency in milliseconds.
step_idx : int | None
Zero-based simulation step index, or None.
Returns¶
list[str] The individual Prometheus metric lines.
Source code in src/scpn_phase_orchestrator/runtime/observability.py
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 | |
export ¶
export(
upde_state: UPDEState,
regime: str,
latency_ms: float,
*,
step_idx: int | None = None,
) -> str
Return full Prometheus text exposition as a single string.
Parameters¶
upde_state : UPDEState
The UPDE state to record or export.
regime : str
The current control regime label.
latency_ms : float
Step latency in milliseconds.
step_idx : int | None
Zero-based simulation step index, or None.
Returns¶
str The full Prometheus text exposition.
Source code in src/scpn_phase_orchestrator/runtime/observability.py
digital_twin_operator_evidence_lines ¶
digital_twin_operator_evidence_lines(
evidence: Mapping[str, object]
| PrometheusEvidenceSource,
) -> list[str]
Build Prometheus lines for live or replayed digital-twin evidence.
Parameters¶
evidence : Mapping[str, object] | PrometheusEvidenceSource Live or replayed digital-twin operator evidence.
Returns¶
list[str] The Prometheus lines for the digital-twin evidence.
Source code in src/scpn_phase_orchestrator/runtime/observability.py
404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 | |
export_digital_twin_operator_evidence ¶
export_digital_twin_operator_evidence(
evidence: Mapping[str, object]
| PrometheusEvidenceSource,
) -> str
Return Prometheus text for digital-twin operator evidence.
Parameters¶
evidence : Mapping[str, object] | PrometheusEvidenceSource Live or replayed digital-twin operator evidence.
Returns¶
str The Prometheus text for the digital-twin operator evidence.
Source code in src/scpn_phase_orchestrator/runtime/observability.py
export_twin_confidence ¶
Return Prometheus text for a twin-confidence operator summary.
Parameters¶
summary : TwinConfidenceSummary The operator-facing aggregate over scored twin-confidence ticks.
Returns¶
str Prometheus exposition text using this exporter's metric prefix.
Source code in src/scpn_phase_orchestrator/runtime/observability.py
OTelExporter ¶
Instrument UPDE steps with OpenTelemetry spans and metrics.
Falls back to validated no-op behaviour when opentelemetry-api is not
installed, so runtime observability remains enabled by default without
making OpenTelemetry a base dependency.
Source code in src/scpn_phase_orchestrator/runtime/observability.py
Attributes¶
enabled
property
¶
True when opentelemetry-api is installed and active.
Returns¶
bool True when opentelemetry-api is installed and active.
Methods:¶
span ¶
Trace span context manager. No-op when OTel is absent.
Parameters¶
name : str
The span or resource name.
attributes : Mapping[str, object] | None
Optional span attributes, or None.
Returns¶
Generator[Any, None, None] A trace-span context manager (no-op without OpenTelemetry).
Source code in src/scpn_phase_orchestrator/runtime/observability.py
record_step ¶
Record metrics from a completed UPDE step.
Parameters¶
upde_state : UPDEState
The UPDE state to record or export.
step_idx : int
Zero-based simulation step index, or None.
Source code in src/scpn_phase_orchestrator/runtime/observability.py
record_regime_change ¶
Emit a span event for regime transitions.
Parameters¶
old : str The previous regime label. new : str The new regime label.
Source code in src/scpn_phase_orchestrator/runtime/observability.py
RuntimeObservability ¶
RuntimeObservability(
*,
service_name: str = "spo",
metric_prefix: str = "spo",
otel_exporter: OTelExporter | None = None,
)
Default observability facade used by runtime serving surfaces.
Source code in src/scpn_phase_orchestrator/runtime/observability.py
Attributes¶
otel_enabled
property
¶
True when OpenTelemetry is installed and active.
Returns¶
bool True when OpenTelemetry is installed and active.
Methods:¶
prometheus_text ¶
Return default Prometheus text for a runtime metric snapshot.
Parameters¶
snapshot : RuntimeMetricSnapshot The runtime metric snapshot.
Returns¶
str The default Prometheus text for the snapshot.
Source code in src/scpn_phase_orchestrator/runtime/observability.py
digital_twin_prometheus_text ¶
Return Prometheus text for live or replayed digital-twin evidence.
Parameters¶
evidence : Mapping[str, object] | PrometheusEvidenceSource Live or replayed digital-twin operator evidence.
Returns¶
str The Prometheus text for the digital-twin evidence.
Source code in src/scpn_phase_orchestrator/runtime/observability.py
twin_confidence_prometheus_text ¶
Return Prometheus text for a twin-confidence operator summary.
Parameters¶
summary : TwinConfidenceSummary The operator-facing aggregate over scored twin-confidence ticks.
Returns¶
str Prometheus exposition text for the twin-confidence summary.
Source code in src/scpn_phase_orchestrator/runtime/observability.py
record_step ¶
Record a runtime step through the optional OpenTelemetry backend.
Parameters¶
snapshot : RuntimeMetricSnapshot The runtime metric snapshot.
Source code in src/scpn_phase_orchestrator/runtime/observability.py
span ¶
Return a validated runtime span, no-op without OpenTelemetry.
Parameters¶
name : str
The span or resource name.
attributes : Mapping[str, object] | None
Optional span attributes, or None.
Returns¶
Generator[Any, None, None] A validated runtime span (no-op without OpenTelemetry).
Source code in src/scpn_phase_orchestrator/runtime/observability.py
Web and gRPC services¶
The service modules expose the FastAPI dashboard state container and the gRPC servicer. Optional web or gRPC dependencies are handled at import time so documentation builds can inspect the public surface without launching servers.
server ¶
FastAPI server for real-time simulation monitoring.
Usage
from scpn_phase_orchestrator.runtime.server import create_app app = create_app("domainpacks/minimal_domain/binding_spec.yaml")
Save that object in an ASGI module, then run it with an ASGI server such as
uvicorn app:app --host 127.0.0.1 --port 8080.
Production profile requires API key authentication and rate limiting:¶
SPO_ENV=production SPO_API_KEY=
Mutable endpoints (step, reset) require X-API-Key header.¶
Development mode remains local by default; do not deploy it externally.¶
Endpoints
GET / HTML dashboard GET /api/state Current UPDE state (JSON) GET /api/studio-feed Live STUDIO feed envelope (JSON) POST /api/step Advance one step (auth required if SPO_API_KEY set) POST /api/reset Reset simulation (auth required if SPO_API_KEY set) GET /api/config Binding spec summary WS /ws/stream Real-time WebSocket stream (read-only observer)
Classes¶
SimulationState ¶
Mutable simulation state shared across API endpoints.
Source code in src/scpn_phase_orchestrator/runtime/server.py
Methods:¶
step ¶
Advance one timestep, return state snapshot.
Returns¶
dict[str, Any] Advance one timestep, return state snapshot.
Source code in src/scpn_phase_orchestrator/runtime/server.py
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 | |
snapshot ¶
Return the current state as a JSON-serialisable dict.
Returns¶
dict[str, Any] Return the current state as a JSON-serialisable dict.
Source code in src/scpn_phase_orchestrator/runtime/server.py
studio_feed ¶
Return the live STUDIO feed for the current simulation snapshot.
Returns¶
dict[str, object]
studio.control-feed.v1 envelope plus SPO runtime snapshot.
Source code in src/scpn_phase_orchestrator/runtime/server.py
reset ¶
Reset to initial state.
Returns¶
dict[str, Any] Reset to initial state.
Source code in src/scpn_phase_orchestrator/runtime/server.py
Functions:¶
create_app ¶
Create FastAPI app for the given binding spec.
Parameters¶
spec_path : str | Path Filesystem path to the binding-spec file.
Returns¶
object The configured FastAPI application.
Raises¶
RuntimeError If the runtime operation fails. ImportError If a required optional dependency is not installed. HTTPException If the request is invalid.
Source code in src/scpn_phase_orchestrator/runtime/server.py
424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 | |
server_grpc ¶
gRPC service implementing the full PhaseOrchestrator API.
Install grpcio to run a live server::
pip install grpcio grpcio-tools
Without grpcio the servicer still works for in-process testing with a mocked context.
Classes¶
PhaseStreamServicer ¶
Bases: PhaseOrchestratorServicer
gRPC servicer that exposes state, step, reset, streaming, and config.
Wraps a SimulationState (from server.py) and translates its
dict snapshots into proto-compatible StateResponse messages.
Source code in src/scpn_phase_orchestrator/runtime/server_grpc.py
Methods:¶
GetState ¶
GRPC unary RPC: return current simulation state.
Parameters¶
request : Any The gRPC request message. context : Any The gRPC servicer context.
Returns¶
StateResponse The current simulation-state response.
Source code in src/scpn_phase_orchestrator/runtime/server_grpc.py
Step ¶
GRPC unary RPC: advance simulation by n_steps and return state.
Parameters¶
request : Any The gRPC request message. context : Any The gRPC servicer context.
Returns¶
StateResponse The simulation-state response after advancing.
Source code in src/scpn_phase_orchestrator/runtime/server_grpc.py
Reset ¶
GRPC unary RPC: reset simulation and return fresh state.
Parameters¶
request : Any The gRPC request message. context : Any The gRPC servicer context.
Returns¶
StateResponse The fresh simulation-state response after reset.
Source code in src/scpn_phase_orchestrator/runtime/server_grpc.py
GetConfig ¶
GRPC unary RPC: return engine configuration.
Parameters¶
request : Any The gRPC request message. context : Any The gRPC servicer context.
Returns¶
ConfigResponse The engine-configuration response.
Source code in src/scpn_phase_orchestrator/runtime/server_grpc.py
StreamPhases ¶
Read-only observer: streams snapshots without advancing simulation.
Parameters¶
request : Any The gRPC request message. context : Any The gRPC servicer context.
Returns¶
Iterator[StateResponse] An iterator of state-response snapshots.
Source code in src/scpn_phase_orchestrator/runtime/server_grpc.py
Functions:¶
Optional dependency detection¶
_compat.py exports two values:
TWO_PI— 2π as float64HAS_RUST—Truewhenspo_kernelRust extension is importable
Individual modules handle their own optional imports locally:
| Module | Guard | Package |
|---|---|---|
nn/ |
pytest.importorskip("jax") |
jax, equinox, optax |
reporting/plots.py |
_HAS_MPL |
matplotlib |
ssgf/topological_integration.py |
_HAS_RIPSER |
ripser |
upde/engine.py |
_HAS_RUST (from _compat) |
spo_kernel |
coupling/knm.py |
_HAS_RUST (from _compat) |
spo_kernel |
oscillators/physical.py |
_HAS_RUST (from _compat) |
spo_kernel |
When an optional dependency is missing, the corresponding subsystem
either skips the optimised path (Rust → Python fallback) or raises
ImportError with install instructions.
Environment readiness¶
runtime.doctor aggregates the per-module detection above into one report
behind the spo doctor command. It probes the interpreter version against
requires-python, the required runtime dependencies, the optional native
backends (Rust/Julia/Go/Mojo), the optional feature extras, and package-local
review/export adapter surfaces such as FMI co-simulation and hybrid
co-compilation, returning a DoctorReport whose status is pass only when the
interpreter is in range and every required dependency is importable. See the
CLI reference for the command and exit codes.
doctor ¶
Runtime environment readiness checks for spo doctor.
This module answers one question deterministically: can this interpreter run SCPN Phase Orchestrator, and which optional accelerators and feature extras are usable right now? It probes three layers without importing heavy optional packages or touching the network:
- the interpreter version against the packaged
requires-pythonwindow; - the mandatory runtime dependencies (NumPy, SciPy, PyYAML, Click, protobuf, urllib3) that the core engine imports unconditionally;
- the optional native compute backends (Rust
spo_kernel, Juliajuliacall, the Go toolchain/shared libraries, the Mojo toolchain) and the optional feature extras (nn,studio,queuewaves,plot,otel,notebook). - opt-in review/export adapter surfaces that should ship with the package, such as the FMI co-simulation export and hybrid co-compiler manifest helpers.
Detection uses :func:importlib.util.find_spec for Python modules (which
locates a distribution without executing its top-level import) and
:func:shutil.which / filesystem probes for external toolchains, so running the
diagnostics is cheap and free of side effects. A missing required dependency
or an out-of-range interpreter makes the overall status fail (non-zero exit
for the CLI); missing optional components are reported as warn and never
fail the run.
Classes¶
DependencyCheck
dataclass
¶
DependencyCheck(
name: str,
category: str,
required: bool,
available: bool,
detail: str,
version: str | None = None,
)
Outcome of probing a single dependency, backend, or toolchain.
Attributes¶
name: Human-facing component name (for example ``numpy`` or ``rust``).
category: Grouping used for rendering — ``interpreter``, ``core``,
``backend``, or one of the optional feature-extra names.
required: Whether the component is mandatory for the core engine.
available: Whether the component was detected as usable.
detail: Short human-readable explanation (version string, path,
install hint, or reason it is unavailable).
version: Resolved distribution version when known, otherwise ``None``.
Attributes¶
status
property
¶
Return ok/missing/warn for the component detection state.
Returns¶
str
Return ok/missing/warn for the component detection state.
Methods:¶
to_record ¶
Return a JSON-serialisable mapping with deterministic key order.
Returns¶
dict[str, object] Return a JSON-serialisable mapping with deterministic key order.
Source code in src/scpn_phase_orchestrator/runtime/doctor.py
DoctorReport
dataclass
¶
Aggregate readiness report produced by :func:run_environment_diagnostics.
Attributes¶
checks: Every :class:`DependencyCheck` in deterministic display order.
python_version: The running interpreter version (``X.Y.Z``).
platform: Short OS/architecture descriptor for the audit record.
Attributes¶
missing_required
property
¶
Required components that were not detected.
Returns¶
tuple[DependencyCheck, ...] Required components that were not detected.
missing_optional
property
¶
Optional components that were not detected.
Returns¶
tuple[DependencyCheck, ...] Optional components that were not detected.
ok
property
¶
True when every required component is present (overall pass).
Returns¶
bool
True when every required component is present (overall pass).
status
property
¶
exit_code
property
¶
Process exit code: 0 when ready, 1 when a requirement is missing.
Returns¶
int
Process exit code: 0 when ready, 1 when a requirement is missing.
Methods:¶
to_audit_record ¶
Return a deterministic JSON-serialisable readiness record.
Returns¶
dict[str, object] Return a deterministic JSON-serialisable readiness record.
Source code in src/scpn_phase_orchestrator/runtime/doctor.py
Functions:¶
run_environment_diagnostics ¶
Probe the interpreter, required dependencies, and optional components.
Parameters¶
repo_root : Path | None
Optional explicit checkout root for the Go shared-library probe. When None
the root is auto-detected from this module's location; pass a path in tests to
exercise both branches.
Returns¶
DoctorReport
A :class:DoctorReport whose :attr:DoctorReport.status is pass only when
the interpreter is in range and every required dependency is importable.
Source code in src/scpn_phase_orchestrator/runtime/doctor.py
render_report ¶
Render a :class:DoctorReport as aligned human-readable lines.
Source code in src/scpn_phase_orchestrator/runtime/doctor.py
Chaos-engineering resilience injection¶
runtime.chaos injects realistic, non-actuating faults — coupling drops,
frequency drift, sensor noise, and drive dropout — into a controlled simulation
and measures how the orchestrator recovers. A ChaosSchedule of ChaosFault
windows is applied through the simulation's scenario_hook boundary, so the
heavy compute stays in the existing multi-language UPDE engine and this module is
the orchestration and scoring layer. run_resilience_experiment runs the spec
once nominally and once perturbed under the same seed, then compute_resilience
derives recovery time, peak coherence drop, stability-margin erosion, and final
deviation. The spo chaos command exposes this from the CLI; all runs are
review-only.
chaos ¶
Chaos-engineering resilience injection for orchestrated phase control.
This module injects realistic, non-actuating perturbations — coupling drops,
frequency drift, sensor noise, and drive dropout — into a controlled simulation
of a binding spec, then measures how the orchestrator recovers. Faults are
applied through the simulation's scenario_hook boundary, so the heavy compute
stays in the existing multi-language UPDE engine; this module is the
orchestration and resilience-scoring layer on top of it.
A resilience experiment runs the same seeded spec twice — once nominal, once with the fault schedule — and compares the two order-parameter trajectories to derive recovery time, peak coherence drop, stability-margin erosion, and final deviation. The output is review-only evidence: nothing here actuates hardware.
Classes¶
ChaosFault
dataclass
¶
One injected fault active over a bounded step window.
Attributes¶
kind : str
Fault type: "coupling_drop" (scale the coupling matrix down),
"frequency_drift" (offset the natural frequencies),
"sensor_noise" (Gaussian phase perturbation), or "drive_dropout"
(attenuate the external drive zeta).
start_step : int
First step at which the fault is active (>= 1 so step 0 captures the
nominal coupling reference).
duration_steps : int
Number of consecutive steps the fault stays active.
magnitude : float
Fault strength. For coupling_drop and drive_dropout it is a
fraction in [0, 1]; for frequency_drift it is an additive offset
in rad/s; for sensor_noise it is the noise standard deviation in rad.
Attributes¶
end_step
property
¶
Methods:¶
active_at ¶
to_audit_record ¶
Return a JSON-safe audit mapping of the fault.
Returns¶
dict[str, object] Deterministic, JSON-safe mapping of the fault fields.
Source code in src/scpn_phase_orchestrator/runtime/chaos.py
ChaosSchedule
dataclass
¶
An ordered set of faults applied during a resilience experiment.
Attributes¶
faults : tuple[ChaosFault, ...] The faults to inject; at least one is required.
Attributes¶
last_fault_end
property
¶
Return the last step at which any fault is still active, plus one.
Returns¶
int
The maximum end_step across the scheduled faults.
Methods:¶
to_audit_record ¶
Return a JSON-safe audit mapping of the schedule.
Returns¶
dict[str, object] Deterministic, JSON-safe mapping with the fault list and window end.
Source code in src/scpn_phase_orchestrator/runtime/chaos.py
ResilienceMetrics
dataclass
¶
ResilienceMetrics(
recovered: bool,
recovery_steps: int | None,
max_coherence_drop: float,
stability_margin_erosion: float,
final_deviation: float,
metrics_hash: str,
)
Resilience evidence derived from nominal vs perturbed R trajectories.
Attributes¶
recovered : bool
Whether the perturbed run returned within recovery_tolerance of the
nominal run after the last fault ended.
recovery_steps : int | None
Steps after the last fault end until recovery, or None if the run
never recovered within the trajectory.
max_coherence_drop : float
Largest positive nominal_R - perturbed_R across the trajectory.
stability_margin_erosion : float
Mean absolute nominal_R - perturbed_R over the post-fault-onset window.
final_deviation : float
Absolute difference of the final order parameters.
metrics_hash : str
Deterministic SHA-256 over the audit record (excluding the hash).
Methods:¶
to_audit_record ¶
Return a JSON-safe audit mapping of the metrics.
Returns¶
dict[str, object] Deterministic, JSON-safe mapping of every metric field.
Source code in src/scpn_phase_orchestrator/runtime/chaos.py
ChaosExperimentResult
dataclass
¶
ChaosExperimentResult(
spec_name: str,
steps: int,
seed: int,
schedule: ChaosSchedule,
metrics: ResilienceMetrics,
nominal_final_r: float,
perturbed_final_r: float,
result_hash: str,
)
Full result of one resilience experiment.
Attributes¶
spec_name : str Name of the binding spec exercised. steps : int Number of simulation steps. seed : int Shared RNG seed for the nominal and perturbed runs. schedule : ChaosSchedule The injected fault schedule. metrics : ResilienceMetrics The derived resilience evidence. nominal_final_r : float Final objective order parameter of the nominal run. perturbed_final_r : float Final objective order parameter of the perturbed run. result_hash : str Deterministic SHA-256 over the audit record (excluding the hash).
Methods:¶
to_audit_record ¶
Return a JSON-safe audit mapping of the experiment.
Returns¶
dict[str, object] Deterministic, JSON-safe mapping including schedule and metrics.
Source code in src/scpn_phase_orchestrator/runtime/chaos.py
Functions:¶
make_chaos_hook ¶
Build a non-actuating scenario hook that injects the fault schedule.
The hook captures the nominal coupling matrix at step 0 (faults start at
step >= 1) so that coupling_drop faults scale relative to the unperturbed
coupling rather than compounding across the fault window.
Parameters¶
schedule : ChaosSchedule The fault schedule to apply.
Returns¶
ScenarioCallback
A callable suitable for simulate(..., scenario_hook=...).
Source code in src/scpn_phase_orchestrator/runtime/chaos.py
compute_resilience ¶
compute_resilience(
nominal_history: tuple[float, ...],
perturbed_history: tuple[float, ...],
*,
fault_onset_step: int,
last_fault_end: int,
recovery_tolerance: float,
) -> ResilienceMetrics
Score resilience from nominal vs perturbed order-parameter trajectories.
Parameters¶
nominal_history, perturbed_history : tuple[float, ...]
Per-step objective order parameters of the nominal and perturbed runs;
they must share length.
fault_onset_step : int
First step at which any fault becomes active; the erosion window starts
here.
last_fault_end : int
First step after the last fault; recovery is measured from here.
recovery_tolerance : float
Absolute |nominal_R - perturbed_R| at or below which the perturbed
run counts as recovered.
Returns¶
ResilienceMetrics The derived resilience evidence with a deterministic hash.
Raises¶
ValueError If the histories are empty, length-mismatched, or the parameters are out of range.
Source code in src/scpn_phase_orchestrator/runtime/chaos.py
run_resilience_experiment ¶
run_resilience_experiment(
spec: BindingSpec,
schedule: ChaosSchedule,
*,
steps: int = 200,
seed: int = 42,
recovery_tolerance: float = 0.05,
) -> ChaosExperimentResult
Run a nominal and a fault-injected simulation and score resilience.
Both runs share the spec, step count, and seed, so the only difference is the injected fault schedule. The simulations are closed-loop (the supervisor reacts to the faults); the result is review-only evidence.
Parameters¶
spec : BindingSpec
A validated binding spec.
schedule : ChaosSchedule
The fault schedule to inject in the perturbed run.
steps : int, optional
Number of simulation steps (default 200); must exceed the last fault
end so recovery can be observed.
seed : int, optional
Shared RNG seed (default 42).
recovery_tolerance : float, optional
Recovery tolerance passed to :func:compute_resilience (default 0.05).
Returns¶
ChaosExperimentResult The schedule, derived metrics, and final order parameters.
Raises¶
ValueError
If steps does not exceed the last fault end.
Source code in src/scpn_phase_orchestrator/runtime/chaos.py
Deterministic (bounded-jitter) execution mode¶
runtime.deterministic runs an arbitrary per-step callable against a fixed
period with the timing guarantees a plain loop cannot give: every step is
scheduled at t0 + i·period on the monotonic clock (sleep, with an optional
final spin, then measured jitter), each step is timed against a worst-case
execution-time budget so an overrun is a fatal deadline miss by default
(miss_policy='abort'). Callers must explicitly choose miss_policy='observe'
for diagnostic runs that record misses and continue. The cyclic garbage
collector is frozen and disabled for the hot path so GC pauses leave the jitter
budget.
run_deterministic_loop returns an ExecutionTimingReport with per-step
latencies and jitters plus aggregate statistics (mean / max / p99 latency, max
absolute jitter, deadline-miss count). The loop is timing-only and
non-actuating: it never inspects or mutates the step's state, so it drives the
simulation step, a controller tick, or any periodic task without coupling to a
specific engine. Advanced callers may pass a monotonic clock_ns and matching
wait_until hook for deterministic simulator clocks; production calls default
to time.perf_counter_ns and the built-in sleep/spin waiter.
deterministic ¶
Bounded-jitter, hard-deadline execution mode for the control step loop.
A control loop only earns credibility if each step lands on time. This module
runs an arbitrary per-step callable against a fixed period with three guarantees
a plain for loop cannot give:
- Bounded jitter — every step is scheduled at
t0 + i·periodon the monotonic clock; the loop sleeps to that boundary (optionally finishing the lastbusy_wait_margin_swith a spin) and records the actual start offset so jitter is measured, not assumed. - WCET budget — each step is timed against a worst-case execution-time
budget; an overrun is a deadline miss. The default
miss_policy='abort'raises :class:DeadlineExceededError; callers must explicitly requestmiss_policy='observe'to record and continue. - No-GC hot path — the cyclic garbage collector is frozen and disabled for the duration of the loop, removing GC pauses from the jitter budget, and restored to its prior state afterwards.
The loop is non-actuating and timing-only: it never inspects or mutates the
step's state. The caller closes over its own state in the step callable, so
this drives the simulation step, a controller tick, or any periodic task without
coupling to a specific engine. Step results stay exactly as deterministic as
the callable; this module makes their timing bounded, which is the property
hard-real-time control needs.
Classes¶
DeadlineExceededError ¶
Bases: RuntimeError
Raised when a step overruns its WCET budget under miss_policy='abort'.
Attributes¶
step_index : int The zero-based index of the step that overran. latency_s : float The measured execution time of that step, in seconds. wcet_s : float The worst-case execution-time budget that was exceeded, in seconds.
Source code in src/scpn_phase_orchestrator/runtime/deterministic.py
DeadlineBudget
dataclass
¶
DeadlineBudget(
period_s: float,
wcet_s: float | None = None,
miss_policy: str = "abort",
freeze_gc: bool = True,
busy_wait_margin_s: float = 0.0,
)
Timing budget for a bounded-jitter step loop.
Attributes¶
period_s : float
Target wall-clock period between consecutive step starts, in seconds.
wcet_s : float
Worst-case execution-time budget for a single step, in seconds. A step
whose measured latency exceeds this is a deadline miss. Defaults to the
full period_s (a step may use the whole period).
miss_policy : str
'abort' raises :class:DeadlineExceededError on the first miss.
'observe' records deadline misses and continues, and must be
selected explicitly for diagnostic runs.
freeze_gc : bool
Freeze (gc.freeze) and disable the cyclic garbage collector for the
loop, restoring the prior state afterwards. Removes GC pauses from the
jitter budget.
busy_wait_margin_s : float
Spin (busy-wait) for the final busy_wait_margin_s before each
scheduled boundary instead of sleeping, trading CPU for lower jitter.
0.0 (default) sleeps the whole remainder.
ExecutionTimingReport
dataclass
¶
ExecutionTimingReport(
latencies_s: FloatArray,
jitters_s: FloatArray,
period_s: float,
wcet_s: float,
deadline_misses: int,
gc_frozen: bool,
wall_time_s: float,
)
Timing record of a bounded-jitter step loop.
Attributes¶
latencies_s : FloatArray
Per-step execution time, shape (steps,).
jitters_s : FloatArray
Per-step start offset from the scheduled boundary (signed; positive is
late), shape (steps,).
period_s : float
The target period.
wcet_s : float
The effective WCET budget against which misses were counted.
deadline_misses : int
Number of steps whose latency exceeded wcet_s.
gc_frozen : bool
Whether the garbage collector was frozen/disabled for the loop.
wall_time_s : float
Total wall-clock time of the loop.
Attributes¶
max_abs_jitter_s
property
¶
Largest absolute start-offset from the scheduled boundary.
Methods:¶
latency_percentile_s ¶
Return the step latency at a given percentile.
Parameters¶
percentile : float
Percentile in [0, 100].
Returns¶
float
The latency at percentile in seconds, or 0.0 if no steps ran.
Raises¶
ValueError
If percentile is not a real number in [0, 100].
Source code in src/scpn_phase_orchestrator/runtime/deterministic.py
summary ¶
Return a flat scalar summary for logging or metric export.
Returns¶
dict[str, float | int | bool] Step count, the period and WCET budget, mean / max / p99 latency, maximum absolute jitter, deadline-miss count and verdict, the GC policy, and the loop wall-time.
Source code in src/scpn_phase_orchestrator/runtime/deterministic.py
Functions:¶
run_deterministic_loop ¶
run_deterministic_loop(
step: StepCallable,
*,
steps: int,
budget: DeadlineBudget,
clock_ns: _MonotonicClockNs | None = None,
wait_until: _WaitUntilNs | None = None,
) -> ExecutionTimingReport
Drive step for steps iterations under a bounded-jitter budget.
Parameters¶
step : StepCallable
Per-step callable receiving the zero-based step index. It owns all state
through its closure; its return value is ignored.
steps : int
Number of iterations (>= 0).
budget : DeadlineBudget
The period, WCET budget, miss policy, GC policy, and spin margin.
clock_ns : Callable[[], int], optional
Monotonic nanosecond clock. Defaults to time.perf_counter_ns.
Supplying this with wait_until lets tests and deterministic
simulators exercise scheduling branches without depending on host wall
time.
wait_until : Callable[[int, int], None], optional
Wait hook receiving the target monotonic nanosecond timestamp and busy
wait margin. Defaults to the production sleep/spin waiter. Custom hooks
must observe the same time base as clock_ns.
Returns¶
ExecutionTimingReport Per-step latencies and jitters plus aggregate timing statistics.
Raises¶
ValueError
If steps is not a non-negative integer.
DeadlineExceededError
If a step overruns budget.wcet_s and miss_policy == 'abort'.
Source code in src/scpn_phase_orchestrator/runtime/deterministic.py
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 | |
Post-quantum audit-chain seal¶
runtime.audit_pqc adds an additive, post-quantum seal over the audit hash
chain. The audit logger already chains each record with SHA-256 and signs it
with HMAC; HMAC is symmetric and not post-quantum. seal_audit_log signs the
chain tip (the _hash of the last record — the SHA-256 commitment to the whole
log) with ML-DSA (FIPS 204, available natively in cryptography), producing
an AuditChainSeal that anyone holding the trusted public key can verify, long
after the run and against a future quantum adversary. verify_audit_log_seal
re-reads the chain tip and record count and rejects the seal if either changed,
so any post-hoc edit is detected. ML-DSA-65 is the default; ML-DSA-44/87 are
selectable, and the seal records its algorithm so SLH-DSA (FIPS 205) can be
added later without breaking existing seals. The seal is additive — it does not
touch the HMAC flow — so it carries no regression risk.
audit_pqc ¶
Post-quantum seal over the audit hash chain.
The audit logger already chains every record with SHA-256 and signs each one with HMAC. HMAC is symmetric: a verifier needs the secret key, and the scheme is not post-quantum. This module adds an additive second seal — it does not touch the HMAC flow, so there is no regression risk — that commits to the whole log with a post-quantum, publicly verifiable signature.
The chain tip (_hash of the last record) is the SHA-256 commitment to the
entire log: change any record and the tip changes. seal_audit_log signs that
tip with ML-DSA (FIPS 204, the NIST module-lattice signature standard),
producing an :class:AuditChainSeal that anyone holding the trusted public key
can verify long after the run — and against a future quantum adversary.
ML-DSA-65 (NIST security category 3) is the default; ML-DSA-44 and ML-DSA-87 are available for lower/higher assurance. FIPS 205 (SLH-DSA / SPHINCS+) is the hash-based alternative; the seal records its algorithm so a second scheme can be added without breaking existing seals.
ML-DSA is provided by the optional cryptography dependency. Install the
pqc extra (pip install scpn-phase-orchestrator[pqc]) to use this module;
cryptography is imported lazily, so importing the module never requires it.
ML-DSA additionally needs an OpenSSL 3.5+ backend, which not every platform wheel
bundles (e.g. the Windows cryptography wheel); on an older backend the seal
functions raise cryptography.exceptions.UnsupportedAlgorithm.
The seal is signed over a domain-separated message binding the algorithm, the record count, and the tip hash, so a signature cannot be replayed across schemes or truncated logs.
Classes¶
AuditChainSeal
dataclass
¶
AuditChainSeal(
algorithm: str,
public_key_id: str,
public_key_hex: str,
tip_hash: str,
record_count: int,
signature_hex: str,
)
A post-quantum signature committing to an audit hash chain.
Attributes¶
algorithm : str
The ML-DSA variant used (one of :data:MLDSA_VARIANTS).
public_key_id : str
Short identifier of the public key (SHA-256 prefix).
public_key_hex : str
The raw ML-DSA public key, hex-encoded, for verification.
tip_hash : str
The SHA-256 chain tip (_hash of the last record) that is sealed.
record_count : int
Number of records the chain contained when sealed.
signature_hex : str
The ML-DSA signature over the domain-separated seal message, hex-encoded.
Methods:¶
to_dict ¶
Return a JSON-serialisable mapping of the seal.
Returns¶
dict[str, str | int] The six seal fields as plain JSON-serialisable values.
Source code in src/scpn_phase_orchestrator/runtime/audit_pqc.py
from_dict
classmethod
¶
Return a seal parsed from a mapping (e.g. loaded JSON).
Parameters¶
data : dict[str, Any] A mapping carrying the six seal fields.
Returns¶
AuditChainSeal The reconstructed seal.
Raises¶
ValueError If any required field is missing.
Source code in src/scpn_phase_orchestrator/runtime/audit_pqc.py
Functions:¶
generate_signing_seed ¶
Return a fresh ML-DSA signing seed.
Returns¶
str A cryptographically random 32-byte seed, hex-encoded.
signing_key_from_seed ¶
Return a deterministic ML-DSA private key derived from a seed.
Parameters¶
seed_hex : str
A 32-byte seed, hex-encoded (see :func:generate_signing_seed).
algorithm : str
The ML-DSA variant (one of :data:MLDSA_VARIANTS).
Returns¶
cryptography ML-DSA private key The deterministic private key for the seed and variant.
Raises¶
ValueError If the seed or algorithm is invalid.
Source code in src/scpn_phase_orchestrator/runtime/audit_pqc.py
sign_bytes ¶
Sign an arbitrary message with an ML-DSA private key.
This is the generic post-quantum signing primitive that higher-level sealers
(the audit-chain seal, the DSSE attestation envelope) build on, so the
cryptography backend is loaded in exactly one place.
Parameters¶
message : bytes
The raw message to sign (already domain-separated by the caller).
private_key : MLDSA private key
An ML-DSA private key matching algorithm.
algorithm : str
The ML-DSA variant; must match private_key.
Returns¶
bytes The raw ML-DSA signature.
Raises¶
ValueError If the algorithm, message, or private key is invalid.
Source code in src/scpn_phase_orchestrator/runtime/audit_pqc.py
verify_bytes ¶
verify_bytes(
message: bytes,
signature: bytes,
trusted_public_key_hex: str,
*,
algorithm: str = DEFAULT_VARIANT,
) -> bool
Verify an ML-DSA signature over a message against a trusted public key.
Parameters¶
message : bytes The raw message the signature is expected to cover. signature : bytes The raw ML-DSA signature. trusted_public_key_hex : str The hex-encoded raw ML-DSA public key the verifier trusts. algorithm : str The ML-DSA variant to verify under.
Returns¶
bool
True if the signature is valid for the trusted key, else False.
Raises¶
ValueError If the algorithm is unknown, the message or signature is not raw bytes, or the trusted key is not a hex string.
Source code in src/scpn_phase_orchestrator/runtime/audit_pqc.py
public_key_id ¶
Return the short identifier of a public key.
Parameters¶
public_bytes : bytes The raw ML-DSA public key bytes.
Returns¶
str The first 16 hex characters of the key's SHA-256 digest.
Raises¶
ValueError
If public_bytes is not raw bytes.
Source code in src/scpn_phase_orchestrator/runtime/audit_pqc.py
seal_audit_chain ¶
seal_audit_chain(
tip_hash: str,
record_count: int,
private_key: Any,
*,
algorithm: str = DEFAULT_VARIANT,
) -> AuditChainSeal
Sign an audit chain tip with ML-DSA and return the seal.
Parameters¶
tip_hash : str
The SHA-256 chain tip (32-byte digest, hex).
record_count : int
Number of records in the sealed chain.
private_key : MLDSA private key
An ML-DSA private key matching algorithm (see
:func:signing_key_from_seed).
algorithm : str
The ML-DSA variant; must match private_key.
Returns¶
AuditChainSeal The post-quantum seal over the chain tip.
Raises¶
ValueError If the algorithm, tip hash, record count, or private key is invalid.
Source code in src/scpn_phase_orchestrator/runtime/audit_pqc.py
verify_audit_chain_seal ¶
Verify a seal's signature against a trusted public key.
The verifier must supply the public key it trusts; the key embedded in the seal is only used as a convenience and is checked to match the trusted one, so an attacker cannot re-sign a forged log under their own key.
Parameters¶
seal : AuditChainSeal The seal to verify. trusted_public_key_hex : str The hex-encoded raw ML-DSA public key the verifier trusts.
Returns¶
bool
True if the signature is valid for the trusted key, else False.
Raises¶
ValueError If the seal's algorithm is unknown or the trusted key is malformed.
Source code in src/scpn_phase_orchestrator/runtime/audit_pqc.py
read_audit_chain_tip ¶
Return the chain tip _hash and record count of an audit JSONL file.
Parameters¶
path : Path Path to the audit JSONL stream.
Returns¶
tuple[str, int]
(tip_hash, record_count).
Raises¶
ValueError
If the file is empty or the last record carries no _hash.
FileNotFoundError
If the file does not exist.
Source code in src/scpn_phase_orchestrator/runtime/audit_pqc.py
seal_audit_log ¶
seal_audit_log(
path: Path,
private_key: Any,
*,
algorithm: str = DEFAULT_VARIANT,
) -> AuditChainSeal
Read an audit JSONL file's chain tip and return a post-quantum seal.
Parameters¶
path : Path
Path to the audit JSONL stream to seal.
private_key : cryptography ML-DSA private key
The signing key (see :func:signing_key_from_seed).
algorithm : str
The ML-DSA variant; must match private_key.
Returns¶
AuditChainSeal The post-quantum seal over the file's chain tip.
Source code in src/scpn_phase_orchestrator/runtime/audit_pqc.py
verify_audit_log_seal ¶
Verify a seal against an audit file and a trusted public key.
Parameters¶
path : Path Path to the audit JSONL stream to check. seal : AuditChainSeal The seal previously produced for this log. trusted_public_key_hex : str The hex-encoded raw ML-DSA public key the verifier trusts.
Returns¶
bool
True only if the file's current chain tip and record count match the
seal and the signature verifies under the trusted key, so the seal is
rejected if the log was altered after sealing.
Source code in src/scpn_phase_orchestrator/runtime/audit_pqc.py
Error handling philosophy¶
SPO follows a fail-fast strategy at system boundaries:
- Input validation:
ValueErrorfor invalid shapes, NaN, etc. - Engine divergence:
EngineErrorwith step number and divergence magnitude - Binding errors:
BindingErrorwith field path and expected type - Audit tampering:
AuditErrorwith chain break location
Internal code trusts validated data — no redundant checks on hot paths. This keeps engine step latency under 1 ms for N ≤ 64.
Role in the production boundary¶
This module is the project-wide seam between raw exceptions and deterministic control outcomes. Every subsystem raises typed SPO errors into the same hierarchy, so operational handlers can route failures consistently across CLI, services, and embedded drivers.
In practice this gives operators a bounded failure vocabulary:
- Validation faults are recoverable through corrected input or fallback profiles.
- Engine faults can trigger deterministic supervisor actions.
- Audit faults stop replay promotion by default to protect chain integrity.
Why optional dependency handling matters¶
The documented optional import and HAS_* flags are part of reliability policy, not
just packaging convenience. They prevent import-time crashes when one runtime path is
unavailable, while keeping production-facing Python behavior explicit:
- Required behaviour remains available on the baseline path.
- Optional accelerators are additive when present.
- Audit and CLI surfaces can still start on minimal environments.
This pattern prevents “one package drift takes down all controls” incidents by making capability and observability dependencies explicit in the import graph.
Enterprise usage note¶
When teams evaluate SPO for regulated or multi-region deployment, this contract is a critical documentation checkpoint because it documents which failures are expected to halt, which failures are reviewed, and which can be auto-recovered without risking audit continuity.