Agent Control Surface Rendering Substrate — Doctrine¶
North star. Agent = Model + Harness + Intent. Harness = vendor harness (Claude Code, Codex, Copilot) + local extension. gzkit is the governance meta-harness layer: it does not replace the vendor model/tool loop; it composes, validates, and audits the control surfaces and deterministic tools that wrap that loop. The Agent Control Surface is the per-turn corpus the harness loads on the model's behalf —
AGENTS.md,CLAUDE.md,.claude/rules/**, skill bodies, the chore registry, persona files, handoffs. gzkit is control surfaces and tools designed so that operator intent makes the model's intrinsic weaknesses and negative tendencies inert.This doctrine governs the substrate gzkit composes those control surfaces from.
Binding claim¶
Corpus construction applies to the agents/claude and rules surfaces. Skills are authored as canonical files under
.gzkit/skills/and synchronized to their vendor and package copies; they are not corpus constructed. Vendor mirrors (.claude/,.codex/,.github/) are derived outputs, never authoring locations. The fidelity validators (ADR-0.0.33) check delivery against each surface's canonical source. The substrate is the harness's own integrity layer.
Operator ruling (2026-09-09, verbatim): "skills are not corpus constructed. only agents/claude and rules." This corrects the earlier blanket claim that every control surface is corpus authored. Skills are not awaiting corpus migration. Content-type schemas, registries, validation and mirror delivery do not imply corpus construction. The broader content-model forecasts below remain distinct from that authoring boundary; they do not authorize expanding the corpus population.
Invariant Tier — 0-Kelvin Floor¶
tier: invariant corpus entries are emitted verbatim at every setpoint — they are
never dropped, combined, or rewritten by the compression composer. This is the
0-Kelvin floor: the dial thins only compressible content; invariant content is
exact operator intent, analogous to the immutable upstream system prompt the
operator cannot edit.
Enforcement: gzkit.content.tier_policy.assert_invariant_verbatim(corpus, rendered_text)
raises ValueError on any violation. The canonical named invariants — PRIME DIRECTIVE,
DO IT RIGHT, NEVER PYTEST — MUST survive verbatim at the leanest setpoint (lite).
Mechanism — corpus → setpoint-compression → committed-rendition → deterministic-playback¶
For agents/claude and rules, the binding corpus composition mechanism is a four-stage pipeline. Skills instead use canonical-file authoring and surface sync.
- Corpus (
gz content remember) — every entry in these corpus surfaces is appended to an addressed, append-only corpus. The surface is never hand-edited at the rendered location; the corpus is the source of truth, analogous to how a harness stores user memories rather than rewriting the system prompt in place. - Setpoint compression (
gz content compose) — the operator sets a density setpoint ("the thermostat"); the composer thins onlycompressiblecorpus entries toward that setpoint by condensing language within sections, never dropping whole sections.tier: invariantentries are emitted verbatim (the 0-Kelvin floor above). - Committed rendition — the composed bytes are committed once to
.gzkit/renditions/<surface>/<consumer>.md, one durable artifact per (surface × consumer) pair. - Deterministic playback — sync replays the committed rendition verbatim to the rendered location: no LLM, no template substitution, no network. Identical committed rendition → byte-identical surface on every call.
Retired framing (do not reintroduce). The 2026-05-30 density-dial mechanism — a
per-Bullet density_min include/exclude switch plus three static lite/medium/heavy
templates — is retired as empirically inert (OBPI-0.0.37-27). Every parsed
Bullet.density_min was None and the playback template emits pillar.lines | join
verbatim, so render(lite) == render(medium) == render(heavy) byte-for-byte: an
include/exclude switch cannot condense language within a section, which is what the
operator's setpoint actually asks for. The retired surface — the per-Bullet density_min
field, its _enforce_judgment_floor validator, and the _project_for_temperature render
filter — has been removed from the runtime. Temperature survives only as a per-vendor
routing parameter (vendors.temperature_for); it no longer projects the model, and the
0-Kelvin floor now lives in the corpus tier: invariant designation, not per-Bullet
density.
Prompt assembly and cache stability¶
Prompt assembly order is part of the substrate contract. gzkit does not control vendor prompt-cache internals, but it does control the bytes it renders and the order in which stable and volatile material appear.
The rendering pipeline must preserve this order:
- Stable gzkit covenant and vendor-independent policy prefix.
- Stable project control surfaces such as
AGENTS.md, rules, personas, and skills. - Path-scoped or task-scoped dynamic context.
- Volatile runtime state, preferably as pointers to durable artifacts rather than pasted transcript text.
This ordering keeps the highest-value static prefix byte-stable for vendor prompt caching, makes drift diffable, and prevents volatile run facts from invalidating the invariant surface. Any renderer or hook that injects mutable session content ahead of the stable prefix creates a substrate defect.
Why this doctrine is foundation-tier¶
By the invariance test (foundation = "without it, we wouldn't be doing the project"):
- gzkit's purpose is to make stochastic LLM vibing structurally inert at the agent control surface.
- The control surface is presently a mix of hand-authored static files and partially-templated mirrors. Hand-authored surfaces accrete vibe-coded drift; the failure pattern recorded in ADR-0.14.0's and ADR-0.16.0's closeout audits is the empirical evidence.
- Corpus-built surfaces require a canonical corpus and committed rendition; skills require a canonical authored file and verified mirror fidelity. Editing a delivered mirror bypasses either source and makes drift invisible until validation runs.
- A substrate that authors against Pydantic models + Jinja2 templates + vendor-aware rendering is the structural backstop the fidelity doctrine (ADR-0.0.33) rests on. Without it, the fidelity doctrine's validators are weaker and the per-turn surface's drift is harder to catch.
This is the foundation/feature distinction applied: the substrate is the invariant; specific implementation features (TUI, LSP integration, retrieval-time disclosure, CMS-styled web admin) are feature-tier work that may or may not happen, but the substrate's invariant binds regardless.
Derivation from upstream pillars¶
This doctrine derives from existing canon, not adjacent to it:
- PRIME DIRECTIVE #5 (FLAG DEFECTS, NEVER EXCUSE THEM): drift in the rendered control surface is a defect; the substrate makes drift detectable at compile time rather than at audit time.
- DO IT RIGHT #1 (fix the class of failure, not the instance): hand-editing derived outputs bypasses their canonical source; source-aware delivery closes that failure class.
- MAKE LLM STOCHASTIC VIBES INERT operative claim 2 ("lighter ceremony is not a tradeoff axis"): the substrate is heavier than direct hand-authoring; that weight is the product, not overhead.
- OPERATOR ECONOMY OF EFFORT operative claim 1 ("agent drafts; operator reviews"): the substrate is the form constraint the agent drafts against; the operator reviews the rendered output and the canonical model interchangeably; both are valid review surfaces, but the rendered output is human-readable prose (the canonical model is agent-input only per OPERATOR ECONOMY anti-patterns).
- STDLIB-FIRST DOCTRINE: Pydantic is the explicit named departure from stdlib (validation semantics stdlib does not supply); Jinja2 is a named departure for template-engine semantics; both inherit the named-rationale-required rule.
The doctrine does not introduce a new philosophical claim. It names what the upstream pillars already imply and gives that implication mechanical teeth.
Scope — the headless-Django mapping¶
The "CLI/TUI Django for these files" framing (operator's verbatim) describes content modeling, validation and delivery. The mapping does not make every registered content type corpus constructed; the binding authoring boundary above governs that distinction.
| Django concept | gzkit-substrate equivalent | Binding commitment |
|---|---|---|
| Models | Pydantic content models per surface (AgentContract, Rule, Skill, Chore, Persona, Handoff, Scenario, Bullet, …) with frozen=True, extra="forbid" |
All per-turn surface artifacts MUST have a canonical Pydantic model |
| Migrations | Schema versioning + content-lifecycle state machine (ADR-0.16.0 OBPI-05 is the seed; this doctrine generalizes its scope) | Models evolve under attestation; rendered output stays consistent across schema generations |
| Templates | Jinja2 templates per (content type × vendor) producing deterministic byte-stable markdown | One canonical model produces N vendor mirrors deterministically |
| Views / URLs | CLI verbs (gz content edit / render / import / list / show / migrate) |
Operator-direct invocation surface; output is human-readable prose, not raw JSON |
| Admin | Agent-mediated dialogical tuning + light CLI/TUI affordances consistent with Claude Code's own surface idioms | The agent IS the authoring UI under operator direction; no heavy editor surface |
| ORM | Pydantic models over filesystem (today); pluggable backend later | Content is queryable as data, not just files; storage backend is not the doctrine |
| Forms | Pydantic models + structured Jinja2 scaffold templates the operator-and-agent fill dialogically | Form constraints are encoded in the model + the scaffold template; not a separate form layer |
| Validation | Pydantic + the fidelity validators from ADR-0.0.33 | Cannot save a content model that violates invariants; cannot render output that violates fidelity |
| Signals / hooks | Ledger event emission on content lifecycle transitions | Every authoring action is auditable in the ledger |
| Reverse rendering | Markdown-to-model parser per content type (ADR-0.0.19's gz justify validate is the precedent) |
Round-trip fidelity is a binding contract |
The eight-component delivery scope:
- Content model registry generalization. Extend ADR-0.16.0 OBPI-01 (rules-only registry) to all per-turn surface artifacts. Every artifact type has a registered Pydantic model with lifecycle, schema, and rendering rules.
- Rendering pipeline. Compose and play back corpus-built agents/claude and rules surfaces from their canonical records. Preserve canonical-file copy and fidelity checks for skills; corpus composition does not replace that delivery path.
- Reverse-parse migration tooling.
gz content import <file> --as <type>reads existing markdown into a canonical Pydantic model. Parsing a content type is not enrollment in corpus construction. - Authoring CLI.
gz content edit / render / list / show— operator-direct invocation; output is human-readable prose summary, never raw JSON. - Light TUI affordances. Claude-Code-style status lines, chore-runner-style result tables, plan-mode-style panels — native CLI affordances. No Textual form editor, no dedicated authoring app.
- Validation hooks. Every render and every save fires the ADR-0.0.33 fidelity validators. Output that fails validation does not land.
- Migration layer. Pydantic schema versioning so model refactors do not break rendered-output stability across releases.
- Vendor manifest expansion. ADR-0.16.0 OBPI-03 seeded the vendor manifest schema; this doctrine binds it as the canonical declaration of which content types render to which vendor mirrors.
The doctrine commits to all eight as the binding scope. OBPIs deliver them in priority order, not all at once.
The authoring surface — agent-mediated dialogical tuning¶
The canonical authoring mode is agent-mediated dialogical tuning, with operator-correctional authority preserved through the OPERATOR ECONOMY pillar. Corpus surfaces use their structured content workflow; skills use their canonical files and sync procedure. Structured scaffolds support that dialogue without changing which surfaces are corpus constructed. This is canonical because:
- ADR-0.0.19 (
gz justify) is the precedent: an 8-section Walkthrough scaffold the operator and agent fill dialogically; operator fills_[To be filled]_blocks via normal interaction; agent drafts substantively; operator corrects verbatim; ceremony attests. - The 2026-04-25 complexity-doctrine handoff records the binding decision: "Distillation is agent-driven, human-reviewed and attested/corrected (not 'joint authoring')."
- ADR-0.0.30 OBPI-04 records the precedent for editor integration: gzkit specifies the contract (LSP-style JSON-over-stdio); editor authors implement editor UIs. gzkit's scope is the protocol surface, not the editor ecosystem.
- The OPERATOR ECONOMY pillar explicitly forbids machine-readable surfaces as review surfaces. The rendered markdown is the review surface; the Pydantic model is agent-input only.
What this means concretely:
| Mode | Surface | Authoring action |
|---|---|---|
| Direct CLI | gz content edit <type> <id> |
Opens the canonical model as a Jinja2-rendered markdown scaffold (with _[To be filled]_ blocks where the model has empty fields); operator and agent iterate dialogically; gz content save validates and ledger-records the lifecycle transition |
| Direct render | gz content render <type> <id> --vendor=<v> |
Renders the canonical model to the vendor-specific mirror location; deterministic byte-stable output |
| Migration | gz content import <file> --as <type> |
Reverse-parses an existing rendered file into the canonical model; supports operator-supervised migration of existing hand-authored surfaces |
| Listing/discovery | gz content list / show |
Human-readable prose summary tables; Rich-rendered; never raw JSON in operator review surface |
| Editor integration (forecast) | LSP-style JSON-over-stdio protocol specified as a contract | Editor authors consume the contract; gzkit does not implement editor plugins |
What is explicitly NOT in scope:
- Textual TUI form editor
- Rich form-shaped UIs for content authoring
- Web admin interface
- Dedicated content-authoring desktop app
- Replacing the agent dialogue with a non-dialogical UI
These are anti-patterns. The agent IS the authoring UI; the templates ARE the form constraints; the Pydantic model IS the validation layer; the ceremony IS the attestation. Adding a UI on top of that stack is over-tooling for a problem the dialogical mode already solves.
Worked example — AgentContract content model¶
Concrete demonstration: the model behind AGENTS.md itself.
# src/gzkit/content/models/agent_contract.py
from pydantic import BaseModel, ConfigDict, Field
class Bullet(BaseModel):
"""A single binding bullet on the agent control surface."""
model_config = ConfigDict(frozen=True, extra="forbid")
id: str = Field(..., description="Stable bullet ID (e.g. PRIME-DIR-1)")
text: str = Field(..., description="The binding bullet text — verbatim")
classification: str = Field(..., description="Mechanical | Promotable | Judgment | Ambiguous")
origin_ghi: int | None = Field(None, description="GHI that authored or last revised the bullet")
class Pillar(BaseModel):
"""A top-level H2 section of the agent contract."""
model_config = ConfigDict(frozen=True, extra="forbid")
name: str = Field(..., description="Pillar name (e.g. PRIME DIRECTIVE)")
subtitle: str | None = Field(None, description="Parenthetical subtitle (e.g. OWNERSHIP)")
bullets: list[Bullet] = Field(default_factory=list)
derivation: str | None = Field(None, description="One-line derivation pointer to upstream pillar")
pointer_targets: list[str] = Field(
default_factory=list,
description="Lift-pointer destinations under docs/governance/",
)
class AgentContract(BaseModel):
"""Canonical content model for AGENTS.md."""
model_config = ConfigDict(frozen=True, extra="forbid")
project_name: str
project_purpose: str
tech_stack: str
north_star: str = Field(..., description="The Agent = Model + Harness + Intent framing")
pillars: list[Pillar]
behavior_rules_always: list[Bullet]
behavior_rules_never: list[Bullet]
gate_covenant_rows: list["GateRow"]
attestation_pattern: str
defect_fix_routing: "DefectFixRoutingTable"
architectural_boundaries: list[Bullet]
governance_doctrine_surfaces: list["DoctrinePointer"]
A change to AGENTS.md is then expressed as:
gz content edit agent_contract --section pillars— opens the rendered scaffold for the pillars section- Operator and agent iterate dialogically; agent drafts; operator corrects verbatim
gz content savevalidates against the Pydantic model and records the lifecycle transition in the ledgergz content render agent_contract --vendor=rootrenders toAGENTS.mddeterministically- ADR-0.0.33 fidelity validators confirm: every Mechanical/Promotable bullet from the canonical model lands in the rendered output; line-count regression is within tolerance; pointer anchors resolve
The corpus-authoring pattern also applies to Rule. Other content types can
have Pydantic schemas and templates without sharing this construction path.
In particular, author Skill content in .gzkit/skills/ and deliver it with
gz agent sync control-surfaces; never route a skill edit through corpus composition.
Round-trip fidelity contract¶
Every content type MUST satisfy:
For any canonical Pydantic model instance, render-then-parse must reconstruct an equivalent instance. ADR-0.0.19's gz justify validate is the reference implementation. The contract is binding: a content type that cannot round-trip is not substrate-compliant.
This enables lossless parsing and rendering where a content type uses that workflow. Any drift between parsed-then-rendered output and its canonical input is a fidelity finding. For copied skills, fidelity compares the authored canonical file with its delivered copies; parsing does not require replacing the authoring source with a corpus.
Levers and constraints (what gzkit controls vs. influences vs. cannot govern)¶
| Layer | Lever (gzkit owns) | Constraint (gzkit accepts) |
|---|---|---|
| Canonical content models | Pydantic schema authorship; validation; lifecycle | — |
| Rendering | Jinja2 template authorship; vendor-aware output paths; deterministic byte-stable rendering | — |
| Prompt assembly order | Stable-prefix, dynamic-context, and volatile-state ordering | Vendor prompt-cache implementation and cache-hit policy |
| Vendor mirrors | Sync to vendor-mirror locations; mirror-fidelity validation | Vendor harnesses honor the mirror format; if a vendor changes its loading semantics, gzkit's templates adapt to the new format |
| Loading per turn | — | Vendor harness decides what to load (path-scoped frontmatter for Claude Code, equivalent for Codex/Copilot) |
| Within-turn dynamics | — | Context window is append-only at runtime; no DOM-style manipulation; "forgetting" only exists between turns |
| Authoring | Agent-mediated dialogue; CLI verbs; LSP-style protocol contract | Editor implementations are the editor ecosystem's responsibility |
The doctrine works the levers gzkit owns to shape outcomes within constraints gzkit does not control. The substrate's resilience is exactly that it does not depend on runtime control of the vendor harness. Composition happens between turns; rendering happens before the agent loads; loading is the vendor harness's call. gzkit governs what gets composed and validates what was composed.
Substrate-invariance across implementation eras¶
The doctrine governs the output's fidelity to its declared invariants, not the composition method. This means the doctrine survives implementation transitions:
| Era | Composition method | What changes | What does not change |
|---|---|---|---|
| Era 1 — today | Hand-authored static files (mostly) + partial templating (src/gzkit/templates/agents.md → AGENTS.md via naive substitution) |
— | Validators check rendered output against canonical models (where models exist) |
| Era 2 — substrate landed | Corpus composition/playback for agents/claude and rules; canonical-file sync for skills | Composition becomes principled within its declared population | Validators check each delivered surface against its own canonical source |
| Era 3 — progressive disclosure | Scenario-aware retrieval selects applicable canonical content | Loading scenarios become first-class; surface size becomes per-scenario | Retrieval does not expand corpus construction to skills; source and delivery fidelity still bind |
In every era, the agent reads a static document. What changes is how the document is composed. Errors of what is printed become feedback for the CMS process (the composition pipeline) regardless of which era's pipeline is active. The doctrine's invariants survive the transitions; the validators evolve to test the active composition method.
Anti-patterns¶
- Editing derived output directly. Author agents/claude and rules through their corpus workflow. Author skills under
.gzkit/skills/, then sync. Vendor and package skill copies are derived outputs; the canonical skill file is not. - Building a heavy TUI editor. The agent is the authoring UI; light CLI affordances suffice; LSP-style protocol contracts are the path for editor integration.
- Asking the operator to read raw Pydantic-model JSON. Per OPERATOR ECONOMY: rendered markdown is the review surface; the canonical model is agent-input only.
- Bypassing round-trip fidelity. A content type that cannot parse-then-render-then-parse to identity is not substrate-compliant.
- Conflating content models with corpus construction. A schema or parser for
Skilldoes not make skills corpus-authored or pending corpus migration. Corpus migration applies only within the declared agents/claude and rules population. - Injecting volatile context before the stable prefix. It weakens prompt-cache behavior and hides drift in the bytes that should be invariant.
- Treating ADR-0.16.0's deliverable as the substrate. ADR-0.16.0 delivered a partial prior — Pydantic content-type registry, vendor-aware sync (file-copy), lifecycle state machine. It did NOT deliver Jinja2-templated rendering for the full surface. The substrate doctrine generalizes ADR-0.16.0's scope to deliver what its prose promised.
- Letting "lighter ceremony" become a tradeoff axis. The substrate adds composition steps the operator may experience as friction. That friction is the product. Per the anti-vibing mantra.
Agent Orientation Index¶
Do not re-derive the rendering architecture from source each session. This index is the routable map from each per-turn control surface to its canonical model, governing doctrine, and the command that loads or regenerates it. Load the index; do not reconstruct it by reading
src/gzkit/content/**. (Folds in the OBPI-0.0.37-16 orientation-index intent — a surface → authoritative-model + doctrine map rendered so the rendering architecture stops being re-derived from source each session.)
| Control surface | Canonical model | Governing doctrine | Load / regenerate command |
|---|---|---|---|
AGENTS.md, CLAUDE.md |
AgentContract (src/gzkit/content/models/agent_contract.py) |
this doctrine; ADR-0.0.37 | gz agent sync control-surfaces (playback of .gzkit/renditions/AGENTS.md/root.md) |
.claude/rules/**, .github/instructions/** |
Rule |
.gzkit/rules/skill-surface-sync.md |
gz agent sync control-surfaces |
.claude/skills/**/SKILL.md |
Authored .gzkit/skills/**/SKILL.md; Skill schema validates structure |
.gzkit/rules/skill-surface-sync.md |
gz skill list; gz agent sync control-surfaces |
| persona files | Persona |
ADR-0.0.11, ADR-0.0.12 | gz personas list |
| chore registry | Chore |
.gzkit/rules/skill-surface-sync.md § class-classifier |
gz agent sync control-surfaces |
| handoffs | Handoff |
gz-session-handoff skill |
gz context <ADR-ID> |
| BDD scenarios | Scenario |
.gzkit/rules/tests.md |
behave |
Authoring vs orientation — two distinct verbs. To change agents/claude or
rules, author the corpus entry (gz content remember) and recompose
(gz content compose). To change a skill, edit its canonical .gzkit/skills/
file, update its version/date, and run gz agent sync control-surfaces.
Never edit a delivered mirror. To orient, read this index for the governing
source and delivery command.
Related canon¶
- ADR-0.0.19 (Validated, foundation):
gz justify— the Pydantic+Jinja2 deterministic rendering precedent. The substrate doctrine generalizes this pattern. - ADR-0.16.0 (closed, drifted): "CMS Architecture Formalization" — partial prior. Delivered Pydantic registry + vendor-aware sync + lifecycle state machine. Did not deliver Jinja2-templated rendering for the full surface. The substrate doctrine is the authored capture of what ADR-0.16.0 promised.
- ADR-0.0.33 (paired foundation, this cluster): Agent Control Surface Fidelity Doctrine. The fidelity invariants the substrate's rendered output must preserve.
- ADR-0.0.30 (Validated, foundation): Complexity Authoring Guidance. Precedent for "specify the protocol; do not implement the editor."
- ADR-0.0.33 (airlineops, Pool): Agent Context Engineering. 12-Factor Agents grounding (Factor 3 — Own Your Context Window; Factor 9 — Compact Errors). 384-session empirical evidence.
- Pool cluster (gzkit):
ADR-pool.progressive-context-disclosure,ADR-pool.brief-loaded-context-manifest,ADR-pool.focused-context-loader,ADR-pool.execution-memory-graph,ADR-pool.cross-session-search,ADR-pool.compression-governance-hooks,ADR-pool.rag-anything-governance-retrieval. Future-feature work that consumes the substrate's invariants when promoted. - Pool stub (gzkit):
ADR-pool.gz-interview-render— render machine-readable artifacts as human-readable prose for operator review. Same JSON-review-anti-pattern this doctrine inherits from OPERATOR ECONOMY.
Origin¶
GHI #327 — instructions-files-diet pass surfaced a long-deferred doctrine gap during a pass-1 lift. The substrate doctrine is the authored capture of a design conversation whose canonical artifact had been lost across ADR-0.0.19's Future Considerations forecast, ADR-0.16.0's aspirational naming, and the pool cluster's distributed intentions. This page consolidates them into one foundation-tier artifact.