Last updated: 2026-09-21

U
Undergraduate level

Building Schemas and Scenarios: Design Notes

Working notes behind Schemas and Scenarios: why a schema’s clauses are ordinary compiled functions rather than inline text, and a checklist of what each stage of that page’s mechanism needed.

require/ensure Are Fatal, So a Schema Can’t Use Them

An earlier version of this page sketched a Schema:/Operation: block extension to PatLang’s Feature-file syntax, with require/ensure clauses written as inline text. Building it for real, rather than just imagining it, forced two corrections — both found by reading the actual PatLang source rather than trusting the sketch’s own assumptions:

  • require/ensure/assert are fatal on violation — confirmed directly in rust-runtime/src/ir/hosts.rs: a failed contract check aborts the running program. That rules out the sketch’s inline text clauses outright; a schema check has to report a diagnosis and keep running, not kill the process on the first violation. The real implementation represents a schema’s invariant, precondition, and postcondition as ordinary, separately-compiled PatLang functions returning bool, invoked by name through PatLang’s existing apply() primitive — the same mechanism self_hosting/lib/primitive_registry.patlang already uses for named contract predicates elsewhere in the codebase, not new machinery.
  • apply() has no argument-spread form. A checking function written once, generic over any schema’s own number of state variables, can’t pass one positional argument per variable — it has no way to know in advance how many there’ll be. State and inputs are therefore always bundled as a single list argument: invariant_fn(state_list), require_fn(state_list, input_list), ensure_fn(before_list, after_list, input_list).

Set and Map types didn’t exist either, as the sketch already suspected — self_hosting/lib/pset.patlang and pmap.patlang are new, small, association-list-based libraries built to fill exactly that gap, following the same list-plus-linear-scan idiom already used elsewhere in the codebase rather than inventing a new primitive.

What Each Stage Needed

PieceStatus
Feature:/Scenario:/Given/When/ThenPre-existing PatLang syntax, unchanged
Set<T>/Map<K,V>Built: pset.patlang, pmap.patlang
Schema/operation declaration + bindingBuilt: schema_bdd.patlang (schema_define, schema_operation, schema_bind_state, schema_bind_input)
The witness check itselfBuilt: schema_check/schema_check_values, four-stage diagnosis
Self-healing synthesis hookBuilt: an opt-in schema check in green_phase, zero effect unless registered
Inductive-synthesis bridgeBuilt: schema_synthesis_bridge.patlang
GOAP state exposure (plan_with_state)Built three times over: Rust interpreter, Rust codegen, and canonically as native self-hosted PatLang
Clean interpreter run (world_run, interp_run)Built: the synthesis engine tests candidate rules without a subprocess, so the induction example above runs in the browser
Declared-schema layer (explorer, feature analysis, outcomes, negative scenarios, sequences, refinement, generation)Built: self_hosting/lib/zs_*.patlang

Full source: the self_hosting/lib directory of the PatLang repository.