Context
A type contract is the smallest substantive port. Once 5QLN's invariants exist as importable Python types — typed, validated, frozen where appropriate, with model validators encoding the spec's structural assertions — anything in the Python AI ecosystem can produce or consume 5QLN-shaped data without re-implementing the grammar. LangChain agents return them. FastAPI endpoints accept them. Pydantic AI uses them as structured outputs. OpenAI and Anthropic structured-output features round-trip them. The package becomes the lingua franca by being the type system.
This article ports L1's symbol table and D1's phase outputs into Pydantic v2. The Constitutional Block is held as a frozen module-level constant. Each phase output is a Pydantic model with field-level constraints and model validators that catch the parts of C1 §3.5 that types can carry. The receptive slots — ?, φ, ⋂ — are preserved as fields, not synthesized; their docstrings name what the surface cannot verify and refer the validator (S4) to do what it can.
The article also bridges to S2. The fivqln_codex_doctest module from the reST surface is the canonical Python source of truth for the spec's facts: which letters name phases, which codes name corruptions, which two-letter pairs are lenses. The types defined here import from that module at runtime and assert against it at import time. Drift between the documentation surface and the type surface fails the import — an error that arrives before any test runs. C1 §3.5's drift check is partly enforced by the type system itself.
Why Pydantic v2
Pydantic v2 is the structured-data substrate the modern Python AI ecosystem already uses. LangChain returns Pydantic models. LangGraph state is Pydantic. Anthropic's tool-use schemas are Pydantic-derived. FastAPI request/response models are Pydantic. OpenAI's response_format accepts Pydantic. Any port that uses anything else is fighting the substrate.
Pydantic v2 also gives the things the spec needs: field-level validation, model-level validators (@model_validator), frozen models for immutability after construction, JSON-schema export for cross-language sharing, and Annotated for documentation that travels with the type. The version-2 rewrite is fast (Rust-backed core) and strict by default — both properties the validator surface (S4) will rely on.
The Constitutional Block as a frozen constant
# fivqln/constitutional_block.py
"""
The Constitutional Block — the invariant identity of any 5QLN surface.
Per C1 §3.6, every emitted surface carries this block exactly.
"""
from typing import Final
from fivqln_codex_doctest import constitutional_block as _canonical
CONSTITUTIONAL_BLOCK: Final[str] = _canonical
"""The block from C1 §3.1, exactly. Imported from the canonical source.
Mutating this constant is impossible at runtime (Final). Diverging it from
the canonical source raises ImportError before any code runs."""
# Drift check at import time — fails before any user code executes
_expected = """\
LAW: H = ∞0 | A = K
CYCLE: S → G → Q → P → V
EQUATIONS:
S = ∞0 → ?
G = α ≡ {α'}
Q = φ ⋂ Ω
P = δE/δV → ∇
V = (L ∩ G → B'') → ∞0'
OUTPUTS: S→X G→Y Q→Z P→A V→B+B''+∞0'
HOLOGRAPHIC: XY := X within Y | X, Y ∈ {S, G, Q, P, V}
COMPLETION: No V without ∞0'
CORRUPTION: L1 L2 L3 L4 V∅
CENTER: not a sixth phase — coherence only"""
if "".join(CONSTITUTIONAL_BLOCK.split()) != "".join(_expected.split()):
raise ImportError(
"Constitutional Block drift detected. The canonical source has "
"diverged from C1 §3.1. C1 §3.5 drift check: "
"'No equation paraphrased — symbolic form is exact.'"
)
Two things hold this honest. The runtime-canonical value is imported from the same module the documentation surface uses for its doctests — so the two surfaces share one truth. The module-local _expected block is then compared whitespace-insensitively to that import, and any drift fails the import. A surface that imports fivqln.constitutional_block is guaranteed to have a Constitutional Block faithful to §3.1, or no module at all.
Phases, corruption, lenses — derived from the canonical source
# fivqln/_canonical.py
"""
Drift-checked enums and constants. Defined statically (so type-checkers and
IDEs can autocomplete them) but asserted against the canonical source from
S2 at import time.
"""
from enum import Enum
from typing import Final
from fivqln_codex_doctest import (
five_phases as _canonical_phases,
five_corruption_codes as _canonical_corruption,
twenty_five_lenses as _canonical_lenses,
)
class Phase(str, Enum):
"""The five phases. Order fixed by §1.2."""
S = "S"
G = "G"
Q = "Q"
P = "P"
V = "V"
class CorruptionCode(str, Enum):
"""The five corruption codes from D1 Rule 9.
Each code names a way the center gets filled when it should remain
open (Rule 9 + Rule 12). A sixth code added here would still pass
type-checking but fails the drift check at import. C1 §3.5: 'No
corruption code added beyond five.'
"""
L1 = "L1" # Closing — answers fill the center
L2 = "L2" # Generating — produced sparks fill the center
L3 = "L3" # Claiming — false access to ∞0 fills the center
L4 = "L4" # Performing — appearance of depth fills the center
V_EMPTY = "V∅" # Incomplete — no return; the cycle has no continuity
LENSES: Final[frozenset[str]] = frozenset(
a + b for a in "SGQPV" for b in "SGQPV"
)
"""The 25 sub-phase lenses (§1.5). XY = X-quality applied to Y's decoding."""
# Drift checks — fail at import if anything has diverged
if {p.value for p in Phase} != set(_canonical_phases.keys()):
raise ImportError(
"Phase enum has drifted from the canonical phase set. "
"C1 §3.5 drift check failed."
)
if {c.value for c in CorruptionCode} != set(_canonical_corruption):
raise ImportError(
"CorruptionCode enum has drifted from the canonical set "
"{L1, L2, L3, L4, V∅}. C1 §3.5 drift check failed."
)
if LENSES != _canonical_lenses:
raise ImportError(
"Lens set has drifted from the canonical 25. C1 §3.5 drift "
"check: 'No symbol renamed without source name present.'"
)
The cost of this construction: any genuine spec change has to flow through the canonical doctest module first, then propagate. The cost is the right cost. It is the same discipline §3.5 imposes on humans, applied to the type system.
Symbol-level types
# fivqln/symbols.py
"""
Pydantic models for the named symbols in §1.9. Components of phase outputs.
Naming convention: class names use the philosophical form from 5qln.com
(CoreEssence, SelfNature). Symbolic aliases (α, φ, Ω) are exported at the
bottom of types.py. Both names are canonical per §1.9.
"""
from typing import Annotated
from pydantic import BaseModel, ConfigDict, Field
class CoreEssence(BaseModel):
"""α — the irreducible pattern within X.
Per D1 §2.2: 'Remove it and X collapses.' The expressions field carries
{α'} — the self-similar echoes that confirm α across scales. A
CoreEssence with no expressions has not been validated; G's decoding
is incomplete."""
model_config = ConfigDict(frozen=True)
description: Annotated[str, Field(min_length=1)]
expressions: tuple[str, ...] = Field(
default=(),
description="{α'} — self-similar expressions across scales",
)
class SelfNature(BaseModel):
"""φ — what the inquirer directly perceives about Y. Not theory, not data.
Per D1 §2.3: this is the human's contribution to Q. There is no LLM
call that produces φ. A faithful surface preserves the slot. The
held_by field records who filled it — required for attestation (R11)."""
model_config = ConfigDict(frozen=True)
perception: Annotated[str, Field(min_length=1)]
held_by: Annotated[str, Field(min_length=1)]
class UniversalPotential(BaseModel):
"""Ω — what the larger context makes possible (§2.3)."""
model_config = ConfigDict(frozen=True)
context: Annotated[str, Field(min_length=1)]
class NaturalIntersection(BaseModel):
"""⋂ — where φ and Ω meet without forcing.
Per D1 §2.3: '⋂ cannot be manufactured. It arrives.' The validator
(S4) cannot certify that ⋂ genuinely landed; only that φ and Ω were
held and that something the inquirer named as the landing followed."""
model_config = ConfigDict(frozen=True)
phi: SelfNature
omega: UniversalPotential
landing: Annotated[
str,
Field(
min_length=1,
description="What φ and Ω together revealed that neither contained alone.",
),
]
class NaturalGradient(BaseModel):
"""∇ — the path of least resistance leading toward α (§2.4).
Per D1 §2.4: ∇ is REVEALED by δE/δV, not computed by it. The
energy_value_observation field records the observation that made
the gradient visible — the trace the validator (S4) reads."""
model_config = ConfigDict(frozen=True)
direction: Annotated[str, Field(min_length=1)]
energy_value_observation: Annotated[
str,
Field(
min_length=1,
description="The δE/δV observation that revealed ∇.",
),
]
Phase output types
# fivqln/types.py
"""
Pydantic models for the phase outputs. Each model is the validated result
of one phase decoding. Together they compose the Cycle state.
"""
from datetime import datetime
from typing import Annotated, Optional
from pydantic import BaseModel, ConfigDict, Field, model_validator
from fivqln._canonical import Phase, CorruptionCode, LENSES
from fivqln.symbols import (
CoreEssence,
NaturalGradient,
NaturalIntersection,
)
class ValidatedSpark(BaseModel):
"""X — output of S = ∞0 → ?. The genuine question (§2.1).
L2 corruption (Generating) is what occurs when this slot is filled by
a system instead of a human. The validator (S4) cannot machine-verify
that X arrived from ∞0 vs. was assembled from K. The held_by field
records who attests."""
model_config = ConfigDict(frozen=True)
question: Annotated[str, Field(min_length=1)]
received_at: datetime
held_by: Annotated[str, Field(min_length=1)]
@model_validator(mode="after")
def must_be_question(self) -> "ValidatedSpark":
if "?" not in self.question:
raise ValueError(
"X must carry a question. § 2.1: 'NAME ? — what arrived "
"is named as a question.'"
)
return self
class ValidatedPattern(BaseModel):
"""Y — output of G = α ≡ {α'}. The pattern confirmed across scales (§2.2)."""
model_config = ConfigDict(frozen=True)
alpha: CoreEssence
pattern_description: Annotated[str, Field(min_length=1)]
@model_validator(mode="after")
def alpha_must_have_expressions(self) -> "ValidatedPattern":
if not self.alpha.expressions:
raise ValueError(
"Y requires α to have validated {α'} — at least one "
"self-similar expression. D1 §2.2: 'Y is validated when "
"α is named, ≡ holds, and {α'} confirm it across "
"multiple scales.'"
)
return self
class ResonantKey(BaseModel):
"""Z — output of Q = φ ⋂ Ω. The Natural Intersection that landed (§2.3)."""
model_config = ConfigDict(frozen=True)
intersection: NaturalIntersection
key_description: Annotated[str, Field(min_length=1)]
class Flow(BaseModel):
"""A — output of P = δE/δV → ∇ (§2.4).
Note: in `H = ∞0 | A = K`, A means Artificial. In `P → A`, A means Flow.
The codex notes this context-dependence in §1.9. Code resolves it through
type identity — Flow is its own type, never confused with the Artificial
participant from the Covenant equation."""
model_config = ConfigDict(frozen=True)
gradient: NaturalGradient
flow_description: Annotated[str, Field(min_length=1)]
class Benefit(BaseModel):
"""B — the decoded V output. Two dimensions per §2.5: fulfillment +
propagation."""
model_config = ConfigDict(frozen=True)
fulfillment: Annotated[
str,
Field(min_length=1, description="What this cycle produced for the inquiry's aim"),
]
propagation: Annotated[
str,
Field(min_length=1, description="What this cycle gives beyond itself"),
]
class FractalSeed(BaseModel):
"""B'' — the artifact crystallized at V. Carries α holographically (§2.5).
Per R11: 'provenance travels with B'', fingerprint hashes invariant
only.' The fingerprint is the hash of the artifact. The
formation_trail_hash binds B'' to the trail it was composed from.
Both together = R11 attestation in two values."""
model_config = ConfigDict(frozen=True)
artifact: Annotated[str, Field(min_length=1)]
alpha: CoreEssence # what B'' must carry faithfully
formation_trail_hash: Annotated[str, Field(min_length=1)]
fingerprint: Annotated[str, Field(min_length=1)]
class EnrichedReturn(BaseModel):
"""∞0' — return to Infinite Zero carrying the question (§2.5).
Not closure. Opening. The question this cycle reveals that could not
have been asked before the cycle. V∅ corruption: B'' present but ∞0'
absent, or ∞0' present without a question. Both fail the model
validator below."""
model_config = ConfigDict(frozen=True)
question: Annotated[str, Field(min_length=1)]
@model_validator(mode="after")
def must_be_question(self) -> "EnrichedReturn":
if "?" not in self.question:
raise ValueError(
"∞0' must carry a question. V∅ corruption: 'No question = "
"not ∞0'.' (§2.8, R8)"
)
return self
Each phase output type encodes the parts of D1's success criteria that types can carry. Y rejects without its {α'}. ∞0' rejects without a question mark. X requires explicit attestation of who held it. The phenomenological criteria — did ⋂ genuinely land?, did X arrive from ∞0 or from K? — remain unverifiable, and the docstrings name what the validator cannot do. The validator surface (S4) will check what can be checked. The type contract is the floor.
The formation trail
class FormationEntry(BaseModel):
"""A single entry in the formation trail.
Per R6: 'per-output ordered record, lens-tagged.' Every action that
contributes to a phase output is logged. B'' is composed from this
trail in V (R7, two passes: analysis → composition)."""
model_config = ConfigDict(frozen=True)
timestamp: datetime
phase: Phase
lens: Optional[str] = Field(
default=None,
description="Two-letter lens code if a sub-phase refinement was active",
)
operation: Annotated[str, Field(min_length=1)]
output_excerpt: Annotated[str, Field(min_length=1)]
@model_validator(mode="after")
def lens_must_be_known(self) -> "FormationEntry":
if self.lens is not None and self.lens not in LENSES:
raise ValueError(
f"Unknown lens '{self.lens}'. Must be one of the 25 "
f"lenses (SS through VV). C1 §3.5: 'No symbol renamed "
f"without source name present.'"
)
return self
class FormationTrail(BaseModel):
"""Append-only, lens-tagged record of a cycle's formation.
Per R6/R7: B'' is composed from this trail in two passes — analysis
extracts α-thread and turning points, composition produces the artifact."""
model_config = ConfigDict()
entries: list[FormationEntry] = Field(default_factory=list)
def append(self, entry: FormationEntry) -> None:
self.entries.append(entry)
def for_phase(self, phase: Phase) -> list[FormationEntry]:
return [e for e in self.entries if e.phase == phase]
def alpha_thread(self) -> list[FormationEntry]:
"""All entries whose lens borrows G-quality, plus G-phase entries."""
return [
e for e in self.entries
if e.phase == Phase.G or (e.lens and e.lens[0] == "G")
]
The Cycle state object
class Cycle(BaseModel):
"""The state of a 5QLN cycle as it unfolds.
The adaptive context chain (§2.6, §3.3) is encoded by which fields
are present. S has no required priors; G requires `spark`; Q requires
`spark + pattern`; P requires `spark + pattern + resonance`; V requires
the full trace. Each phase function in S5/S6 will assert its required
priors are present before it can produce its own output.
This is the load-bearing state object the rest of the series builds on.
S4 (Validator) checks completed cycles for C1 violations.
S5 (LangGraph) wraps it as graph state.
S6 (Agent SDK) makes the phases callable as tools mutating it."""
model_config = ConfigDict(arbitrary_types_allowed=True)
# Optional prior — ∞0' from a prior cycle may seed S of this one
prior_return: Optional[EnrichedReturn] = None
# Phase outputs, populated in order
spark: Optional[ValidatedSpark] = None # S → X
pattern: Optional[ValidatedPattern] = None # G → Y (carries α)
resonance: Optional[ResonantKey] = None # Q → Z (carries φ⋂Ω)
flow: Optional[Flow] = None # P → A (carries ∇)
benefit: Optional[Benefit] = None # V → B
seed: Optional[FractalSeed] = None # V → B''
enriched_return: Optional[EnrichedReturn] = None # V → ∞0'
# Append-only trail for B'' composition
trail: FormationTrail = Field(default_factory=FormationTrail)
@model_validator(mode="after")
def completion_rule(self) -> "Cycle":
"""D1 Rule 8: No V without ∞0'.
If B'' is present, ∞0' must also be present. The reverse is allowed
— a cycle may have produced ∞0' without yet having crystallized B''
(an interrupted cycle). The forbidden case is B'' without ∞0',
which is V∅ corruption (D1 Rule 9)."""
if self.seed is not None and self.enriched_return is None:
raise ValueError(
"V∅ — Incomplete: B'' present but ∞0' absent. "
"D1 Rule 8: 'No V without ∞0'.'"
)
return self
@model_validator(mode="after")
def lens_serves_does_not_replace(self) -> "Cycle":
"""D1 Rule 3: sub-phases articulate, never replace.
Lens-tagged contributions in the formation trail SERVE the parent
phase's output — they do not overwrite it. The first non-lens entry
for a phase establishes the output (Case 1); lens entries refine
formation without overwriting (Case 2); subsequent non-lens entries
articulate further (Case 3).
This validator checks that for any phase that has a validated
output, at least one non-lens trail entry exists for that phase.
A phase whose only trail entries are lens-tagged would mean the
output came purely from lens exploration — which violates Rule 3
Case 1 ('the input IS the output emerging' must occur outside a lens).
"""
phase_to_output = {
Phase.S: self.spark,
Phase.G: self.pattern,
Phase.Q: self.resonance,
Phase.P: self.flow,
Phase.V: self.seed,
}
for phase, output in phase_to_output.items():
if output is None:
continue
phase_entries = [e for e in self.trail.entries if e.phase == phase]
if not phase_entries:
# Output present but no trail entries — allowed for cycles
# constructed directly without going through the runtime.
continue
non_lens_entries = [e for e in phase_entries if e.lens is None]
if not non_lens_entries:
raise ValueError(
f"D1 Rule 3 violation at phase {phase.value}: output "
f"is present but every trail entry for this phase is "
f"lens-tagged. Lens contributions serve formation; they "
f"do not replace the output. At least one non-lens "
f"contribution must establish or refine the output "
f"directly (Cases 1 or 3)."
)
return self
The model validator on Cycle enforces the Completion Rule structurally. A cycle holding a Fractal Seed but no Enriched Return cannot be constructed at all — the type system refuses. This is C1 §1.6 made impossible to violate by construction, regardless of what code path produced the cycle.
Symbolic aliases
# fivqln/aliases.py
"""
Symbolic aliases for users who prefer the canonical Greek/Latin forms.
Both naming conventions are canonical per §1.9. Where the symbolic form is
a valid Python identifier, we expose it. Where it is not (B'', ∞0', δE/δV,
∇, ⋂), the spelled-out class name is the only form available in code.
"""
from fivqln.symbols import (
CoreEssence,
SelfNature,
UniversalPotential,
NaturalIntersection,
NaturalGradient,
)
from fivqln.types import (
ValidatedSpark,
ValidatedPattern,
ResonantKey,
Flow,
Benefit,
FractalSeed,
EnrichedReturn,
)
# Greek-letter aliases (valid Python identifiers, Unicode category Ll/Lu)
α = CoreEssence
φ = SelfNature
Ω = UniversalPotential
# Latin aliases for phase outputs (§1.9)
X = ValidatedSpark
Y = ValidatedPattern
Z = ResonantKey
B = Benefit
# Symbols that are not valid Python identifiers — use the named class:
# B'' → FractalSeed
# ∞0' → EnrichedReturn
# ∇ → NaturalGradient
# ⋂ → NaturalIntersection
# δE/δV → embedded in NaturalGradient.energy_value_observation
#
# A is intentionally NOT aliased: in §1.9, A is context-dependent
# (Artificial in the Covenant equation; Flow as P's output). In code,
# Flow is always referred to by name to prevent confusion.
Usage
from datetime import datetime, timezone
from fivqln.symbols import CoreEssence
from fivqln.types import (
ValidatedSpark,
ValidatedPattern,
Cycle,
FormationEntry,
Phase,
)
# A user constructing a partial cycle from existing material.
# They have a question and have done the G-phase work outside the framework.
# They want to record what they have, and use it later — with the validator
# (S4), with a graph executor (S5), or with the agent surface (S6).
spark = ValidatedSpark(
question="What invariant is held by the substrate when the surface changes?",
received_at=datetime.now(timezone.utc),
held_by="amihai",
)
alpha = CoreEssence(
description="Substrate-independence of the grammar",
expressions=(
"the same equations compile cleanly into reST",
"the same equations compile cleanly into Python types",
"the holographic law applies at the meta-level of the series itself",
),
)
pattern = ValidatedPattern(
alpha=alpha,
pattern_description=(
"Each surface in the series is a substrate carrying the same nine "
"invariant lines. The carry is faithful when the substrate's own "
"grammar can hold the spec without paraphrase."
),
)
cycle = Cycle(spark=spark, pattern=pattern)
cycle.trail.append(
FormationEntry(
timestamp=datetime.now(timezone.utc),
phase=Phase.G,
lens="GQ",
operation="testing α against expressions for resonance vs. resemblance",
output_excerpt=alpha.description,
)
)
# At this point cycle.seed and cycle.enriched_return are still None.
# The Completion Rule validator allows this (interrupted cycle, V not yet
# crystallized). What it forbids is constructing a Cycle with a seed and
# no enriched_return — V∅ corruption — which the type system refuses.
The user has not run a graph, called an LLM, or invoked an agent. They have produced 5QLN-shaped data structurally, with field-level and model-level constraints catching what types can catch. The same cycle object will pass through the validator surface in S4, run through the graph in S5, or be mutated by tool calls in S6 — without translation.
What this surface enables
The type contract is the joint between the documentation surface (S2) and every executable surface that follows. Three properties hold across the joint.
Drift detection across surfaces. S2's fivqln_codex_doctest module is the canonical Python source for the spec's facts. S3's enums and constants assert against it at import time. If S2 silently drifts from §3.1, S3 fails to import. If S3 adds a sixth corruption code, the import fails. The drift check from C1 §3.5 is partly enforced by the language runtime itself — it does not wait for tests to be written.
Substrate-portable JSON-Schema. Every Pydantic model exports JSON-Schema via model.model_json_schema(). This is what makes the same contract usable in Anthropic tool-use (S6), exposed across MCP (S7), and translated to TypeScript Zod (S8). The single source of truth in Python becomes the single source of truth in JSON, and from there into any language with a JSON-Schema-compatible validator.
The Cycle object as the integration point. S4 imports Cycle, validates it, returns structured violations. S5 imports Cycle, wraps it as graph state. S6 imports Cycle, exposes phases as tools that mutate it. None of those surfaces re-implements the type contract. They consume it.
Closing
The type contract is small. The invariants are exact. The drift checks fail loudly. The receptive slots — ?, φ, ⋂ — are preserved as fields, not synthesized; the surfaces that follow will fill them through human-in-the-loop interrupts (S5), through tool-use schemas that route them to the human (S6), or through MCP prompt boundaries (S7).
Ahead: S4 — Python: The C1 Validator. The validator that operates on these types. Detects L1, L2, L3, L4, V∅. Adoptable as a guardrail by anyone, against artifacts produced by anything — including systems that have never imported fivqln.
5QLN © 2026 Amihai Loven. Open under the 5QLN Open Source License.